Package com.bbn.openmap.omGraphics.event

Source Code of com.bbn.openmap.omGraphics.event.StandardMapMouseInterpreter$MouseTimerListener

// **********************************************************************
//
// <copyright>
//
//  BBN Technologies
//  10 Moulton Street
//  Cambridge, MA 02138
//  (617) 873-8000
//
//  Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/omGraphics/event/StandardMapMouseInterpreter.java,v $
// $RCSfile: StandardMapMouseInterpreter.java,v $
// $Revision: 1.11.2.7 $
// $Date: 2007/08/17 17:57:14 $
// $Author: epgordon $
//
// **********************************************************************

package com.bbn.openmap.omGraphics.event;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.Iterator;
import java.util.List;

import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.ToolTipManager;

import com.bbn.openmap.event.MapMouseEvent;
import com.bbn.openmap.layer.OMGraphicHandlerLayer;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.util.Debug;

/**
* The StandardMapMouseInterpreter is a basic implementation of the
* MapMouseInterpreter, working with an OMGraphicHandlerLayer to
* handle MouseEvents on it. This class allows the
* OMGraphicHandlerLayer, which implements the GestureResponsePolicy,
* to not have to deal with MouseEvents and the OMGraphicList, but to
* just react to the meanings of the user's gestures.
* <p>
*
* The StandardMapMouseInterpreter uses highlighing to indicate that
* mouse movement is occurring over an OMGraphic, and gives the layer
* three ways to react to that movement. After finding out if the
* OMGraphic is highlightable, the SMMI will tell the layer to
* highlight the OMGraphic (which usually means to call select() on
* it), provide a tool tip string for the OMGraphic, and provide a
* string to use on the InformationDelegator info line. The layer can
* reply or ignore any and all of these notifications, depending on
* how it's supposed to act.
* <p>
*
* For left mouse clicks, the SMMI uses selection as a notification
* that the user is choosing an OMGraphic, and that the OMGraphic
* should be prepared to be moved, modified or deleted. For a single
* OMGraphic, this is usually handled by handing the OMGraphic off to
* the OMDrawingTool. However the GestureResponsPolicy handles the
* situation where the selection is of multiple OMGraphics, and the
* layer should prepare to handle those situations as movement or
* deletion notifications. This usually means to change the
* OMGraphic's display to indicate that the OMGraphics have been
* selected. Selection notifications can come in series, and the
* GestureResponsePolicy is expected to keep track of which OMGraphics
* it has been told are selected. Deselection notifications may come
* as well, or other action notifications such as cut or copy may
* arrive. For cut and copy notifications, the OMGraphics should be
* removed from any selection list. For pastings, the OMGraphics
* should be added to the selection list.
* <p>
*
* For right mouse clicks, the layer will be provided with a
* JPopupMenu to use to populate with options for actions over a
* OMGraphic or over the map.
* <p>
*
* The StandardMapMouseInterpreter uses a timer to pace how mouse
* movement actions are responded to. Highlight reactions only occur
* after the mouse has paused over the map for the timer interval, so
* the application doesn't try to respond to constantly changing mouse
* locations. You can disable this delay by setting the timer interval
* to zero.
*/
public class StandardMapMouseInterpreter implements MapMouseInterpreter {

    protected boolean DEBUG = false;
    protected OMGraphicHandlerLayer layer = null;
    protected String[] mouseModeServiceList = null;
    protected String lastToolTip = null;
    protected GestureResponsePolicy grp = null;
    protected GeometryOfInterest clickInterest = null;
    protected GeometryOfInterest movementInterest = null;
    protected boolean consumeEvents = false;

    /**
     * The OMGraphicLayer should be set at some point before use.
     */
    public StandardMapMouseInterpreter() {
        DEBUG = Debug.debugging("grp");
    }

    /**
     * The standard constructor.
     */
    public StandardMapMouseInterpreter(OMGraphicHandlerLayer l) {
        this();
        setLayer(l);
    }

