/* * @(#)LightweightDispatcher.java 1.15 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ package java.awt; import java.awt.event.*; import sun.awt.peer.*; /** * Class to manage the dispatching of events to the lightweight * components contained by a native container. * * @author Timothy Prinzing */ class LightweightDispatcher implements java.io.Serializable, AWTEventListener { /* * JDK 1.1 serialVersionUID */ private static final long serialVersionUID = 5184291520170872969L; /* * Our own mouse event for when we're dragged over from another hw container */ private static final int LWD_MOUSE_DRAGGED_OVER = AWTEvent.RESERVED_ID_MAX + 1; LightweightDispatcher(Container nativeContainer) { this.nativeContainer = nativeContainer; mouseEventTarget = null; eventMask = 0; } /* * Disposes any external resources allocated by the dispatcher */ void dispose() { stopListeningForOtherDrags(); } /** * Enables events to lightweight components. */ void enableEvents(long events) { eventMask |= events; } /** * Dispatches an event to a lightweight sub-component if necessary, and * returns whether or not the event was forwarded to a lightweight * sub-component. * * @param e the event */ boolean dispatchEvent(AWTEvent e) { boolean ret = false; if ((eventMask & PROXY_EVENT_MASK) != 0) { if ((e instanceof MouseEvent) && ((eventMask & MOUSE_MASK) != 0)) { MouseEvent me = (MouseEvent) e; ret = processMouseEvent(me); } } if (e instanceof MouseEvent) { // find out what component the mouse moved in MouseEvent me = (MouseEvent) e; if (me.getID() == MouseEvent.MOUSE_MOVED) { cursorOn = nativeContainer.getCursorTarget(me.getX(), me.getY()); // 6201639 // cursorOn can be null in the following cases // 1) mouse position is on the nativeContainer // 2) mouse position is on a heavyweight // The updateCursor() method always treats a null argument // as the nativeContainer, so we need to make this non-null // check. // (See the related fix in Container.getCursorTarget()) if ( cursorOn != null ) { // 6201639 updateCursor(cursorOn); } } } return ret; } /** * This method attempts to distribute a mouse event to a lightweight * component. It tries to avoid doing any unnecessary probes down * into the component tree to minimize the overhead of determining * where to route the event, since mouse movement events tend to * come in large and frequent amounts. */ private boolean processMouseEvent(MouseEvent e) { int id = e.getID(); Component targetOver; Component lwOver; targetOver = nativeContainer.getMouseEventTarget(e.getX(), e.getY(), true); trackMouseEnterExit(targetOver, e); if (id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_PRESSED || id == MouseEvent.MOUSE_RELEASED ) { lwOver = (targetOver != nativeContainer) ? targetOver : null; setMouseTarget(lwOver, e); } if (mouseEventTarget != null) { // we are currently forwarding to some component, check // to see if we should continue to forward. switch (id) { case MouseEvent.MOUSE_DRAGGED: if (dragging) { retargetMouseEvent(mouseEventTarget, id, e); } break; case MouseEvent.MOUSE_PRESSED: dragging = true; retargetMouseEvent(mouseEventTarget, id, e); break; case MouseEvent.MOUSE_RELEASED: dragging = false; retargetMouseEvent(mouseEventTarget, id, e); break; // MOUSE_CLICKED should never be dispatched to a Component // other than that which received the MOUSE_PRESSED event. If the // mouse is now over a different Component, don't dispatch the event. // The previous fix for a similar problem was associated with bug // 4155217. case MouseEvent.MOUSE_CLICKED: if (targetOver == mouseEventTarget) { retargetMouseEvent(mouseEventTarget, id, e); } break; case MouseEvent.MOUSE_ENTERED: break; case MouseEvent.MOUSE_EXITED: if (!dragging) { setMouseTarget(null, e); } break; case MouseEvent.MOUSE_MOVED: retargetMouseEvent(mouseEventTarget, id, e); break; } e.consume(); } return e.isConsumed(); } /** * Change the current target of mouse events. */ private void setMouseTarget(Component target, MouseEvent e) { if (target != mouseEventTarget) { //System.out.println("setMouseTarget: " + target); mouseEventTarget = target; } } /* * Generates enter/exit events as mouse moves over lw components * @param targetOver Target mouse is over (including native container) * @param e Mouse event in native container */ private void trackMouseEnterExit(Component targetOver, MouseEvent e) { Component targetEnter = null; int id = e.getID(); if (id != MouseEvent.MOUSE_EXITED && id != MouseEvent.MOUSE_DRAGGED && id != LWD_MOUSE_DRAGGED_OVER && isMouseInNativeContainer == false) { // any event but an exit or drag means we're in the native container isMouseInNativeContainer = true; startListeningForOtherDrags(); } else if (id == MouseEvent.MOUSE_EXITED) { isMouseInNativeContainer = false; stopListeningForOtherDrags(); } if (isMouseInNativeContainer) { targetEnter = targetOver; } //System.out.println("targetEnter = " + targetEnter); //System.out.println("targetLastEntered = " + targetLastEntered); if (targetLastEntered == targetEnter) { return; } retargetMouseEvent(targetLastEntered, MouseEvent.MOUSE_EXITED, e); if (id == MouseEvent.MOUSE_EXITED) { // consume native exit event if we generate one e.consume(); } retargetMouseEvent(targetEnter, MouseEvent.MOUSE_ENTERED, e); if (id == MouseEvent.MOUSE_ENTERED) { // consume native enter event if we generate one e.consume(); } //System.out.println("targetLastEntered: " + targetLastEntered); targetLastEntered = targetEnter; } private void startListeningForOtherDrags() { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run() { nativeContainer.getToolkit().addAWTEventListener(LightweightDispatcher.this, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); return null; } } ); } private void stopListeningForOtherDrags() { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run() { nativeContainer.getToolkit().removeAWTEventListener(LightweightDispatcher.this); return null; } } ); // removed any queued up dragged-over events Toolkit.getEventQueue().removeEvents(MouseEvent.class, LWD_MOUSE_DRAGGED_OVER); } /* * (Implementation of AWTEventListener) * Listen for drag events posted in other hw components so we can * track enter/exit regardless of where a drag originated */ public void eventDispatched(AWTEvent e) { boolean isForeignDrag = (e instanceof MouseEvent) && (e.id == MouseEvent.MOUSE_DRAGGED) && (e.getSource() != nativeContainer); if (!isForeignDrag) { // only interested in drags from other hw components return; } // execute trackMouseEnterExit on EventDispatchThread Toolkit.getEventQueue().postEvent( new TrackEnterExitEvent(nativeContainer, (MouseEvent) e) ); } /* * ActiveEvent that calls trackMouseEnterExit as a result of a drag * originating in a 'foreign' hw container. Normally, we'd only be * able to track mouse events in our own hw container. */ private class TrackEnterExitEvent extends AWTEvent implements ActiveEvent { MouseEvent srcEvent; public TrackEnterExitEvent(Component trackSrc, MouseEvent e) { super(trackSrc, 0); srcEvent = e; } public void dispatch() { MouseEvent me; synchronized (nativeContainer.getTreeLock()) { Component srcComponent = srcEvent.getComponent(); // component may have disappeared since drag event posted // (i.e. Swing hierarchical menus) if (!srcComponent.isShowing() || !nativeContainer.isShowing()) { return; } // // create an internal 'dragged-over' event indicating // we are being dragged over from another hw component // me = new MouseEvent(nativeContainer, LWD_MOUSE_DRAGGED_OVER, srcEvent.getWhen(), srcEvent.getModifiers(), srcEvent.getX(), srcEvent.getY(), srcEvent.getClickCount(), srcEvent.isPopupTrigger()); // translate coordinates to this native container Point ptSrcOrigin = srcComponent.getLocationOnScreen(); Point ptDstOrigin = nativeContainer.getLocationOnScreen(); me.translatePoint(ptSrcOrigin.x - ptDstOrigin.x, ptSrcOrigin.y - ptDstOrigin.y); } //System.out.println("Track event: " + me); // feed the 'dragged-over' event directly to the enter/exit // code (not a real event so don't pass it to dispatchEvent) Component targetOver = nativeContainer.getMouseEventTarget(me.getX(), me.getY(), true); trackMouseEnterExit(targetOver, me); } } /** * Sends a mouse event to the current mouse event recipient using * the given event (sent to the windowed host) as a srcEvent. If * the mouse event target is still in the component tree, the * coordinates of the event are translated to those of the target. * If the target has been removed, we don't bother to send the * message. */ void retargetMouseEvent(Component target, int id, MouseEvent e) { if (target == null) { return; // mouse is over another hw component } int x = e.getX(), y = e.getY(); Component component; for (component = target; component != null && component != nativeContainer; component = component.getParent()) { x -= component.x; y -= component.y; } if (component != null) { MouseEvent retargeted = new MouseEvent(target, id, e.getWhen(), e.getModifiers(), x, y, e.getClickCount(), e.isPopupTrigger()); if (target == nativeContainer) { // avoid recursively calling LightweightDispatcher... ((Container) target).dispatchEventToSelf(retargeted); } else { target.dispatchEvent(retargeted); } } } /** * Set the cursor for a lightweight component * Enforce that null cursor means inheriting from parent */ void updateCursor(Component comp) { // if user wants to change the cursor, we do it even mouse is dragging // so LightweightDispatcher's dragging state is not checked here if (comp != cursorOn) { return; } if (comp == null) { comp = nativeContainer; } Cursor cursor = comp.getCursor(); while (cursor == null && comp != nativeContainer) { comp = comp.getParent(); if (comp == null) { cursor = nativeContainer.getCursor(); break; } cursor = comp.getCursor(); } if (cursor != lightCursor) { lightCursor = cursor; // Only change the cursor on the peer, because we want client code to think // that the Container's cursor only changes in response to setCursor calls. ComponentPeer ncPeer = nativeContainer.peer; if (ncPeer != null) { ncPeer.setCursor(cursor); } } } /** * get the lightweight component mouse cursor is on * null means the nativeContainer */ Component getCursorOn() { return cursorOn; } // --- member variables ------------------------------- /** * The windowed container that might be hosting events for * lightweight components. */ private Container nativeContainer; /** * The current lightweight component that has focus that is being * hosted by this container. If this is a null reference then * there is currently no focus on a lightweight component being * hosted by this container */ private Component focus; /** * The current lightweight component being hosted by this windowed * component that has mouse events being forwarded to it. If this * is null, there are currently no mouse events being forwarded to * a lightweight component. */ private transient Component mouseEventTarget; /** * lightweight component the mouse cursor is on */ private transient Component cursorOn; /** * The last component entered */ private transient Component targetLastEntered; /** * Is the mouse over the native container */ private transient boolean isMouseInNativeContainer = false; /** * Indicates if the mouse pointer is currently being dragged... * this is needed because we may receive exit events while dragging * and need to keep the current mouse target in this case. */ private boolean dragging; /** * The cursor that is currently displayed for the lightwieght * components. Remember this cursor, so we do not need to * change cursor on every mouse event. */ private Cursor lightCursor; /** * The event mask for contained lightweight components. Lightweight * components need a windowed container to host window-related * events. This seperate mask indicates events that have been * requested by contained lightweight components without effecting * the mask of the windowed component itself. */ private long eventMask; /** * The kind of events routed to lightweight components from windowed * hosts. */ private static final long PROXY_EVENT_MASK = AWTEvent.FOCUS_EVENT_MASK | AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK; private static final long MOUSE_MASK = AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK; }