diff options
author | Tom Tromey <tromey@gcc.gnu.org> | 2007-01-09 19:58:05 +0000 |
---|---|---|
committer | Tom Tromey <tromey@gcc.gnu.org> | 2007-01-09 19:58:05 +0000 |
commit | 97b8365cafc3a344a22d3980b8ed885f5c6d8357 (patch) | |
tree | 996a5f57d4a68c53473382e45cb22f574cb3e4db /libjava/classpath/java/awt/LightweightDispatcher.java | |
parent | c648dedbde727ca3f883bb5fd773aa4af70d3369 (diff) | |
download | gcc-97b8365cafc3a344a22d3980b8ed885f5c6d8357.zip gcc-97b8365cafc3a344a22d3980b8ed885f5c6d8357.tar.gz gcc-97b8365cafc3a344a22d3980b8ed885f5c6d8357.tar.bz2 |
Merged gcj-eclipse branch to trunk.
From-SVN: r120621
Diffstat (limited to 'libjava/classpath/java/awt/LightweightDispatcher.java')
-rw-r--r-- | libjava/classpath/java/awt/LightweightDispatcher.java | 384 |
1 files changed, 199 insertions, 185 deletions
diff --git a/libjava/classpath/java/awt/LightweightDispatcher.java b/libjava/classpath/java/awt/LightweightDispatcher.java index 3ea3f90..04196bd 100644 --- a/libjava/classpath/java/awt/LightweightDispatcher.java +++ b/libjava/classpath/java/awt/LightweightDispatcher.java @@ -38,7 +38,10 @@ exception statement from your version. */ package java.awt; +import java.awt.event.InputEvent; import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.peer.LightweightPeer; import java.util.WeakHashMap; /** @@ -49,7 +52,7 @@ import java.util.WeakHashMap; * * @author Roman Kennke (kennke@aicas.com) */ -class LightweightDispatcher +final class LightweightDispatcher { /** @@ -60,26 +63,17 @@ class LightweightDispatcher private static WeakHashMap instances = new WeakHashMap(); /** - * The component that is the start of a mouse dragging. All MOUSE_DRAGGED - * events that follow the initial press must have the source set to this, - * as well as the MOUSE_RELEASED event following the dragging. - */ - private Component dragTarget; - - /** - * Stores the button number which started the drag operation. This is needed - * because we want to handle only one drag operation and only the button that - * started the dragging should be able to stop it (by a button release). - */ - private int dragButton; - - /** * The last mouse event target. If the target changes, additional * MOUSE_ENTERED and MOUSE_EXITED events must be dispatched. */ private Component lastTarget; /** + * The current mouseEventTarget. + */ + private Component mouseEventTarget; + + /** * Returns an instance of LightweightDispatcher for the current thread's * thread group. * @@ -113,9 +107,9 @@ class LightweightDispatcher * * @param event the event */ - public boolean dispatchEvent(AWTEvent event) + public boolean dispatchEvent(final AWTEvent event) { - if (event instanceof MouseEvent && event.getSource() instanceof Window) + if (event instanceof MouseEvent) { MouseEvent mouseEvent = (MouseEvent) event; return handleMouseEvent(mouseEvent); @@ -130,151 +124,49 @@ class LightweightDispatcher * @param ev the mouse event * @return whether or not we found a lightweight that handled the event. */ - private boolean handleMouseEvent(MouseEvent ev) + private boolean handleMouseEvent(final MouseEvent ev) { - Window window = (Window) ev.getSource(); - // Find the target for the mouse event. We first seach the deepest - // component at the specified location. The we go up to its parent and - // try to find a neighbor of the deepest component that is suitable as - // mouse event target (it must be showing, at that location and have either - // a MouseListener or MouseMotionListener installed). If no such component - // is found, then we walk up the container hierarchy and find the next - // container that has a MouseListener or MouseMotionListener installed. - Component deepest = window.findComponentAt(ev.getX(), ev.getY()); - if (deepest == null) - return false; - Container parent = deepest.getParent(); - Point loc = ev.getPoint(); - loc = convertPointToChild(window, loc, parent); - Component target = null; - if (parent != null) - { - target = findTarget(parent, loc); - while (target == null && parent != null) - { - if (parent.mouseListener != null - || parent.mouseMotionListener != null - || (parent.eventMask - & (AWTEvent.MOUSE_EVENT_MASK - | AWTEvent.MOUSE_MOTION_EVENT_MASK)) != 0) - { - target = parent; - } - else - parent = parent.getParent(); - } - } - if (target == null || target.isLightweight()) + Container container = (Container) ev.getSource(); + Component target = findTarget(container, ev.getX(), ev.getY()); + trackEnterExit(target, ev); + int id = ev.getID(); + + // Dont update the mouseEventTarget when dragging. Also, MOUSE_CLICKED + // must be dispatched to the original target of MOUSE_PRESSED, so don't + // update in this case either. + if (! isDragging(ev) && id != MouseEvent.MOUSE_CLICKED) + mouseEventTarget = (target != container) ? target : null; + + if (mouseEventTarget != null) { - // Dispatch additional MOUSE_EXITED and MOUSE_ENTERED if event target - // is different from the last event target. - if (target != lastTarget) + switch (id) { - if (lastTarget != null) - { - Point p1 = convertPointToChild(window, ev.getPoint(), - lastTarget); - MouseEvent mouseExited = - new MouseEvent(lastTarget, MouseEvent.MOUSE_EXITED, - ev.getWhen(), ev.getModifiers(), p1.x, p1.y, - ev.getClickCount(), ev.isPopupTrigger()); - //System.err.println("event: " + mouseExited); - lastTarget.dispatchEvent(mouseExited); - } - - // If a target exists dispatch the MOUSE_ENTERED event. - // Experimenting shows that the MOUSE_ENTERED is also dispatched - // when the mouse is dragging. - if (target != null) - { - Point p = convertPointToChild(window, ev.getPoint(), target); - MouseEvent mouseEntered = - new MouseEvent(target, - MouseEvent.MOUSE_ENTERED, ev.getWhen(), - ev.getModifiers(), p.x, p.y, ev.getClickCount(), - ev.isPopupTrigger()); - //System.err.println("event: " + mouseEntered); - target.dispatchEvent(mouseEntered); - } - } - - switch (ev.getID()) - { - case MouseEvent.MOUSE_PRESSED: - // Handle the start of a drag operation or discard the event if - // one is already in progress. This prevents focus changes with the - // other mouse buttons when one is used for dragging. - if (dragTarget == null) - { - lastTarget = dragTarget = target; - - // Save the button that started the drag operation. - dragButton = ev.getButton(); - } - else - return false; - + case MouseEvent.MOUSE_ENTERED: + case MouseEvent.MOUSE_EXITED: + // This is already handled in trackEnterExit(). break; + case MouseEvent.MOUSE_PRESSED: case MouseEvent.MOUSE_RELEASED: - // Stop the drag operation only when the button that started - // it was released. - if (dragTarget != null && dragButton == ev.getButton()) - { - // Only post MOUSE_RELEASED to dragTarget (set in - // MOUSE_PRESSED) when the dragTarget is actually visible. - // Otherwise post the event to the normal target. - if (dragTarget.isVisible()) - target = dragTarget; - dragTarget = null; - } - - lastTarget = target; + case MouseEvent.MOUSE_MOVED: + redispatch(ev, mouseEventTarget, id); break; case MouseEvent.MOUSE_CLICKED: - // When we receive a MOUSE_CLICKED, we set the target to the - // previous target, which must have been a MOUSE_RELEASED event. - // This is necessary for the case when the MOUSE_RELEASED has - // caused the original target (like an internal component) go - // away. - // This line is the reason why it is not possible to move the - // 'lastTarget = target' assignment before the switch-statement. - target = lastTarget; + // MOUSE_CLICKED must be dispatched to the original target of + // MOUSE_PRESSED. + if (target == mouseEventTarget) + redispatch(ev, mouseEventTarget, id); break; case MouseEvent.MOUSE_DRAGGED: - // We consider only dragTarget for redispatching the event still - // we have to act in a way that the newly found target component - // was handled. - lastTarget = target; - target = dragTarget; + if (isDragging(ev)) + redispatch(ev, mouseEventTarget, id); break; - default: - // Only declare current target as the old value in all other - // cases. - lastTarget = target; - break; - } - - if (target != null) - { - Point targetCoordinates = convertPointToChild(window, - ev.getPoint(), - target); - int dx = targetCoordinates.x - ev.getX(); - int dy = targetCoordinates.y - ev.getY(); - ev.translatePoint(dx, dy); - ev.setSource(target); - target.dispatchEvent(ev); - - // We reset the event, so that the normal event dispatching is not - // influenced by this modified event. - ev.setSource(window); - ev.translatePoint(-dx, -dy); + case MouseEvent.MOUSE_WHEEL: + redispatch(ev, mouseEventTarget, id); } - - return true; + ev.consume(); } - else - return false; + + return ev.isConsumed(); } /** @@ -290,58 +182,180 @@ class LightweightDispatcher * @return the actual receiver of the mouse event, or null, if no such * component has been found */ - private Component findTarget(Container c, Point loc) + private Component findTarget(final Container c, final int x, final int y) { - int numComponents = c.getComponentCount(); Component target = null; - if (c != null) + + // First we check the children of the container. + + // Note: It is important that we use the package private Container + // fields ncomponents and component here. There are applications + // that override getComponentCount() + // and getComponent() to hide internal components, which makes + // the LightweightDispatcher not work correctly in these cases. + // As a positive sideeffect this is slightly more efficient. + int nChildren = c.ncomponents; + for (int i = 0; i < nChildren && target == null; i++) { - for (int i = 0; i < numComponents; i++) + Component child = c.component[i]; + int childX = x - child.x; + int childY = y - child.y; + if (child != null && child.visible + && child.peer instanceof LightweightPeer + && child.contains(childX, childY)) { - Component child = c.getComponent(i); - if (child.isShowing()) + // Check if there's a deeper possible target. + if (child instanceof Container) { - if (child.contains(loc.x - child.getX(), loc.y - child.getY()) - && (child.mouseListener != null - || child.mouseMotionListener != null - || (child.eventMask - & (AWTEvent.MOUSE_EVENT_MASK - | AWTEvent.MOUSE_MOTION_EVENT_MASK)) != 0)) - { - target = child; - break; - } + Component deeper = findTarget((Container) child, + childX, childY); + if (deeper != null) + target = deeper; } + // Check if the child itself is interested in mouse events. + else if (isMouseListening(child)) + target = child; } } + + // Check the container itself, if we didn't find a target yet. + if (target == null && c.contains(x, y) && isMouseListening(c)) + target = c; + return target; } /** - * Converts a point in the parent's coordinate system to a child coordinate - * system. The resulting point is stored in the same Point object and - * returned. + * Checks if the specified component would be interested in a mouse event. + * + * @param c the component to check + * + * @return <code>true</code> if the component has mouse listeners installed, + * <code>false</code> otherwise + */ + private boolean isMouseListening(final Component c) + { + // Note: It is important to NOT check if the component is listening + // for a specific event (for instance, mouse motion events). The event + // gets dispatched to the component if the component is listening + // for ANY mouse event, even when the component is not listening for the + // specific type of event. There are applications that depend on this + // (sadly). + return c.mouseListener != null + || c.mouseMotionListener != null + || c.mouseWheelListener != null + || (c.eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0 + || (c.eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0 + || (c.eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0; + } + + /** + * Tracks MOUSE_ENTERED and MOUSE_EXIT as well as MOUSE_MOVED and + * MOUSE_DRAGGED and creates synthetic MOUSE_ENTERED and MOUSE_EXITED for + * lightweight component.s + * + * @param target the current mouse event target + * @param ev the mouse event + */ + private void trackEnterExit(final Component target, final MouseEvent ev) + { + int id = ev.getID(); + if (target != lastTarget) + { + if (lastTarget != null) + redispatch(ev, lastTarget, MouseEvent.MOUSE_EXITED); + if (id == MouseEvent.MOUSE_EXITED) + ev.consume(); + if (target != null) + redispatch(ev, target, MouseEvent.MOUSE_ENTERED); + if (id == MouseEvent.MOUSE_ENTERED) + ev.consume(); + lastTarget = target; + } + + } + + /** + * Redispatches the specified mouse event to the specified target with the + * specified id. * - * @param parent the parent component - * @param p the point - * @param child the child component + * @param ev the mouse event + * @param target the new target + * @param id the new id + */ + private void redispatch(MouseEvent ev, Component target, int id) + { + Component source = ev.getComponent(); + if (target != null) + { + // Translate coordinates. + int x = ev.getX(); + int y = ev.getY(); + for (Component c = target; c != null && c != source; c = c.getParent()) + { + x -= c.x; + y -= c.y; + } + + // Retarget event. + MouseEvent retargeted; + if (id == MouseEvent.MOUSE_WHEEL) + { + MouseWheelEvent mwe = (MouseWheelEvent) ev; + retargeted = new MouseWheelEvent(target, id, ev.getWhen(), + ev.getModifiers() + | ev.getModifiersEx(), x, y, + ev.getClickCount(), + ev.isPopupTrigger(), + mwe.getScrollType(), + mwe.getScrollAmount(), + mwe.getWheelRotation()); + } + else + { + retargeted = new MouseEvent(target, id, ev.getWhen(), + ev.getModifiers() | ev.getModifiersEx(), + x, y, ev.getClickCount(), + ev.isPopupTrigger(), ev.getButton()); + } + + if (target == source) + ((Container) target).dispatchNoLightweight(retargeted); + else + target.dispatchEvent(retargeted); + } + } + + /** + * Determines if we are in the middle of a drag operation, that is, if + * any of the buttons is held down. + * + * @param ev the mouse event to check * - * @return the translated point + * @return <code>true</code> if we are in the middle of a drag operation, + * <code>false</code> otherwise */ - private Point convertPointToChild(Component parent, Point p, - Component child) + private boolean isDragging(MouseEvent ev) { - int offX = 0; - int offY = 0; - Component comp = child; - while (comp != null && comp != parent) + int mods = ev.getModifiersEx(); + int id = ev.getID(); + if (id == MouseEvent.MOUSE_PRESSED || id == MouseEvent.MOUSE_RELEASED) { - offX += comp.getX(); - offY += comp.getY(); - comp = comp.getParent(); + switch (ev.getButton()) + { + case MouseEvent.BUTTON1: + mods ^= InputEvent.BUTTON1_DOWN_MASK; + break; + case MouseEvent.BUTTON2: + mods ^= InputEvent.BUTTON2_DOWN_MASK; + break; + case MouseEvent.BUTTON3: + mods ^= InputEvent.BUTTON3_DOWN_MASK; + break; + } } - p.x -= offX; - p.y -= offY; - return p; + return (mods & (InputEvent.BUTTON1_DOWN_MASK + | InputEvent.BUTTON2_DOWN_MASK + | InputEvent.BUTTON3_DOWN_MASK)) != 0; } } |