    /**
     * Helper class used to keep track of OMGraphics of interest.
     * Interest means that a MouseEvent that occurred over an OMGraphic
     * that combined with another MouseEvent, may be interpreted as a
     * significant event.
     */
    public class GeometryOfInterest {
        OMGraphic omg;
        int button;
        boolean leftButton;

        /**
         * Create a Geometry of Interest with the OMGraphic and the
         * first mouse event.
         */
        public GeometryOfInterest(OMGraphic geom, MouseEvent me) {
            omg = geom;
            button = getButton(me);
            leftButton = isLeftMouseButton(me);
        }

        /**
         * A check to see if an OMGraphic is the same as the one of
         * interest.
         */
        public boolean appliesTo(OMGraphic geom) {
            return (geom == omg);
        }

        /**
         * A check to see if a mouse event that is occurring over an
         * OMGraphic is infact occurring over the one of interest, and
         * with the same mouse button.
         */
        public boolean appliesTo(OMGraphic geom, MouseEvent me) {
            return (geom == omg && sameButton(me));
        }

        /**
         * A check to see if the current mouse event concerns the same
         * mouse button as the original.
         */
        public boolean sameButton(MouseEvent me) {
            return button == getButton(me);
        }

        /**
         * Return the OMGraphic of interest.
         */
        public OMGraphic getGeometry() {
            return omg;
        }

        /**
         * Return the button that caused the interest.
         */
        public int getButton() {
            return button;
        }

        /**
         * Utility method to get around MouseEvent.getButton 1.4
         * requirement.
         */
        protected int getButton(MouseEvent me) {
            // jdk 1.4 version
            // return me.getButton();

            // jdk 1.3 version Don't know if the numbers are the same
            // as in me.getButton, shouldn't make a difference.
            if (me.isControlDown() /* PopupTrigger() */
                    || SwingUtilities.isRightMouseButton(me)) {
                return 1;
            } else if (SwingUtilities.isLeftMouseButton(me)) {
                return 0;
            } else {
                return 2;
            }
        }

        /**
         * Return if the current button is the left one.
         */
        public boolean isLeftButton() {
            return leftButton;
        }

        /**
         * Called when the popup trigger is known to have been
         * triggered and a click interest has been set by it.
         *
         * @param b
         */
        public void setLeftButton(boolean b) {
            leftButton = b;
        }
    }

    /**
     * A flag to tell the interpreter to be selfish about consuming
     * MouseEvents it receives. If set to true, it will consume events
     * so that other MapMouseListeners will not receive the events. If
     * false, lower layers will also receive events, which will let
     * them react too. Intended to let other layers provide
     * information about what the mouse is over when editing is
     * occurring.
     */
    public void setConsumeEvents(boolean consume) {
        consumeEvents = consume;
    }

    public boolean getConsumeEvents() {
        return consumeEvents;
    }

    public void setLayer(OMGraphicHandlerLayer l) {
        layer = l;
    }

    public OMGraphicHandlerLayer getLayer() {
        return layer;
    }

    /**
     * Set the ID's of the mouse modes that this interpreter should be
     * listening to. If set to null, this SMMI won't receive
     * MouseEvents.
     */
    public void setMouseModeServiceList(String[] list) {
        mouseModeServiceList = list;
    }

    /**
     * A method to set how a left mouse button is interpreted. We
     * count control-clicks as not a left mouse click.
     */
    public boolean isLeftMouseButton(MouseEvent me) {
        return SwingUtilities.isLeftMouseButton(me);
    }

    /**
     * Return a list of the modes that are interesting to the
     * MapMouseListener. You MUST override this with the modes you're
     * interested in, or set the mouse mode service list, or you won't
     * receive mouse events.
     */
    public String[] getMouseModeServiceList() {
        return mouseModeServiceList;
    }

    /**
     * Set the GeometryOfInterest as one that could possibly be in the
     * process of being clicked upon.
     */
    protected void setClickInterest(GeometryOfInterest goi) {
        clickInterest = goi;
    }

    /**
     * Get the GeometryOfInterest as one that could possibly be in the
     * process of being clicked upon.
     */
    protected GeometryOfInterest getClickInterest() {
        return clickInterest;
    }

