001: /*
002: * @(#)LightweightDispatcher.java 1.15 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.awt;
029:
030: import java.awt.event.*;
031: import sun.awt.peer.*;
032:
033: /**
034: * Class to manage the dispatching of events to the lightweight
035: * components contained by a native container.
036: *
037: * @author Timothy Prinzing
038: */
039: class LightweightDispatcher implements java.io.Serializable,
040: AWTEventListener {
041: /*
042: * JDK 1.1 serialVersionUID
043: */
044: private static final long serialVersionUID = 5184291520170872969L;
045: /*
046: * Our own mouse event for when we're dragged over from another hw container
047: */
048: private static final int LWD_MOUSE_DRAGGED_OVER = AWTEvent.RESERVED_ID_MAX + 1;
049:
050: LightweightDispatcher(Container nativeContainer) {
051: this .nativeContainer = nativeContainer;
052: mouseEventTarget = null;
053: eventMask = 0;
054: }
055:
056: /*
057: * Disposes any external resources allocated by the dispatcher
058: */
059: void dispose() {
060: stopListeningForOtherDrags();
061: }
062:
063: /**
064: * Enables events to lightweight components.
065: */
066: void enableEvents(long events) {
067: eventMask |= events;
068: }
069:
070: /**
071: * Dispatches an event to a lightweight sub-component if necessary, and
072: * returns whether or not the event was forwarded to a lightweight
073: * sub-component.
074: *
075: * @param e the event
076: */
077: boolean dispatchEvent(AWTEvent e) {
078: boolean ret = false;
079: if ((eventMask & PROXY_EVENT_MASK) != 0) {
080: if ((e instanceof MouseEvent)
081: && ((eventMask & MOUSE_MASK) != 0)) {
082: MouseEvent me = (MouseEvent) e;
083: ret = processMouseEvent(me);
084: }
085: }
086: if (e instanceof MouseEvent) {
087: // find out what component the mouse moved in
088: MouseEvent me = (MouseEvent) e;
089: if (me.getID() == MouseEvent.MOUSE_MOVED) {
090: cursorOn = nativeContainer.getCursorTarget(me.getX(),
091: me.getY());
092: // 6201639
093: // cursorOn can be null in the following cases
094: // 1) mouse position is on the nativeContainer
095: // 2) mouse position is on a heavyweight
096: // The updateCursor() method always treats a null argument
097: // as the nativeContainer, so we need to make this non-null
098: // check.
099: // (See the related fix in Container.getCursorTarget())
100: if (cursorOn != null) {
101: // 6201639
102: updateCursor(cursorOn);
103: }
104: }
105: }
106: return ret;
107: }
108:
109: /**
110: * This method attempts to distribute a mouse event to a lightweight
111: * component. It tries to avoid doing any unnecessary probes down
112: * into the component tree to minimize the overhead of determining
113: * where to route the event, since mouse movement events tend to
114: * come in large and frequent amounts.
115: */
116: private boolean processMouseEvent(MouseEvent e) {
117: int id = e.getID();
118: Component targetOver;
119: Component lwOver;
120: targetOver = nativeContainer.getMouseEventTarget(e.getX(), e
121: .getY(), true);
122: trackMouseEnterExit(targetOver, e);
123:
124: if (id == MouseEvent.MOUSE_MOVED
125: || id == MouseEvent.MOUSE_PRESSED
126: || id == MouseEvent.MOUSE_RELEASED) {
127: lwOver = (targetOver != nativeContainer) ? targetOver
128: : null;
129: setMouseTarget(lwOver, e);
130: }
131: if (mouseEventTarget != null) {
132: // we are currently forwarding to some component, check
133: // to see if we should continue to forward.
134: switch (id) {
135: case MouseEvent.MOUSE_DRAGGED:
136: if (dragging) {
137: retargetMouseEvent(mouseEventTarget, id, e);
138: }
139: break;
140:
141: case MouseEvent.MOUSE_PRESSED:
142: dragging = true;
143: retargetMouseEvent(mouseEventTarget, id, e);
144: break;
145:
146: case MouseEvent.MOUSE_RELEASED:
147: dragging = false;
148: retargetMouseEvent(mouseEventTarget, id, e);
149: break;
150:
151: // MOUSE_CLICKED should never be dispatched to a Component
152: // other than that which received the MOUSE_PRESSED event. If the
153: // mouse is now over a different Component, don't dispatch the event.
154: // The previous fix for a similar problem was associated with bug
155: // 4155217.
156: case MouseEvent.MOUSE_CLICKED:
157: if (targetOver == mouseEventTarget) {
158: retargetMouseEvent(mouseEventTarget, id, e);
159: }
160: break;
161:
162: case MouseEvent.MOUSE_ENTERED:
163: break;
164:
165: case MouseEvent.MOUSE_EXITED:
166: if (!dragging) {
167: setMouseTarget(null, e);
168: }
169: break;
170:
171: case MouseEvent.MOUSE_MOVED:
172: retargetMouseEvent(mouseEventTarget, id, e);
173: break;
174: }
175: e.consume();
176: }
177: return e.isConsumed();
178: }
179:
180: /**
181: * Change the current target of mouse events.
182: */
183: private void setMouseTarget(Component target, MouseEvent e) {
184: if (target != mouseEventTarget) {
185: //System.out.println("setMouseTarget: " + target);
186: mouseEventTarget = target;
187: }
188: }
189:
190: /*
191: * Generates enter/exit events as mouse moves over lw components
192: * @param targetOver Target mouse is over (including native container)
193: * @param e Mouse event in native container
194: */
195: private void trackMouseEnterExit(Component targetOver, MouseEvent e) {
196: Component targetEnter = null;
197: int id = e.getID();
198: if (id != MouseEvent.MOUSE_EXITED
199: && id != MouseEvent.MOUSE_DRAGGED
200: && id != LWD_MOUSE_DRAGGED_OVER
201: && isMouseInNativeContainer == false) {
202: // any event but an exit or drag means we're in the native container
203: isMouseInNativeContainer = true;
204: startListeningForOtherDrags();
205: } else if (id == MouseEvent.MOUSE_EXITED) {
206: isMouseInNativeContainer = false;
207: stopListeningForOtherDrags();
208: }
209: if (isMouseInNativeContainer) {
210: targetEnter = targetOver;
211: }
212: //System.out.println("targetEnter = " + targetEnter);
213: //System.out.println("targetLastEntered = " + targetLastEntered);
214:
215: if (targetLastEntered == targetEnter) {
216: return;
217: }
218: retargetMouseEvent(targetLastEntered, MouseEvent.MOUSE_EXITED,
219: e);
220: if (id == MouseEvent.MOUSE_EXITED) {
221: // consume native exit event if we generate one
222: e.consume();
223: }
224: retargetMouseEvent(targetEnter, MouseEvent.MOUSE_ENTERED, e);
225: if (id == MouseEvent.MOUSE_ENTERED) {
226: // consume native enter event if we generate one
227: e.consume();
228: }
229: //System.out.println("targetLastEntered: " + targetLastEntered);
230: targetLastEntered = targetEnter;
231: }
232:
233: private void startListeningForOtherDrags() {
234: java.security.AccessController
235: .doPrivileged(new java.security.PrivilegedAction() {
236: public Object run() {
237: nativeContainer
238: .getToolkit()
239: .addAWTEventListener(
240: LightweightDispatcher.this ,
241: AWTEvent.MOUSE_EVENT_MASK
242: | AWTEvent.MOUSE_MOTION_EVENT_MASK);
243: return null;
244: }
245: });
246: }
247:
248: private void stopListeningForOtherDrags() {
249: java.security.AccessController
250: .doPrivileged(new java.security.PrivilegedAction() {
251: public Object run() {
252: nativeContainer.getToolkit()
253: .removeAWTEventListener(
254: LightweightDispatcher.this );
255: return null;
256: }
257: });
258: // removed any queued up dragged-over events
259: Toolkit.getEventQueue().removeEvents(MouseEvent.class,
260: LWD_MOUSE_DRAGGED_OVER);
261: }
262:
263: /*
264: * (Implementation of AWTEventListener)
265: * Listen for drag events posted in other hw components so we can
266: * track enter/exit regardless of where a drag originated
267: */
268: public void eventDispatched(AWTEvent e) {
269: boolean isForeignDrag = (e instanceof MouseEvent)
270: && (e.id == MouseEvent.MOUSE_DRAGGED)
271: && (e.getSource() != nativeContainer);
272: if (!isForeignDrag) {
273: // only interested in drags from other hw components
274: return;
275: }
276: // execute trackMouseEnterExit on EventDispatchThread
277: Toolkit.getEventQueue()
278: .postEvent(
279: new TrackEnterExitEvent(nativeContainer,
280: (MouseEvent) e));
281: }
282:
283: /*
284: * ActiveEvent that calls trackMouseEnterExit as a result of a drag
285: * originating in a 'foreign' hw container. Normally, we'd only be
286: * able to track mouse events in our own hw container.
287: */
288: private class TrackEnterExitEvent extends AWTEvent implements
289: ActiveEvent {
290: MouseEvent srcEvent;
291:
292: public TrackEnterExitEvent(Component trackSrc, MouseEvent e) {
293: super (trackSrc, 0);
294: srcEvent = e;
295: }
296:
297: public void dispatch() {
298: MouseEvent me;
299: synchronized (nativeContainer.getTreeLock()) {
300: Component srcComponent = srcEvent.getComponent();
301: // component may have disappeared since drag event posted
302: // (i.e. Swing hierarchical menus)
303: if (!srcComponent.isShowing()
304: || !nativeContainer.isShowing()) {
305: return;
306: }
307: //
308: // create an internal 'dragged-over' event indicating
309: // we are being dragged over from another hw component
310: //
311: me = new MouseEvent(nativeContainer,
312: LWD_MOUSE_DRAGGED_OVER, srcEvent.getWhen(),
313: srcEvent.getModifiers(), srcEvent.getX(),
314: srcEvent.getY(), srcEvent.getClickCount(),
315: srcEvent.isPopupTrigger());
316: // translate coordinates to this native container
317: Point ptSrcOrigin = srcComponent.getLocationOnScreen();
318: Point ptDstOrigin = nativeContainer
319: .getLocationOnScreen();
320: me.translatePoint(ptSrcOrigin.x - ptDstOrigin.x,
321: ptSrcOrigin.y - ptDstOrigin.y);
322: }
323: //System.out.println("Track event: " + me);
324: // feed the 'dragged-over' event directly to the enter/exit
325: // code (not a real event so don't pass it to dispatchEvent)
326: Component targetOver = nativeContainer.getMouseEventTarget(
327: me.getX(), me.getY(), true);
328: trackMouseEnterExit(targetOver, me);
329: }
330: }
331:
332: /**
333: * Sends a mouse event to the current mouse event recipient using
334: * the given event (sent to the windowed host) as a srcEvent. If
335: * the mouse event target is still in the component tree, the
336: * coordinates of the event are translated to those of the target.
337: * If the target has been removed, we don't bother to send the
338: * message.
339: */
340: void retargetMouseEvent(Component target, int id, MouseEvent e) {
341: if (target == null) {
342: return; // mouse is over another hw component
343: }
344: int x = e.getX(), y = e.getY();
345: Component component;
346: for (component = target; component != null
347: && component != nativeContainer; component = component
348: .getParent()) {
349: x -= component.x;
350: y -= component.y;
351: }
352: if (component != null) {
353: MouseEvent retargeted = new MouseEvent(target, id, e
354: .getWhen(), e.getModifiers(), x, y, e
355: .getClickCount(), e.isPopupTrigger());
356: if (target == nativeContainer) {
357: // avoid recursively calling LightweightDispatcher...
358: ((Container) target).dispatchEventToSelf(retargeted);
359: } else {
360: target.dispatchEvent(retargeted);
361: }
362: }
363: }
364:
365: /**
366: * Set the cursor for a lightweight component
367: * Enforce that null cursor means inheriting from parent
368: */
369: void updateCursor(Component comp) {
370: // if user wants to change the cursor, we do it even mouse is dragging
371: // so LightweightDispatcher's dragging state is not checked here
372: if (comp != cursorOn) {
373: return;
374: }
375:
376: if (comp == null) {
377: comp = nativeContainer;
378: }
379: Cursor cursor = comp.getCursor();
380: while (cursor == null && comp != nativeContainer) {
381: comp = comp.getParent();
382: if (comp == null) {
383: cursor = nativeContainer.getCursor();
384: break;
385: }
386: cursor = comp.getCursor();
387: }
388: if (cursor != lightCursor) {
389: lightCursor = cursor;
390: // Only change the cursor on the peer, because we want client code to think
391: // that the Container's cursor only changes in response to setCursor calls.
392: ComponentPeer ncPeer = nativeContainer.peer;
393: if (ncPeer != null) {
394: ncPeer.setCursor(cursor);
395: }
396: }
397: }
398:
399: /**
400: * get the lightweight component mouse cursor is on
401: * null means the nativeContainer
402: */
403: Component getCursorOn() {
404: return cursorOn;
405: }
406:
407: // --- member variables -------------------------------
408:
409: /**
410: * The windowed container that might be hosting events for
411: * lightweight components.
412: */
413: private Container nativeContainer;
414: /**
415: * The current lightweight component that has focus that is being
416: * hosted by this container. If this is a null reference then
417: * there is currently no focus on a lightweight component being
418: * hosted by this container
419: */
420: private Component focus;
421: /**
422: * The current lightweight component being hosted by this windowed
423: * component that has mouse events being forwarded to it. If this
424: * is null, there are currently no mouse events being forwarded to
425: * a lightweight component.
426: */
427: private transient Component mouseEventTarget;
428: /**
429: * lightweight component the mouse cursor is on
430: */
431: private transient Component cursorOn;
432: /**
433: * The last component entered
434: */
435: private transient Component targetLastEntered;
436: /**
437: * Is the mouse over the native container
438: */
439: private transient boolean isMouseInNativeContainer = false;
440: /**
441: * Indicates if the mouse pointer is currently being dragged...
442: * this is needed because we may receive exit events while dragging
443: * and need to keep the current mouse target in this case.
444: */
445: private boolean dragging;
446: /**
447: * The cursor that is currently displayed for the lightwieght
448: * components. Remember this cursor, so we do not need to
449: * change cursor on every mouse event.
450: */
451: private Cursor lightCursor;
452: /**
453: * The event mask for contained lightweight components. Lightweight
454: * components need a windowed container to host window-related
455: * events. This seperate mask indicates events that have been
456: * requested by contained lightweight components without effecting
457: * the mask of the windowed component itself.
458: */
459: private long eventMask;
460: /**
461: * The kind of events routed to lightweight components from windowed
462: * hosts.
463: */
464: private static final long PROXY_EVENT_MASK = AWTEvent.FOCUS_EVENT_MASK
465: | AWTEvent.KEY_EVENT_MASK
466: | AWTEvent.MOUSE_EVENT_MASK
467: | AWTEvent.MOUSE_MOTION_EVENT_MASK;
468: private static final long MOUSE_MASK = AWTEvent.MOUSE_EVENT_MASK
469: | AWTEvent.MOUSE_MOTION_EVENT_MASK;
470: }
|