001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Michael Danilov, Dmitry A. Durnev
019: * @version $Revision$
020: */package java.awt;
021:
022: import java.awt.event.ComponentEvent;
023: import java.awt.event.FocusEvent;
024: import java.awt.event.InputEvent;
025: import java.awt.event.KeyEvent;
026: import java.awt.event.MouseEvent;
027: import java.awt.event.PaintEvent;
028: import java.awt.event.WindowEvent;
029:
030: import org.apache.harmony.awt.internal.nls.Messages;
031: import org.apache.harmony.awt.wtk.NativeEvent;
032: import org.apache.harmony.awt.wtk.NativeWindow;
033:
034: /**
035: * Helper package-private class for managing lightweight components &
036: * dispatching events from heavyweight source
037: */
038: class Dispatcher {
039:
040: final PopupDispatcher popupDispatcher = new PopupDispatcher();
041:
042: final FocusDispatcher focusDispatcher;
043:
044: final MouseGrabManager mouseGrabManager = new MouseGrabManager();
045:
046: final MouseDispatcher mouseDispatcher;
047:
048: private final ComponentDispatcher componentDispatcher = new ComponentDispatcher();
049:
050: private final KeyDispatcher keyDispatcher = new KeyDispatcher();
051:
052: private final Toolkit toolkit;
053:
054: int clickInterval = 250;
055:
056: /**
057: * @param toolkit - AWT toolkit
058: */
059: Dispatcher(Toolkit toolkit) {
060: this .toolkit = toolkit;
061:
062: focusDispatcher = new FocusDispatcher(toolkit);
063: mouseDispatcher = new MouseDispatcher(mouseGrabManager, toolkit);
064: }
065:
066: /**
067: * Dispatch native event: produce appropriate AWT events,
068: * update component's fields when needed
069: * @param event - native event to dispatch
070: * @return - true means default processing by OS is not needed
071: */
072: public boolean onEvent(NativeEvent event) {
073: int eventId = event.getEventId();
074:
075: if (eventId == NativeEvent.ID_CREATED) {
076: return toolkit.onWindowCreated(event.getWindowId());
077: } else if (eventId == NativeEvent.ID_MOUSE_GRAB_CANCELED) {
078: return mouseGrabManager.onGrabCanceled();
079: } else if (popupDispatcher.onEvent(event)) {
080: return false;
081: } else {
082: Component src = toolkit.getComponentById(event
083: .getWindowId());
084:
085: if (src != null) {
086: if (((eventId >= ComponentEvent.COMPONENT_FIRST) && (eventId <= ComponentEvent.COMPONENT_LAST))
087: || ((eventId >= WindowEvent.WINDOW_FIRST) && (eventId <= WindowEvent.WINDOW_LAST))
088: || (eventId == NativeEvent.ID_INSETS_CHANGED)
089: || (eventId == NativeEvent.ID_BOUNDS_CHANGED)
090: || (eventId == NativeEvent.ID_THEME_CHANGED)) {
091: return componentDispatcher.dispatch(src, event);
092: } else if ((eventId >= MouseEvent.MOUSE_FIRST)
093: && (eventId <= MouseEvent.MOUSE_LAST)) {
094: return mouseDispatcher.dispatch(src, event);
095: } else if (eventId == PaintEvent.PAINT) {
096: src.redrawManager.addPaintRegion(src, event
097: .getClipRects());
098: return true;
099: }
100: }
101: if ((eventId >= FocusEvent.FOCUS_FIRST)
102: && (eventId <= FocusEvent.FOCUS_LAST)) {
103:
104: return focusDispatcher.dispatch(src, event);
105: } else if ((eventId >= KeyEvent.KEY_FIRST)
106: && (eventId <= KeyEvent.KEY_LAST)) {
107: return keyDispatcher.dispatch(src, event);
108: }
109: }
110:
111: return false;
112: }
113:
114: /**
115: * The dispatcher of native events that affect
116: * component's state or bounds
117: */
118: final class ComponentDispatcher {
119:
120: /**
121: * Handle native event that affects component's state or bounds
122: * @param src - the component updated by the event
123: * @param event - the native event
124: * @return - as in Dispatcher.onEvent()
125: * @see Dispatcher#onEvent(NativeEvent)
126: */
127: boolean dispatch(Component src, NativeEvent event) {
128: int id = event.getEventId();
129:
130: if ((id == NativeEvent.ID_INSETS_CHANGED)
131: || (id == NativeEvent.ID_THEME_CHANGED)) {
132: return dispatchInsets(event, src);
133: } else if ((id >= WindowEvent.WINDOW_FIRST)
134: && (id <= WindowEvent.WINDOW_LAST)) {
135: return dispatchWindow(event, src);
136: } else {
137: return dispatchPureComponent(event, src);
138: }
139: }
140:
141: /**
142: * Handle the change of top-level window's native decorations
143: * @param event - the native event
144: * @param src - the component updated by the event
145: * @return - as in Dispatcher.onEvent()
146: * @see Dispatcher#onEvent(NativeEvent)
147: */
148: boolean dispatchInsets(NativeEvent event, Component src) {
149: if (src instanceof Window) {
150: ((Window) src).setNativeInsets(event.getInsets());
151: }
152: return false;
153: }
154:
155: /**
156: * Handle the change of top-level window's state
157: * @param event - the native event
158: * @param src - the component updated by the event
159: * @return - as in Dispatcher.onEvent()
160: * @see Dispatcher#onEvent(NativeEvent)
161: */
162: boolean dispatchWindow(NativeEvent event, Component src) {
163: Window window = (Window) src;
164: int id = event.getEventId();
165:
166: if (id == WindowEvent.WINDOW_CLOSING) {
167: toolkit.getSystemEventQueueImpl().postEvent(
168: new WindowEvent(window,
169: WindowEvent.WINDOW_CLOSING));
170:
171: return true;
172: } else if (id == WindowEvent.WINDOW_STATE_CHANGED) {
173: if (window instanceof Frame) {
174: ((Frame) window).updateExtendedState(event
175: .getWindowState());
176: }
177: }
178:
179: return false;
180: }
181:
182: /**
183: * Handle the change of component's size and/or position
184: * @param event - the native event
185: * @param src - the component updated by the event
186: * @return - as in Dispatcher.onEvent()
187: * @see Dispatcher#onEvent(NativeEvent)
188: */
189: private boolean dispatchPureComponent(NativeEvent event,
190: Component src) {
191: Rectangle rect = event.getWindowRect();
192: Point loc = rect.getLocation();
193: int mask;
194:
195: switch (event.getEventId()) {
196: case NativeEvent.ID_BOUNDS_CHANGED:
197: mask = 0;
198: break;
199: case ComponentEvent.COMPONENT_MOVED:
200: mask = NativeWindow.BOUNDS_NOSIZE;
201: break;
202: case ComponentEvent.COMPONENT_RESIZED:
203: mask = NativeWindow.BOUNDS_NOMOVE;
204: break;
205: default:
206: // awt.12E=Unknown component event id.
207: throw new RuntimeException(Messages
208: .getString("awt.12E")); //$NON-NLS-1$
209: }
210:
211: if (!(src instanceof Window)) {
212: Component compTo = src.getParent();
213: Component compFrom = src.getHWAncestor();
214:
215: if ((compTo != null) && (compFrom != null)) {
216: loc = MouseDispatcher.convertPoint(compFrom, loc,
217: compTo);
218: }
219: } else {
220: int windowState = event.getWindowState();
221:
222: if ((windowState >= 0) && (src instanceof Frame)) {
223: ((Frame) src).updateExtendedState(windowState);
224: }
225: }
226: src.setBounds(loc.x, loc.y, rect.width, rect.height, mask,
227: false);
228:
229: return false;
230: }
231:
232: }
233:
234: /**
235: * The dispatcher of the keyboard events
236: */
237: final class KeyDispatcher {
238:
239: /**
240: * Handle the keyboard event using the KeyboardFocusManager
241: * @param src - the component receiving the event
242: * @param event - the native event
243: * @return - as in Dispatcher.onEvent()
244: * @see Dispatcher#onEvent(NativeEvent)
245: */
246: boolean dispatch(Component src, NativeEvent event) {
247: int id = event.getEventId();
248: int modifiers = event.getInputModifiers();
249: int location = event.getKeyLocation();
250: int code = event.getVKey();
251: StringBuffer chars = event.getKeyChars();
252: int charsLength = chars.length();
253: long time = event.getTime();
254: char keyChar = event.getLastChar();
255:
256: if (src == null) {
257: //retarget focus proxy key events to focusOwner:
258: Window focusProxyOwner = toolkit
259: .getFocusProxyOwnerById(event.getWindowId());
260: if (focusProxyOwner == null) {
261: return false;
262: }
263: src = KeyboardFocusManager.actualFocusOwner;
264: }
265:
266: EventQueue eventQueue = toolkit.getSystemEventQueueImpl();
267:
268: if (src != null) {
269: eventQueue.postEvent(new KeyEvent(src, id, time,
270: modifiers, code, keyChar, location));
271: // KEY_TYPED goes after KEY_PRESSED
272: if (id == KeyEvent.KEY_PRESSED) {
273: for (int i = 0; i < charsLength; i++) {
274: keyChar = chars.charAt(i);
275: if (keyChar != KeyEvent.CHAR_UNDEFINED) {
276: eventQueue.postEvent(new KeyEvent(src,
277: KeyEvent.KEY_TYPED, time,
278: modifiers, KeyEvent.VK_UNDEFINED,
279: keyChar,
280: KeyEvent.KEY_LOCATION_UNKNOWN));
281: }
282: }
283: }
284: }
285:
286: return false;
287: }
288:
289: }
290:
291: /**
292: * Retargets the mouse events to the grab owner when mouse is grabbed,
293: * grab and ungrab mouse when mouse buttons are pressed and released
294: */
295: static final class MouseGrabManager {
296:
297: /**
298: * The top-level window holding the mouse grab
299: * that was explicitly started by startGrab() method
300: */
301: private Window nativeGrabOwner = null;
302: /**
303: * The component that owns the synthetic
304: * mouse grab while at least one of the
305: * mouse buttons is pressed
306: */
307: private Component syntheticGrabOwner = null;
308:
309: /**
310: * Previous value of syntheticGrabOwner
311: */
312: private Component lastSyntheticGrabOwner = null;
313:
314: /**
315: * Number of mouse buttons currently pressed
316: */
317: private int syntheticGrabDepth = 0;
318:
319: /**
320: * The callback to be called when the explicit mouse grab ends
321: */
322: private Runnable whenCanceled;
323:
324: /**
325: * Explicitly start the mouse grab
326: * @param grabWindow - the window that will own the grab
327: * @param whenCanceled - the callback to call when the grab ends.
328: * This parameter can be null
329: */
330: void startGrab(Window grabWindow, Runnable whenCanceled) {
331: if (nativeGrabOwner != null) {
332: // awt.12F=Attempt to start nested mouse grab
333: throw new RuntimeException(Messages
334: .getString("awt.12F")); //$NON-NLS-1$
335: }
336:
337: NativeWindow win = grabWindow.getNativeWindow();
338: if (win == null) {
339: // awt.130=Attempt to grab mouse in not displayable window
340: throw new RuntimeException(Messages
341: .getString("awt.130")); //$NON-NLS-1$
342: }
343:
344: nativeGrabOwner = grabWindow;
345: this .whenCanceled = whenCanceled;
346: win.grabMouse();
347: }
348:
349: /**
350: * Ends the explicit mouse grab. If the non-null callback was provided
351: * in the startGrab() method, this callback is called
352: */
353: void endGrab() {
354: if (nativeGrabOwner == null) {
355: return;
356: }
357:
358: Window grabWindow = nativeGrabOwner;
359: nativeGrabOwner = null;
360: NativeWindow win = grabWindow.getNativeWindow();
361:
362: if (win != null) {
363: win.ungrabMouse();
364: if (whenCanceled != null) {
365: whenCanceled.run();
366: whenCanceled = null;
367: }
368: }
369: }
370:
371: /**
372: * Ends both explicit and synthetic grans
373: * @return - always returns false
374: */
375: boolean onGrabCanceled() {
376: endGrab();
377: resetSyntheticGrab();
378:
379: return false;
380: }
381:
382: /**
383: * Starts the synthetic mouse grab, increases the counter
384: * of currently pressed mouse buttons
385: * @param source - the component where mouse press event occured
386: * @return - the component that owns the synthetic grab
387: */
388: Component onMousePressed(Component source) {
389: if (syntheticGrabDepth == 0) {
390: syntheticGrabOwner = source;
391: lastSyntheticGrabOwner = source;
392: }
393: syntheticGrabDepth++;
394:
395: return syntheticGrabOwner;
396: }
397:
398: /**
399: * Decreases the counter of currently pressed mouse buttons,
400: * ends the synthetic mouse grab, when this counter becomes zero
401: * @param source - the component where mouse press event occured
402: * @return - the component that owns the synthetic grab,
403: * or source parameter if mouse grab was released
404: */
405: Component onMouseReleased(Component source) {
406: Component ret = source;
407:
408: if (syntheticGrabOwner != null && nativeGrabOwner == null) {
409: ret = syntheticGrabOwner;
410: }
411: syntheticGrabDepth--;
412: if (syntheticGrabDepth <= 0) {
413: resetSyntheticGrab();
414: lastSyntheticGrabOwner = null;
415: }
416:
417: return ret;
418: }
419:
420: /**
421: * Update the state of synthetic ouse gram
422: * when the mouse is moved/dragged
423: * @param event - the native event
424: */
425: void preprocessEvent(NativeEvent event) {
426: int id = event.getEventId();
427: switch (id) {
428: case MouseEvent.MOUSE_MOVED:
429: if (syntheticGrabOwner != null) {
430: syntheticGrabOwner = null;
431: syntheticGrabDepth = 0;
432: }
433: if (lastSyntheticGrabOwner != null) {
434: lastSyntheticGrabOwner = null;
435: }
436: case MouseEvent.MOUSE_DRAGGED:
437: if (syntheticGrabOwner == null
438: && lastSyntheticGrabOwner != null) {
439: syntheticGrabOwner = lastSyntheticGrabOwner;
440: syntheticGrabDepth = 0;
441: int mask = event.getInputModifiers();
442: syntheticGrabDepth += (mask & InputEvent.BUTTON1_DOWN_MASK) != 0 ? 1
443: : 0;
444: syntheticGrabDepth += (mask & InputEvent.BUTTON2_DOWN_MASK) != 0 ? 1
445: : 0;
446: syntheticGrabDepth += (mask & InputEvent.BUTTON3_DOWN_MASK) != 0 ? 1
447: : 0;
448: }
449: }
450: }
451:
452: /**
453: * @return the component that currently owns the synthetic grab
454: */
455: Component getSyntheticGrabOwner() {
456: return syntheticGrabOwner;
457: }
458:
459: /**
460: * ends synthetic grab
461: */
462: private void resetSyntheticGrab() {
463: syntheticGrabOwner = null;
464: syntheticGrabDepth = 0;
465: }
466:
467: }
468:
469: /**
470: * Dispatches native events related to the pop-up boxes
471: * (the non-component windows such as menus and drop lists)
472: */
473: final class PopupDispatcher {
474:
475: private PopupBox activePopup;
476:
477: private PopupBox underCursor;
478:
479: private final MouseGrab grab = new MouseGrab();
480:
481: /**
482: * Handles the mouse grab for pop-up boxes
483: */
484: private final class MouseGrab {
485: private int depth;
486:
487: private PopupBox owner;
488:
489: private final Point start = new Point();
490:
491: /**
492: * Starts the grab when mouse is pressed
493: * @param src - the pop-up box where mouse event has occured
494: * @param where - the mouse pointer location
495: * @return - the grab owner
496: */
497: PopupBox mousePressed(PopupBox src, Point where) {
498: if (depth == 0) {
499: owner = src;
500: start.setLocation(where);
501: }
502: depth++;
503: return owner;
504: }
505:
506: /**
507: * Ends the grab when all mousebuttons are released
508: * @param src - the pop-up box where mouse event has occured
509: * @param where - the mouse pointer location
510: * @return - the grab owner, or src parameter if the grab has ended
511: */
512: PopupBox mouseReleased(PopupBox src, Point where) {
513: PopupBox ret = (owner != null) ? owner : src;
514: if (depth == 0) {
515: return ret;
516: }
517: depth--;
518: if (depth == 0) {
519: PopupBox tgt = owner;
520: owner = null;
521: if (tgt != null && src == null) {
522: Point a = new Point(start);
523: Point b = new Point(where);
524: Point pos = tgt.getScreenLocation();
525: a.translate(-pos.x, -pos.y);
526: b.translate(-pos.x, -pos.y);
527: if (tgt.closeOnUngrab(a, b)) {
528: return null;
529: }
530: }
531: }
532: return ret;
533: }
534:
535: /**
536: * Set the grab owner to null
537: */
538: void reset() {
539: depth = 0;
540: owner = null;
541: start.setLocation(0, 0);
542: }
543:
544: /**
545: * @return - the pop-up box currently owning the grab
546: */
547: public PopupBox getOwner() {
548: return owner;
549: }
550: }
551:
552: /**
553: * Call the mouse event handler of the pop-up box
554: * @param src - the pop-up box where the mouse event occured
555: * @param eventId - the event ID, one of MouseEvent.MOUSE_* constants
556: * @param where - the mouse pointer location
557: * @param event - native event
558: */
559: private void mouseEvent(PopupBox src, int eventId, Point where,
560: NativeEvent event) {
561: Point pos = src.getScreenLocation();
562: pos.setLocation(where.x - pos.x, where.y - pos.y);
563:
564: src.onMouseEvent(eventId, pos, event.getMouseButton(),
565: event.getTime(), event.getInputModifiers(), event
566: .getWheelRotation());
567: }
568:
569: /**
570: * Handle the native event targeted by a pop-up box. This could be
571: * paint event, mouse or keyboard event.
572: * @param event - the native event
573: * @return - false if the event was handled and doesn't
574: * need the further processing; true when the further
575: * processing is needed
576: */
577: boolean onEvent(NativeEvent event) {
578: PopupBox src = toolkit.getPopupBoxById(event.getWindowId());
579: int id = event.getEventId();
580:
581: if ((id == PaintEvent.PAINT)) {
582: if (src != null) {
583: src.paint(event.getClipRects());
584: return true;
585: }
586: Component c = toolkit.getComponentById(event
587: .getWindowId());
588: if ((c != null) && (c instanceof Frame)) {
589: ((Frame) c).paintMenuBar(event.getClipRects());
590: }
591: return false;
592: }
593:
594: if ((id >= MouseEvent.MOUSE_FIRST)
595: && (id <= MouseEvent.MOUSE_LAST)) {
596: Point where = event.getScreenPos();
597:
598: if (src != underCursor) {
599: if (underCursor != null) {
600: mouseEvent(underCursor,
601: MouseEvent.MOUSE_EXITED, where, event);
602: }
603: underCursor = src;
604: if (underCursor != null) {
605: mouseEvent(underCursor,
606: MouseEvent.MOUSE_ENTERED, where, event);
607: underCursor.setDefaultCursor();
608: }
609: }
610: if (id == MouseEvent.MOUSE_EXITED) {
611: underCursor = null;
612: }
613:
614: if ((activePopup == null)
615: && (src == null || !src.isMenuBar())) {
616: return false;
617: }
618:
619: if (id == MouseEvent.MOUSE_PRESSED) {
620: src = grab.mousePressed(src, where);
621: } else if (id == MouseEvent.MOUSE_RELEASED) {
622: src = grab.mouseReleased(src, where);
623: } else if (src == null) {
624: src = grab.getOwner();
625: }
626:
627: PopupBox wasActive = activePopup;
628:
629: if (src != null) {
630: mouseEvent(src, id, where, event);
631: return src.isMenu() || src.contains(where);
632: }
633:
634: if (wasActive != null && activePopup == null) {
635: return wasActive.isMenu();
636: }
637:
638: if ((id == MouseEvent.MOUSE_PRESSED)
639: || (id == MouseEvent.MOUSE_RELEASED)) {
640: boolean isMenu = activePopup.isMenu();
641: deactivateAll();
642: return !isMenu;
643: }
644: return true;
645: }
646:
647: if (activePopup == null) {
648: return false;
649: }
650:
651: if ((id >= KeyEvent.KEY_FIRST) && (id <= KeyEvent.KEY_LAST)) {
652: boolean isMenu = activePopup.isMenu();
653: activePopup.dispatchKeyEvent(id, event.getVKey(), event
654: .getTime(), event.getInputModifiers());
655:
656: return isMenu;
657: }
658:
659: return false;
660: }
661:
662: /**
663: * Remember the pop-up as active and grab the mouse on it
664: * @param popup - the pop-up box to activate
665: */
666: void activate(final PopupBox popup) {
667: if (activePopup == null) {
668:
669: activePopup = popup;
670: mouseGrabManager.startGrab(popup.getOwner(),
671: new Runnable() {
672: public void run() {
673: deactivate(popup);
674: }
675: });
676: }
677: }
678:
679: /**
680: * Deactivate the currently active pop-up box
681: */
682: void deactivateAll() {
683: deactivate(activePopup);
684: }
685:
686: /**
687: * Deactivate the pop-up box, end the mouse grab
688: */
689: void deactivate(PopupBox popup) {
690: grab.reset();
691:
692: if (activePopup != null && activePopup == popup) {
693: activePopup = null;
694: mouseGrabManager.endGrab();
695: popup.hide();
696: underCursor = null;
697: }
698: }
699:
700: /**
701: * Check that the pop-up box is currently active
702: * @param popup - the pop-up box to check
703: * @return - true if active
704: */
705: boolean isActive(PopupBox popup) {
706: return (popup == activePopup) && (popup != null);
707: }
708: }
709:
710: }
|