    /**
     * Set the GeometryOfInterest for something that the mouse is
     * over. Prevents excessive modifications of the GUI if this
     * remains constant.
     */
    protected void setMovementInterest(GeometryOfInterest goi) {
        movementInterest = goi;
    }

    /**
     * Get the GeometryOfInterest for something that the mouse is
     * over. Prevents excessive modifications of the GUI if this
     * remains constant.
     */
    protected GeometryOfInterest getMovementInterest() {
        return movementInterest;
    }

    /**
     * Return the OMGraphic object that is under a mouse event
     * occurrance on the map, null if nothing applies.
     */
    public OMGraphic getGeometryUnder(MouseEvent me) {
        OMGraphic omg = null;
        OMGraphicList list = null;
        if (layer != null) {
            list = layer.getList();
            if (list != null) {
                omg = list.findClosest(me.getX(), me.getY(), 4);
            } else {
                if (DEBUG) {
                    Debug.output("SMMI: no layer to evaluate mouse event");
                }
            }
        } else {
            if (DEBUG) {
                Debug.output("SMMI: no layer to evaluate mouse event");
            }
        }

        return omg;
    }

    // Mouse Listener events
    ////////////////////////

    /**
     * Invoked when a mouse button has been pressed on a component.
     *
     * @param e MouseEvent
     * @return false if nothing was pressed over, or the consumeEvents
     *         setting if something was.
     */
    public boolean mousePressed(MouseEvent e) {
        if (DEBUG) {
            Debug.output("SMMI: mousePressed()");
        }
        setCurrentMouseEvent(e);
        boolean ret = false;

        GeometryOfInterest goi = getClickInterest();
        OMGraphic omg = getGeometryUnder(e);

        if (goi != null && !goi.appliesTo(omg, e)) {
            // If the click doesn't match the geometry or button
            // of the geometry of interest, need to tell the goi
            // that is was clicked off, and set goi to null.
            if (goi.isLeftButton()) {
                leftClickOff(goi.getGeometry(), e);
            } else {
                rightClickOff(goi.getGeometry(), e);
            }
            setClickInterest(null);
        }

        if (omg != null) {
            setClickInterest(new GeometryOfInterest(omg, e));
        }

        ret = testForAndHandlePopupTrigger(e);

        if (omg != null && !ret) {
            select(omg);
            ret = true;
        }

        return ret && consumeEvents;
    }

    /**
     * Invoked when a mouse button has been released on a component.
     *
     * @param e MouseEvent
     * @return false
     */
    public boolean mouseReleased(MouseEvent e) {
        setCurrentMouseEvent(e);
        return testForAndHandlePopupTrigger(e) && consumeEvents;
    }

    /**
     * Tests the MouseEvent to see if it's a popup trigger, and calls
     * rightClick appropriately if there is an OMGraphic involved.
     *
     * @param e MouseEvent
     * @return true if the MouseEvent is a popup trigger and has been consumed.
     */
    public boolean testForAndHandlePopupTrigger(MouseEvent e) {
        boolean ret = false;
        if (e.isPopupTrigger()) {
            GeometryOfInterest goi = getClickInterest();
            // If there is a click interest
            if (goi != null) {
                // Tell the policy it an OMGraphic was clicked.
                goi.setLeftButton(false);
                ret = rightClick(goi.getGeometry(), e);
            } else {
                ret = rightClick(e);
            }
        }
        return ret;
    }

    /**
     * Invoked when the mouse has been clicked. Notifies the left
     * click methods for the applicable OMGraphic or the map. Right
     * click methods are handled when the testForAndHandlePopupTrigger
     * method is called in mousePressed and mouseReleased.
     *
     * @param e MouseEvent
     * @return the consumeEvents setting.
     */
    public boolean mouseClicked(MouseEvent e) {
        setCurrentMouseEvent(e);

        if (isLeftMouseButton(e)) {
            GeometryOfInterest goi = getClickInterest();
            // If there is a click interest
            if (goi != null && goi.isLeftButton()) {
                // Tell the policy it an OMGraphic was clicked.
                leftClick(goi.getGeometry(), e);
            } else {
                leftClick(e);
            }

            return consumeEvents;
        }

        return false;
    }

