// **********************************************************************
//
// <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/event/MapMouseSupport.java,v $
// $RCSfile: MapMouseSupport.java,v $
// $Revision: 1.9 $
// $Date: 2008/10/16 19:33:08 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.event;
import java.awt.event.MouseEvent;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This is a utility class that can be used by beans that need support for
* handling MapMouseListeners and firing MapMouseEvents. You can use an instance
* of this class as a member field of your bean and delegate work to it.
* <p>
* You can set the behavior of how MouseEvents are propagated by setting whether
* to "consume" events. If the MouseMode is consuming events, then the event is
* not propagated further than the first listener to successfully process it.
* Otherwise the event is propagated to all listeners. The default is to consume
* events.
*/
public class MapMouseSupport
extends ListenerSupport<MapMouseListener> {
private static final long serialVersionUID = 1L;
protected static Logger logger = Logger.getLogger("com.bbn.openmap.event.MapMouseSupport");
/**
* The flag that dictates whether the events should be passed to all the
* listeners or just limited to the first listener that can deal with it. The
* default value is set to true, which means the event will be consumed by
* the first layer that can handle it.
*/
protected boolean consumeEvents = true;
/**
* The priority MapMouseListener will be guaranteed to receive events that go
* hand in hand (pressed - released, etc.).
*/
protected MapMouseListener priorityListener = null;
/**
* Used to determine whether a release should reset the priorityListener on a
* mouse release.
*/
protected boolean clickHappened = false;
/**
* A MapMouseMode that may be using the parent of this support object as a
* proxy.
*/
protected transient MapMouseMode proxy = null;
protected transient int proxyDistributionMask = 0;
public final static int PROXY_DISTRIB_MOUSE_PRESSED = 1 << 0;
public final static int PROXY_ACK_CONSUMED_MOUSE_PRESSED = 1 << 1;
public final static int PROXY_DISTRIB_MOUSE_RELEASED = 1 << 2;
public final static int PROXY_ACK_CONSUMED_MOUSE_RELEASED = 1 << 3;
public final static int PROXY_DISTRIB_MOUSE_CLICKED = 1 << 4;
public final static int PROXY_ACK_CONSUMED_MOUSE_CLICKED = 1 << 5;
public final static int PROXY_DISTRIB_MOUSE_MOVED = 1 << 6;
public final static int PROXY_ACK_CONSUMED_MOUSE_MOVED = 1 << 7;
public final static int PROXY_DISTRIB_MOUSE_DRAGGED = 1 << 8;
public final static int PROXY_ACK_CONSUMED_MOUSE_DRAGGED = 1 << 9;
public final static int PROXY_DISTRIB_MOUSE_ENTERED = 1 << 10;
public final static int PROXY_DISTRIB_MOUSE_EXITED = 1 << 11;
protected boolean DEBUG = false;
protected boolean DEBUG_DETAIL = false;
/**
* Construct a default MapMouseSupport. The default value of consumeEvents is
* set to true.
*/
public MapMouseSupport() {
this(null, true);
}
/**
* Construct a default MapMouseSupport. The default value of consumeEvents is
* set to true.
*
* @param mode the parent MapMouseMode to use with creating the
* MapMouseEvent.
*/
public MapMouseSupport(MapMouseMode mode) {
this(mode, true);
}
/**
* Construct a MapMouseSupport.
*
* @param shouldConsumeEvents if true, events are propagated to the first
* MapMouseListener that successfully processes the event, if false,
* events are propagated to all MapMouseListeners
*/
public MapMouseSupport(boolean shouldConsumeEvents) {
this(null, shouldConsumeEvents);
}
/**
* Construct a MapMouseSupport.
*
* @param mode the parent MapMouseMode to use with creating the
* MapMouseEvent.
* @param shouldConsumeEvents if true, events are propagated to the first
* MapMouseListener that successfully processes the event, if false,
* events are propagated to all MapMouseListeners
*/
public MapMouseSupport(MapMouseMode mode, boolean shouldConsumeEvents) {
super(mode);
consumeEvents = shouldConsumeEvents;
DEBUG = logger.isLoggable(Level.FINE);
DEBUG_DETAIL = logger.isLoggable(Level.FINER);
}
/**
* Set the parent MapMouseMode to use in constructing MapMouseEvents.
*/
public void setParentMode(MapMouseMode mode) {
setSource(mode);
}
public MapMouseMode getParentMode() {
return (MapMouseMode) getSource();
}
/**
* Sets how the mouse support passes out events. If the value passed in is
* true, the mouse support will only pass the event to the first listener
* that can respond to the event. If false, the mouse support will pass the
* event on to all its listeners.
*
* @param shouldConsumeEvents true for limited distribution.
*/
public void setConsumeEvents(boolean shouldConsumeEvents) {
consumeEvents = shouldConsumeEvents;
}
/**
* Returns how the mouse support is set up to distribute events.
*
* @return true if only one listener gets to act on an event.
*/
public boolean isConsumeEvents() {
return consumeEvents;
}
/**
* Handle a mousePressed MouseListener event.
*
* @param evt MouseEvent to be handled
*/
public boolean fireMapMousePressed(MouseEvent evt) {
if (DEBUG) {
logger.fine("MapMouseSupport.fireMapMousePressed()");
}
boolean consumed = false;
if (DEBUG) {
logger.fine(" -- has proxy (" + (proxy != null) + ") -- shift used (" + evt.isShiftDown() + ")");
}
if (proxy == null || evt.isShiftDown() || (proxyDistributionMask & PROXY_DISTRIB_MOUSE_PRESSED) > 0) {
evt = new MapMouseEvent(getParentMode(), evt);
if (DEBUG && proxy != null && evt.isShiftDown()) {
logger.fine("MMS.fireMapMousePressed(): proxy enabled, but side stepping to send event to primary listeners");
}
Iterator<MapMouseListener> it = iterator();
while (it.hasNext() && !consumed) {
MapMouseListener target = it.next();
consumed = target.mousePressed(evt) && consumeEvents;
if (consumed) {
priorityListener = target;
}
}
}
boolean ignoreConsumed = !consumed || (consumed && ((proxyDistributionMask & PROXY_ACK_CONSUMED_MOUSE_PRESSED) == 0));
if (proxy != null && ignoreConsumed && !evt.isShiftDown()) {
proxy.mousePressed(evt);
consumed = true;
} else {
if (DEBUG && evt.isShiftDown()) {
logger.fine("MMS.fireMapMousePressed(): side-stepped proxy");
}
}
return consumed;
}
/**
* Handle a mouseReleased MouseListener event. Checks to see if there is a
* priorityListener, and will direct the event to that listener. The
* priorityListener variable will be reset to null. If there is not a
* priorityListener, the event is passed through the listeners, subject to
* the consumeEvents mode.
*
* @param evt MouseEvent to be handled.
*/
public boolean fireMapMouseReleased(MouseEvent evt) {
if (DEBUG) {
logger.fine("MapMouseSupport: fireMapMouseReleased");
}
boolean consumed = false;
evt = new MapMouseEvent(getParentMode(), evt);
if (priorityListener != null) {
priorityListener.mouseReleased(evt);
if (!clickHappened) {
priorityListener = null;
}
consumed = true;
}
if (proxy == null || evt.isShiftDown() || (proxyDistributionMask & PROXY_DISTRIB_MOUSE_RELEASED) > 0) {
Iterator<MapMouseListener> it = iterator();
while (it.hasNext() && !consumed) {
consumed = it.next().mouseReleased(evt) && consumeEvents;
}
}
boolean ignoreConsumed = !consumed || (consumed && ((proxyDistributionMask & PROXY_ACK_CONSUMED_MOUSE_RELEASED) == 0));
if (proxy != null && ignoreConsumed && !evt.isShiftDown()) {
proxy.mouseReleased(evt);
consumed = true;
}
return consumed;
}
/**
* Handle a mouseClicked MouseListener event. If the priorityListener is set,
* it automatically gets the clicked event. If it is not set, the other
* listeners get a shot at the event according to the consumeEvent mode.
*
* @param evt MouseEvent to be handled.
*/
public boolean fireMapMouseClicked(MouseEvent evt) {
if (DEBUG) {
logger.fine("MapMouseSupport: fireMapMouseClicked");
}
clickHappened = true;
boolean consumed = false;
evt = new MapMouseEvent(getParentMode(), evt);
if (priorityListener != null && evt.getClickCount() > 1) {
priorityListener.mouseClicked(evt);
consumed = true;
}
priorityListener = null;
if (proxy == null || evt.isShiftDown() || (proxyDistributionMask & PROXY_DISTRIB_MOUSE_CLICKED) > 0) {
Iterator<MapMouseListener> it = iterator();
while (it.hasNext() && !consumed) {
MapMouseListener target = it.next();
consumed = target.mouseClicked(evt) && consumeEvents;
if (consumed) {
priorityListener = target;
}
}
}
boolean ignoreConsumed = !consumed || (consumed && ((proxyDistributionMask & PROXY_ACK_CONSUMED_MOUSE_CLICKED) == 0));
if (proxy != null && ignoreConsumed && !evt.isShiftDown()) {
proxy.mouseClicked(evt);
consumed = true;
}
return consumed;
}
/**
* Handle a mouseEntered MouseListener event.
*
* @param evt MouseEvent to be handled
* @return true if there was a target to send the event to.
*/
public boolean fireMapMouseEntered(MouseEvent evt) {
if (DEBUG) {
logger.fine("MapMouseSupport: fireMapMouseEntered");
}
boolean consumed = false;
if (proxy == null || evt.isShiftDown() || (proxyDistributionMask & PROXY_DISTRIB_MOUSE_ENTERED) > 0) {
evt = new MapMouseEvent(getParentMode(), evt);
for (MapMouseListener listener : this) {
listener.mouseEntered(evt);
consumed = true;
}
}
if (proxy != null && !evt.isShiftDown()) {
proxy.mouseEntered(evt);
consumed = true;
}
return consumed;
}
/**
* Handle a mouseExited MouseListener event.
*
* @param evt MouseEvent to be handled
* @return true if there was a target to send the event to.
*/
public boolean fireMapMouseExited(MouseEvent evt) {
if (DEBUG) {
logger.fine("MapMouseSupport: fireMapMouseExited");
}
boolean consumed = false;
if (proxy == null || evt.isShiftDown() || (proxyDistributionMask & PROXY_DISTRIB_MOUSE_EXITED) > 0) {
evt = new MapMouseEvent(getParentMode(), evt);
for (MapMouseListener listener : this) {
listener.mouseExited(evt);
consumed = true;
}
}
if (proxy != null && !evt.isShiftDown()) {
proxy.mouseExited(evt);
consumed = true;
}
return consumed;
}
/**
* Handle a mouseDragged MouseListener event.
*
* @param evt MouseEvent to be handled
* @return false.
*/
public boolean fireMapMouseDragged(MouseEvent evt) {
if (DEBUG_DETAIL) {
logger.finer("MapMouseSupport: fireMapMouseDragged");
}
clickHappened = false;
boolean consumed = false;
if (proxy == null || evt.isShiftDown() || (proxyDistributionMask & PROXY_DISTRIB_MOUSE_DRAGGED) > 0) {
evt = new MapMouseEvent(getParentMode(), evt);
Iterator<MapMouseListener> it = iterator();
while (it.hasNext() && !consumed) {
consumed = it.next().mouseDragged(evt) && consumeEvents;
}
}
boolean ignoreConsumed = !consumed || (consumed && ((proxyDistributionMask & PROXY_ACK_CONSUMED_MOUSE_DRAGGED) == 0));
if (proxy != null && ignoreConsumed && !evt.isShiftDown()) {
proxy.mouseDragged(evt);
consumed = true;
}
return consumed;
}
/**
* Handle a mouseMoved MouseListener event. If the moved event is consumed,
* the rest of the listeners that didn't have a chance to respond get called
* in the mouse moved method without arguments.
*
* @param evt MouseEvent to be handled
* @return true if the event was consumed.
*/
public boolean fireMapMouseMoved(MouseEvent evt) {
if (DEBUG_DETAIL) {
logger.fine("MapMouseSupport: fireMapMouseMoved");
}
boolean consumed = false;
if (proxy == null || evt.isShiftDown() || (proxyDistributionMask & PROXY_DISTRIB_MOUSE_MOVED) > 0) {
evt = new MapMouseEvent(getParentMode(), evt);
Iterator<MapMouseListener> it = iterator();
while (it.hasNext()) {
MapMouseListener target = it.next();
if (consumed) {
target.mouseMoved();
} else {
consumed = target.mouseMoved(evt);
}
}
}
// consumed was used above to figure out whether to send
// mouseMoved(evt) or mouseMoved(), now we have to set it
// based on whether the MouseMode should be consuming events.
consumed &= consumeEvents;
boolean ignoreConsumed = !consumed || (consumed && ((proxyDistributionMask & PROXY_ACK_CONSUMED_MOUSE_MOVED) == 0));
if (proxy != null && ignoreConsumed && !evt.isShiftDown()) {
proxy.mouseMoved(evt);
consumed = true;
}
return consumed;
}
/**
* Request to have the parent MapMouseMode act as a proxy for a MapMouseMode
* that wants to remain hidden. Can be useful for directing events to one
* object.
*
* @param mmm the hidden MapMouseMode for this MapMouseMode to send events
* to.
* @param pdm the proxy distribution mask to use, which lets this support
* object notify its targets of events if the parent is acting as a
* proxy.
* @return true if the proxy setup (essentially a lock) is successful, false
* if the proxy is already set up for another listener.
*/
protected synchronized boolean setProxyFor(MapMouseMode mmm, int pdm) {
proxyDistributionMask = pdm;
if (proxy == null || proxy == mmm) {
proxy = mmm;
return true;
}
return false;
}
/**
* Can check if the MapMouseMode is acting as a proxy for another
* MapMouseMode.
*/
public synchronized boolean isProxyFor(MapMouseMode mmm) {
return proxy == mmm;
}
/**
* Release the proxy lock on the MapMouseMode. Resets the proxy distribution
* mask.
*/
protected synchronized void releaseProxy() {
proxy = null;
proxyDistributionMask = 0;
}
/**
* @return MapMouseMode being proxied (the hidden MapMouseMode that the
* parent mode is providing events to).
*/
public synchronized MapMouseMode getProxied() {
return proxy;
}
/**
* Set the mask that dictates which events get sent to this support object's
* targets even if the parent mouse mode is acting as a proxy.
*/
protected void setProxyDistributionMask(int mask) {
proxyDistributionMask = mask;
}
/**
* Get the mask that dictates which events get sent to this support object's
* targets even if the parent mouse mode is acting as a proxy.
*/
protected int getProxyDistributionMask() {
return proxyDistributionMask;
}
}