    /**
     * Invoked when the mouse enters a component.
     *
     * @param e MouseEvent
     */
    public void mouseEntered(MouseEvent e) {
        setCurrentMouseEvent(e);
    }

    /**
     * Invoked when the mouse exits a component.
     *
     * @param e MouseEvent
     */
    public void mouseExited(MouseEvent e) {
        setCurrentMouseEvent(e);
    }

    // Mouse Motion Listener events
    ///////////////////////////////

    /**
     * Invoked when a mouse button has been pressed and is moving.
     * Resets the click geometry of interest to null.
     *
     * @param e MouseEvent
     * @return the result from mouseMoved (also called from this
     *         method) combined with the consumeEvents setting.
     */
    public boolean mouseDragged(MouseEvent e) {
        setCurrentMouseEvent(e);
        GeometryOfInterest goi = getClickInterest();
        if (goi != null) {
            setClickInterest(null);
        }

        return mouseMoved(e) && consumeEvents;
    }

    /**
     * Invoked when the mouse has been moved. Sets the movement
     * geometry of interest and updates the movement timer.
     *
     * @param e MouseEvent
     * @return the result of updateMouseMoved() if the timer isn't
     *         being used, or false.
     */
    public boolean mouseMoved(MouseEvent e) {
        setCurrentMouseEvent(e);

        if ((noTimerOverOMGraphic && getMovementInterest() != null)
                || mouseTimerInterval <= 0) {
            return updateMouseMoved(e);
        } else {
            if (mouseTimer == null) {
                mouseTimer = new Timer(mouseTimerInterval, mouseTimerListener);
                mouseTimer.setRepeats(false);
            }

            mouseTimerListener.setEvent(e);
            mouseTimer.restart();
            return false;
        }
    }

    protected boolean noTimerOverOMGraphic = true;

    /**
     * Set whether to ignore the timer when movement is occurring over
     * an OMGraphic. Sometimes unhighlight can be inappropriately
     * delayed when timer is enabled.
     */
    public void setNoTimerOverOMGraphic(boolean val) {
        noTimerOverOMGraphic = val;
    }

    /**
     * Get whether the timer should be ignored when movement is
     * occurring over an OMGraphic.
     */
    public boolean getNoTimerOverOMGraphic() {
        return noTimerOverOMGraphic;
    }

    /**
     * The wait interval before a mouse over event gets triggered.
     */
    protected int mouseTimerInterval = 150;

    /**
     * Set the time interval that the mouse timer waits before calling
     * upateMouseMoved. A negative number or zero will disable the
     * timer.
     */
    public void setMouseTimerInterval(int interval) {
        mouseTimerInterval = interval;
    }

    public int getMouseTimerInterval() {
        return mouseTimerInterval;
    }

    /**
     * The timer used to track the wait interval.
     */
    protected Timer mouseTimer = null;

    /**
     * The timer listener that calls updateMouseMoved.
     */
    protected MouseTimerListener mouseTimerListener = new MouseTimerListener();

    /**
     * The definition of the listener that calls updateMouseMoved when
     * the timer goes off.
     */
    protected class MouseTimerListener implements ActionListener {

        private MouseEvent event;

        public synchronized void setEvent(MouseEvent e) {
            event = e;
        }

        public synchronized void actionPerformed(ActionEvent ae) {
            if (event != null) {
                updateMouseMoved(event);
            }
        }
    }

    /**
     * The real mouseMoved call, called when mouseMoved is called and,
     * if there is a mouse timer interval set, that interval time has
     * passed.
     *
     * @return the consumeEvents setting of the mouse event concerns
     *         an OMGraphic, false if it didn't.
     */
    protected boolean updateMouseMoved(MouseEvent e) {
        boolean ret = false;
        OMGraphic omg = getGeometryUnder(e);
        GeometryOfInterest goi = getMovementInterest();

        if (omg != null && grp != null) {

            // This gets called if the goi is new or if the goi
            // refers to a different OMGraphic as previously noted.
            if (goi == null || !goi.appliesTo(omg)) {

                if (goi != null) {
                    mouseNotOver(goi.getGeometry());
                }

                goi = new GeometryOfInterest(omg, e);
                setMovementInterest(goi);
                setNoTimerOverOMGraphic(!omg.shouldRenderFill());
                ret = mouseOver(omg, e);
            }

        } else {
            if (goi != null) {
                mouseNotOver(goi.getGeometry());
                setMovementInterest(null);
            }
            ret = mouseOver(e);
        }

        return ret && consumeEvents;
    }

    /**
     * Handle notification that another layer consumed a mouse moved
     * event. Sets movement interest to null.
     */
    public void mouseMoved() {
        GeometryOfInterest goi = getMovementInterest();
        if (goi != null) {
            mouseNotOver(goi.getGeometry());
            setMovementInterest(null);
        }
    }

    /**
     * Handle a left-click on the map. Does nothing by default.
     *
     * @return false
     */
    public boolean leftClick(MouseEvent me) {
        if (DEBUG) {
            Debug.output("leftClick(MAP) at " + me.getX() + ", " + me.getY());
        }

        if (grp != null && grp.receivesMapEvents()
                && me instanceof MapMouseEvent) {
            return grp.leftClick((MapMouseEvent) me);
        }

        return false;
    }

    /**
     * Handle a left-click on an OMGraphic. Does nothing by default.
     *
     * @return true
     */
    public boolean leftClick(OMGraphic omg, MouseEvent me) {
        if (DEBUG) {
            Debug.output("leftClick(" + omg.getClass().getName() + ") at "
                    + me.getX() + ", " + me.getY());
        }

        return false;
    }

    /**
     * Notification that the user clicked on something else other than
     * the provided OMGraphic that was previously left-clicked on.
     * Calls deselect(omg).
     *
     * @return false
     */
    public boolean leftClickOff(OMGraphic omg, MouseEvent me) {
        if (DEBUG) {
            Debug.output("leftClickOff(" + omg.getClass().getName() + ") at "
                    + me.getX() + ", " + me.getY());
        }

        deselect(omg);

        return false;
    }

    /**
     * Notification that the map was right-clicked on.
     *
     * @return false
     */
    public boolean rightClick(MouseEvent me) {
        if (DEBUG) {
            Debug.output("rightClick(MAP) at " + me.getX() + ", " + me.getY());
        }

        if (me instanceof MapMouseEvent) {
            return displayPopup(grp.getItemsForMapMenu((MapMouseEvent) me), me);
        }

        return false;
    }

    /**
     * Notification that an OMGraphic was right-clicked on.
     *
     * @return true
     */
    public boolean rightClick(OMGraphic omg, MouseEvent me) {
        if (DEBUG) {
            Debug.output("rightClick(" + omg.getClass().getName() + ") at "
                    + me.getX() + ", " + me.getY());
        }

        return displayPopup(grp.getItemsForOMGraphicMenu(omg), me);
    }

    /**
     * Create a popup menu from GRP requests, over the mouse event
     * location.
     *
     * @return true if popup was presented, false if not.
     */
    protected boolean displayPopup(List contents, MouseEvent me) {
        if (DEBUG) {
            Debug.output("displayPopup(" + contents + ") " + me);
        }
        if (contents != null && contents.size() > 0) {
            JPopupMenu jpm = new JPopupMenu();
            Iterator it = contents.iterator();
            while (it.hasNext()) {
                Object obj = it.next();
                if (obj instanceof java.awt.Component) {
                    jpm.add((java.awt.Component) obj);
                }
            }
            jpm.show((java.awt.Component) me.getSource(), me.getX(), me.getY());
            return true;
        }
        return false;
    }

    /**
     * Notification that the user clicked on something else other than
     * the provided OMGraphic that was previously right-clicked on.
     *
     * @return false
     */
    public boolean rightClickOff(OMGraphic omg, MouseEvent me) {
        if (DEBUG) {
            Debug.output("rightClickOff(" + omg.getClass().getName() + ") at "
                    + me.getX() + ", " + me.getY());
        }

        return false;
    }

    /**
     * Notification that the mouse is not over an OMGraphic, but over
     * the map at some location.
     *
     * @return false
     */
    public boolean mouseOver(MouseEvent me) {
        if (DEBUG) {
            Debug.output("mouseOver(MAP) at " + me.getX() + ", " + me.getY());
        }
        if (grp != null && grp.receivesMapEvents()
                && me instanceof MapMouseEvent) {
            return grp.mouseOver((MapMouseEvent) me);
        }

        return false;
    }

    /**
     * Notification that the mouse is over an OMGraphic. Makes all the
     * highlight calls.
     *
     * @return true
     */
    public boolean mouseOver(OMGraphic omg, MouseEvent me) {
        if (DEBUG) {
            Debug.output("mouseOver(" + omg.getClass().getName() + ") at "
                    + me.getX() + ", " + me.getY());
        }

        if (grp != null) {
            handleToolTip(grp.getToolTipTextFor(omg), me);
            handleInfoLine(grp.getInfoText(omg));
            if (grp.isHighlightable(omg)) {
                grp.highlight(omg);
            }
        }
        return true;
    }

    /**
     * Given a tool tip String, use the layer to get it displayed.
     */
    protected void handleToolTip(String tip, MouseEvent me) {
        if (lastToolTip == tip) {
            return;
        }
        lastToolTip = tip;
        if (layer != null) {
            if (lastToolTip != null) {
                layer.fireRequestToolTip(lastToolTip);
                // forward the event to the tool tip manager so it will popup
                // the tool tip right away, otherwise an additional event is
                // required to trigger it
                ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
                toolTipManager.mouseMoved(me);
            } else {
                layer.fireHideToolTip();
            }
        }
    }

    /**
     * Given an information line, use the layer to get it displayed on
     * the InformationDelegator.
     */
    protected void handleInfoLine(String line) {
        if (layer != null) {
            layer.fireRequestInfoLine((line == null) ? "" : line);
        }
    }

    /**
     * Notification that the mouse has moved off of an OMGraphic.
     */
    public boolean mouseNotOver(OMGraphic omg) {
        if (DEBUG) {
            Debug.output("mouseNotOver(" + omg.getClass().getName() + ")");
        }

        if (grp != null) {
            grp.unhighlight(omg);
        }
        handleToolTip(null, null);
        handleInfoLine(null);
        return false;
    }

    /**
     * Notify the GRP that the OMGraphic has been selected. Wraps the
     * OMGraphic in an OMGraphicList.
     */
    public void select(OMGraphic omg) {
        if (grp != null && grp.isSelectable(omg)) {
            OMGraphicList omgl = new OMGraphicList();
            omgl.add(omg);
            grp.select(omgl);
        }
    }

    /**
     * Notify the GRP that the OMGraphic has been deselected. Wraps
     * the OMGraphic in an OMGraphicList.
     */
    public void deselect(OMGraphic omg) {
        if (grp != null && grp.isSelectable(omg)) {
            OMGraphicList omgl = new OMGraphicList();
            omgl.add(omg);
            grp.deselect(omgl);
        }
    }

    /**
     * The last MouseEvent received, for later reference.
     */
    protected MouseEvent currentMouseEvent;

    /**
     * Set the last MouseEvent received.
     */
    protected void setCurrentMouseEvent(MouseEvent me) {
        currentMouseEvent = me;
    }

    /**
     * Get the last MouseEvent received.
     */
    public MouseEvent getCurrentMouseEvent() {
        return currentMouseEvent;
    }

    /**
     * Set the GestureResponsePolicy to notify of the mouse actions
     * over the layer's OMGraphicList.
     */
    public void setGRP(GestureResponsePolicy grp) {
        this.grp = grp;
    }

    /**
     * Get the GestureResponsePolicy that is being notified of the
     * mouse actions over the layer's OMGraphicList.
     */
    public GestureResponsePolicy getGRP() {
        return grp;
    }
}
TOP

Related Classes of com.bbn.openmap.omGraphics.event.StandardMapMouseInterpreter$MouseTimerListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.