KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > awt > DefaultKeyboardFocusManager


1 /*
2  * @(#)DefaultKeyboardFocusManager.java 1.36 07/03/09
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package java.awt;
8
9 import java.awt.event.FocusEvent JavaDoc;
10 import java.awt.event.KeyEvent JavaDoc;
11 import java.awt.event.WindowEvent JavaDoc;
12 import java.awt.peer.ComponentPeer;
13 import java.awt.peer.LightweightPeer;
14 import java.beans.PropertyChangeListener JavaDoc;
15 import java.util.LinkedList JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.ListIterator JavaDoc;
18 import java.util.Set JavaDoc;
19
20 import java.util.logging.*;
21
22 import sun.awt.AppContext;
23 import sun.awt.SunToolkit;
24
25 /**
26  * The default KeyboardFocusManager for AWT applications. Focus traversal is
27  * done in response to a Component's focus traversal keys, and using a
28  * Container's FocusTraversalPolicy.
29  * <p>
30  * Please see
31  * <a HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/focus.html">
32  * How to Use the Focus Subsystem</a>,
33  * a section in <em>The Java Tutorial</em>, and the
34  * <a HREF="../../java/awt/doc-files/FocusSpec.html">Focus Specification</a>
35  * for more information.
36  *
37  * @author David Mendenhall
38  * @version 1.36, 03/09/07
39  *
40  * @see FocusTraversalPolicy
41  * @see Component#setFocusTraversalKeys
42  * @see Component#getFocusTraversalKeys
43  * @since 1.4
44  */

45 public class DefaultKeyboardFocusManager extends KeyboardFocusManager JavaDoc {
46     private static final Logger focusLog = Logger.getLogger("java.awt.focus.DefaultKeyboardFocusManager");
47
48     private Window JavaDoc realOppositeWindow;
49     private Component JavaDoc realOppositeComponent;
50     private int inSendMessage;
51     private LinkedList JavaDoc enqueuedKeyEvents = new LinkedList JavaDoc(),
52         typeAheadMarkers = new LinkedList JavaDoc();
53     private boolean consumeNextKeyTyped;
54
55     private static class TypeAheadMarker {
56         long after;
57         Component JavaDoc untilFocused;
58
59         TypeAheadMarker(long after, Component JavaDoc untilFocused) {
60             this.after = after;
61             this.untilFocused = untilFocused;
62         }
63         /**
64          * Returns string representation of the marker
65          */

66         public String JavaDoc toString() {
67             return ">>> Marker after " + after + " on " + untilFocused;
68         }
69     }
70
71     private Window JavaDoc getOwningFrameDialog(Window JavaDoc window) {
72         while (window != null && !(window instanceof Frame JavaDoc ||
73                                    window instanceof Dialog JavaDoc)) {
74             window = (Window JavaDoc)window.getParent();
75         }
76         return window;
77     }
78
79     /*
80      * This series of restoreFocus methods is used for recovering from a
81      * rejected focus or activation change. Rejections typically occur when
82      * the user attempts to focus a non-focusable Component or Window.
83      */

84     private void restoreFocus(FocusEvent JavaDoc fe, Window JavaDoc newFocusedWindow) {
85         Component JavaDoc realOppositeComponent = this.realOppositeComponent;
86         Component JavaDoc vetoedComponent = fe.getComponent();
87         if (newFocusedWindow != null && restoreFocus(newFocusedWindow,
88                                                      vetoedComponent, false))
89         {
90         } else if (realOppositeComponent != null &&
91                    restoreFocus(realOppositeComponent, false)) {
92         } else if (fe.getOppositeComponent() != null &&
93                    restoreFocus(fe.getOppositeComponent(), false)) {
94         } else {
95             clearGlobalFocusOwner();
96         }
97     }
98     private void restoreFocus(WindowEvent JavaDoc we) {
99         Window JavaDoc realOppositeWindow = this.realOppositeWindow;
100         if (realOppositeWindow != null && restoreFocus(realOppositeWindow,
101                                                        null, false)) {
102         } else if (we.getOppositeWindow() != null &&
103                    restoreFocus(we.getOppositeWindow(), null, false)) {
104         } else {
105             clearGlobalFocusOwner();
106         }
107     }
108     private boolean restoreFocus(Window JavaDoc aWindow, Component JavaDoc vetoedComponent,
109                                  boolean clearOnFailure) {
110         Component JavaDoc toFocus =
111             KeyboardFocusManager.getMostRecentFocusOwner(aWindow);
112         if (toFocus != null && toFocus != vetoedComponent && restoreFocus(toFocus, false)) {
113             return true;
114         } else if (clearOnFailure) {
115             clearGlobalFocusOwner();
116             return true;
117         } else {
118             return false;
119         }
120     }
121     private boolean restoreFocus(Component JavaDoc toFocus, boolean clearOnFailure) {
122         if (toFocus.isShowing() && toFocus.isFocusable() &&
123             toFocus.requestFocus(false)) {
124             return true;
125         } else if (toFocus.nextFocusHelper()) {
126             return true;
127         } else if (clearOnFailure) {
128             clearGlobalFocusOwner();
129             return true;
130         } else {
131             return false;
132         }
133     }
134
135     /**
136      * A special type of SentEvent which updates a counter in the target
137      * KeyboardFocusManager if it is an instance of
138      * DefaultKeyboardFocusManager.
139      */

140     private static class DefaultKeyboardFocusManagerSentEvent
141         extends SentEvent JavaDoc
142     {
143         public DefaultKeyboardFocusManagerSentEvent(AWTEvent JavaDoc nested,
144                                                     AppContext toNotify) {
145             super(nested, toNotify);
146         }
147         public final void dispatch() {
148             KeyboardFocusManager JavaDoc manager =
149                 KeyboardFocusManager.getCurrentKeyboardFocusManager();
150             DefaultKeyboardFocusManager JavaDoc defaultManager =
151                 (manager instanceof DefaultKeyboardFocusManager JavaDoc)
152                 ? (DefaultKeyboardFocusManager JavaDoc)manager
153                 : null;
154
155             if (defaultManager != null) {
156                 synchronized (defaultManager) {
157                     defaultManager.inSendMessage++;
158                 }
159             }
160
161             super.dispatch();
162
163             if (defaultManager != null) {
164                 synchronized (defaultManager) {
165                     defaultManager.inSendMessage--;
166                 }
167             }
168         }
169     }
170
171     /**
172      * Sends a synthetic AWTEvent to a Component. If the Component is in
173      * the current AppContext, then the event is immediately dispatched.
174      * If the Component is in a different AppContext, then the event is
175      * posted to the other AppContext's EventQueue, and this method blocks
176      * until the event is handled or target AppContext is disposed.
177      * Returns true if successfuly dispatched event, false if failed
178      * to dispatch.
179      */

180     static boolean sendMessage(Component JavaDoc target, AWTEvent JavaDoc e) {
181         e.isPosted = true;
182         AppContext myAppContext = AppContext.getAppContext();
183         final AppContext targetAppContext = target.appContext;
184         final SentEvent JavaDoc se =
185             new DefaultKeyboardFocusManagerSentEvent(e, myAppContext);
186
187         if (myAppContext == targetAppContext) {
188             se.dispatch();
189         } else {
190             if (targetAppContext.isDisposed()) {
191                 return false;
192             }
193             SunToolkit.postEvent(targetAppContext, se);
194             if (EventQueue.isDispatchThread()) {
195                 EventDispatchThread JavaDoc edt = (EventDispatchThread JavaDoc)
196                     Thread.currentThread();
197                 edt.pumpEvents(SentEvent.ID, new Conditional JavaDoc() {
198                         public boolean evaluate() {
199                             return !se.dispatched && !targetAppContext.isDisposed();
200                         }
201                     });
202             } else {
203                 synchronized (se) {
204                     while (!se.dispatched && !targetAppContext.isDisposed()) {
205                         try {
206                             se.wait(1000);
207                         } catch (InterruptedException JavaDoc ie) {
208                             break;
209                         }
210                     }
211                 }
212             }
213         }
214         return se.dispatched;
215     }
216
217     /**
218      * This method is called by the AWT event dispatcher requesting that the
219      * current KeyboardFocusManager dispatch the specified event on its behalf.
220      * DefaultKeyboardFocusManagers dispatch all FocusEvents, all WindowEvents
221      * related to focus, and all KeyEvents. These events are dispatched based
222      * on the KeyboardFocusManager's notion of the focus owner and the focused
223      * and active Windows, sometimes overriding the source of the specified
224      * AWTEvent. If this method returns <code>false</code>, then the AWT event
225      * dispatcher will attempt to dispatch the event itself.
226      *
227      * @param e the AWTEvent to be dispatched
228      * @return <code>true</code> if this method dispatched the event;
229      * <code>false</code> otherwise
230      */

231     public boolean dispatchEvent(AWTEvent JavaDoc e) {
232         if (focusLog.isLoggable(Level.FINE) && (e instanceof WindowEvent JavaDoc || e instanceof FocusEvent JavaDoc)) focusLog.fine("" + e);
233         switch (e.getID()) {
234             case WindowEvent.WINDOW_GAINED_FOCUS: {
235                 WindowEvent JavaDoc we = (WindowEvent JavaDoc)e;
236                 Window JavaDoc oldFocusedWindow = getGlobalFocusedWindow();
237                 Window JavaDoc newFocusedWindow = we.getWindow();
238                 if (newFocusedWindow == oldFocusedWindow) {
239                     break;
240                 }
241
242                 // If there exists a current focused window, then notify it
243
// that it has lost focus.
244
if (oldFocusedWindow != null) {
245                     boolean isEventDispatched =
246                         sendMessage(oldFocusedWindow,
247                                 new WindowEvent JavaDoc(oldFocusedWindow,
248                                                 WindowEvent.WINDOW_LOST_FOCUS,
249                                                 newFocusedWindow));
250                     // Failed to dispatch, clear by ourselfves
251
if (!isEventDispatched) {
252                         setGlobalFocusOwner(null);
253                         setGlobalFocusedWindow(null);
254                     }
255                 }
256
257                 // Because the native libraries do not post WINDOW_ACTIVATED
258
// events, we need to synthesize one if the active Window
259
// changed.
260
Window JavaDoc newActiveWindow =
261                     getOwningFrameDialog(newFocusedWindow);
262                 Window JavaDoc currentActiveWindow = getGlobalActiveWindow();
263                 if (newActiveWindow != currentActiveWindow) {
264                     sendMessage(newActiveWindow,
265                                 new WindowEvent JavaDoc(newActiveWindow,
266                                                 WindowEvent.WINDOW_ACTIVATED,
267                                                 currentActiveWindow));
268                     if (newActiveWindow != getGlobalActiveWindow()) {
269                         // Activation change was rejected. Unlikely, but
270
// possible.
271
restoreFocus(we);
272                         break;
273                     }
274                 }
275
276                 setGlobalFocusedWindow(newFocusedWindow);
277
278                 if (newFocusedWindow != getGlobalFocusedWindow()) {
279                     // Focus change was rejected. Will happen if
280
// newFocusedWindow is not a focusable Window.
281
restoreFocus(we);
282                     break;
283                 }
284
285                 setNativeFocusedWindow(newFocusedWindow);
286                 // Restore focus to the Component which last held it. We do
287
// this here so that client code can override our choice in
288
// a WINDOW_GAINED_FOCUS handler.
289
//
290
// Make sure that the focus change request doesn't change the
291
// focused Window in case we are no longer the focused Window
292
// when the request is handled.
293
if (inSendMessage == 0) {
294                     // Identify which Component should initially gain focus
295
// in the Window.
296
//
297
// * If we're in SendMessage, then this is a synthetic
298
// WINDOW_GAINED_FOCUS message which was generated by a
299
// the FOCUS_GAINED handler. Allow the Component to
300
// which the FOCUS_GAINED message was targeted to
301
// receive the focus.
302
// * Otherwise, look up the correct Component here.
303
// We don't use Window.getMostRecentFocusOwner because
304
// window is focused now and 'null' will be returned
305

306
307                     // Calculating of most recent focus owner and focus
308
// request should be synchronized on KeyboardFocusManager.class
309
// to prevent from thread race when user will request
310
// focus between calculation and our request.
311
// But if focus transfer is synchronous, this synchronization
312
// may cause deadlock, thus we don't synchronize this block.
313
Component JavaDoc toFocus = KeyboardFocusManager.
314                         getMostRecentFocusOwner(newFocusedWindow);
315                     if ((toFocus == null) &&
316                         newFocusedWindow.isFocusableWindow())
317                     {
318                         toFocus = newFocusedWindow.getFocusTraversalPolicy().
319                             getInitialComponent(newFocusedWindow);
320                     }
321                     Component JavaDoc tempLost = null;
322                     synchronized(KeyboardFocusManager JavaDoc.class) {
323                         tempLost = newFocusedWindow.setTemporaryLostComponent(null);
324                     }
325
326                     // The component which last has the focus when this window was focused
327
// should receive focus first
328
if (focusLog.isLoggable(Level.FINER)) {
329                         focusLog.log(Level.FINER, "tempLost {0}, toFocus {1}",
330                                      new Object JavaDoc[]{tempLost, toFocus});
331                     }
332                     setInActivation(true);
333                     if (tempLost != null) {
334                         tempLost.requestFocusInWindow();
335                     }
336
337                     if (toFocus != null && toFocus != tempLost) {
338                         // If there is a component which requested focus when this window
339
// was inactive it expects to receive focus after activation.
340
toFocus.requestFocusInWindow();
341                     }
342                     setInActivation(false);
343                 }
344
345                 Window JavaDoc realOppositeWindow = this.realOppositeWindow;
346                 if (realOppositeWindow != we.getOppositeWindow()) {
347                     we = new WindowEvent JavaDoc(newFocusedWindow,
348                                          WindowEvent.WINDOW_GAINED_FOCUS,
349                                          realOppositeWindow);
350                 }
351                 return typeAheadAssertions(newFocusedWindow, we);
352             }
353
354             case WindowEvent.WINDOW_ACTIVATED: {
355                 WindowEvent JavaDoc we = (WindowEvent JavaDoc)e;
356                 Window JavaDoc oldActiveWindow = getGlobalActiveWindow();
357                 Window JavaDoc newActiveWindow = we.getWindow();
358                 if (oldActiveWindow == newActiveWindow) {
359                     break;
360                 }
361
362                 // If there exists a current active window, then notify it that
363
// it has lost activation.
364
if (oldActiveWindow != null) {
365                     boolean isEventDispatched =
366                         sendMessage(oldActiveWindow,
367                                 new WindowEvent JavaDoc(oldActiveWindow,
368                                                 WindowEvent.WINDOW_DEACTIVATED,
369                                                 newActiveWindow));
370                     // Failed to dispatch, clear by ourselfves
371
if (!isEventDispatched) {
372                         setGlobalActiveWindow(null);
373                     }
374                     if (getGlobalActiveWindow() != null) {
375                         // Activation change was rejected. Unlikely, but
376
// possible.
377
break;
378                     }
379                 }
380
381                 setGlobalActiveWindow(newActiveWindow);
382
383                 if (newActiveWindow != getGlobalActiveWindow()) {
384                     // Activation change was rejected. Unlikely, but
385
// possible.
386
break;
387                 }
388
389                 return typeAheadAssertions(newActiveWindow, we);
390             }
391
392             case FocusEvent.FOCUS_GAINED: {
393                 FocusEvent JavaDoc fe = (FocusEvent JavaDoc)e;
394                 Component JavaDoc oldFocusOwner = getGlobalFocusOwner();
395                 Component JavaDoc newFocusOwner = fe.getComponent();
396                 if (oldFocusOwner == newFocusOwner) {
397                     if (focusLog.isLoggable(Level.FINE)) focusLog.log(Level.FINE, "Skipping {0} because focus owner is the same",
398                                                                         new Object JavaDoc[] {e});
399                     // We can't just drop the event - there could be
400
// type-ahead markers associated with it.
401
dequeueKeyEvents(-1, newFocusOwner);
402                     break;
403                 }
404
405                 // If there exists a current focus owner, then notify it that
406
// it has lost focus.
407
if (oldFocusOwner != null) {
408                     boolean isEventDispatched =
409                         sendMessage(oldFocusOwner,
410                                     new FocusEvent JavaDoc(oldFocusOwner,
411                                                    FocusEvent.FOCUS_LOST,
412                                                    fe.isTemporary(),
413                                                    newFocusOwner));
414                     // Failed to dispatch, clear by ourselfves
415
if (!isEventDispatched) {
416                         setGlobalFocusOwner(null);
417                         if (!fe.isTemporary()) {
418                             setGlobalPermanentFocusOwner(null);
419                         }
420                     }
421                 }
422
423                 // Because the native windowing system has a different notion
424
// of the current focus and activation states, it is possible
425
// that a Component outside of the focused Window receives a
426
// FOCUS_GAINED event. We synthesize a WINDOW_GAINED_FOCUS
427
// event in that case.
428
Component JavaDoc newFocusedWindow = newFocusOwner;
429                 while (newFocusedWindow != null &&
430                        !(newFocusedWindow instanceof Window JavaDoc)) {
431                     newFocusedWindow = newFocusedWindow.parent;
432                 }
433                 Window JavaDoc currentFocusedWindow = getGlobalFocusedWindow();
434                 if (newFocusedWindow != null &&
435                     newFocusedWindow != currentFocusedWindow)
436                 {
437                     sendMessage(newFocusedWindow,
438                                 new WindowEvent JavaDoc((Window JavaDoc)newFocusedWindow,
439                                         WindowEvent.WINDOW_GAINED_FOCUS,
440                                                 currentFocusedWindow));
441                     if (newFocusedWindow != getGlobalFocusedWindow()) {
442                         // Focus change was rejected. Will happen if
443
// newFocusedWindow is not a focusable Window.
444

445                         // Need to recover type-ahead, but don't bother
446
// restoring focus. That was done by the
447
// WINDOW_GAINED_FOCUS handler
448
dequeueKeyEvents(-1, newFocusOwner);
449                         break;
450                     }
451                 }
452
453                 setGlobalFocusOwner(newFocusOwner);
454
455                 if (newFocusOwner != getGlobalFocusOwner()) {
456                     // Focus change was rejected. Will happen if
457
// newFocusOwner is not focus traversable.
458
dequeueKeyEvents(-1, newFocusOwner);
459                     if (! disableRestoreFocus ){
460                        restoreFocus(fe, (Window JavaDoc)newFocusedWindow);
461                     }
462                     break;
463                 }
464
465                 if (!fe.isTemporary()) {
466                     setGlobalPermanentFocusOwner(newFocusOwner);
467
468                     if (newFocusOwner != getGlobalPermanentFocusOwner()) {
469                         // Focus change was rejected. Unlikely, but possible.
470
dequeueKeyEvents(-1, newFocusOwner);
471                         if (! disableRestoreFocus ){
472                             restoreFocus(fe, (Window JavaDoc)newFocusedWindow);
473                         }
474                         break;
475                     }
476                 }
477
478                 setNativeFocusOwner(getHeavyweight(newFocusOwner));
479
480                 Component JavaDoc realOppositeComponent = this.realOppositeComponent;
481                 if (realOppositeComponent != null &&
482                     realOppositeComponent != fe.getOppositeComponent()) {
483                     fe = new FocusEvent JavaDoc(newFocusOwner,
484                                         FocusEvent.FOCUS_GAINED,
485                                         fe.isTemporary(),
486                                         realOppositeComponent);
487                     ((AWTEvent JavaDoc) fe).isPosted = true;
488                 }
489                 return typeAheadAssertions(newFocusOwner, fe);
490             }
491
492             case FocusEvent.FOCUS_LOST: {
493                 FocusEvent JavaDoc fe = (FocusEvent JavaDoc)e;
494                 Component JavaDoc currentFocusOwner = getGlobalFocusOwner();
495                 if (currentFocusOwner == null) {
496                     if (focusLog.isLoggable(Level.FINE)) focusLog.log(Level.FINE, "Skipping {0} because focus owner is null",
497                                                                         new Object JavaDoc[] {e});
498                     break;
499                 }
500                 // Ignore cases where a Component loses focus to itself.
501
// If we make a mistake because of retargeting, then the
502
// FOCUS_GAINED handler will correct it.
503
if (currentFocusOwner == fe.getOppositeComponent()) {
504                     if (focusLog.isLoggable(Level.FINE)) focusLog.log(Level.FINE, "Skipping {0} because current focus owner is equal to opposite",
505                                                                       new Object JavaDoc[] {e});
506                     break;
507                 }
508
509                 setGlobalFocusOwner(null);
510
511                 if (getGlobalFocusOwner() != null) {
512                     // Focus change was rejected. Unlikely, but possible.
513
restoreFocus(currentFocusOwner, true);
514                     break;
515                 }
516
517                 if (!fe.isTemporary()) {
518                     setGlobalPermanentFocusOwner(null);
519
520                     if (getGlobalPermanentFocusOwner() != null) {
521                         // Focus change was rejected. Unlikely, but possible.
522
restoreFocus(currentFocusOwner, true);
523                         break;
524                     }
525                 } else {
526                     Window JavaDoc owningWindow = currentFocusOwner.getContainingWindow();
527                     if (owningWindow != null) {
528                         owningWindow.setTemporaryLostComponent(currentFocusOwner);
529                     }
530                 }
531
532                 setNativeFocusOwner(null);
533
534                 fe.setSource(currentFocusOwner);
535
536                 realOppositeComponent = (fe.getOppositeComponent() != null)
537                     ? currentFocusOwner : null;
538
539                 return typeAheadAssertions(currentFocusOwner, fe);
540             }
541
542             case WindowEvent.WINDOW_DEACTIVATED: {
543                 WindowEvent JavaDoc we = (WindowEvent JavaDoc)e;
544                 Window JavaDoc currentActiveWindow = getGlobalActiveWindow();
545                 if (currentActiveWindow == null) {
546                     break;
547                 }
548
549                 if (currentActiveWindow != e.getSource()) {
550                     // The event is lost in time.
551
// Allow listeners to precess the event but do not
552
// change any global states
553
break;
554                 }
555
556                 setGlobalActiveWindow(null);
557                 if (getGlobalActiveWindow() != null) {
558                     // Activation change was rejected. Unlikely, but possible.
559
break;
560                 }
561
562                 we.setSource(currentActiveWindow);
563                 return typeAheadAssertions(currentActiveWindow, we);
564             }
565
566             case WindowEvent.WINDOW_LOST_FOCUS: {
567                 WindowEvent JavaDoc we = (WindowEvent JavaDoc)e;
568                 Window JavaDoc currentFocusedWindow = getGlobalFocusedWindow();
569                 Window JavaDoc losingFocusWindow = we.getWindow();
570                 Window JavaDoc activeWindow = getGlobalActiveWindow();
571                 Window JavaDoc oppositeWindow = we.getOppositeWindow();
572                 if (focusLog.isLoggable(Level.FINE)) focusLog.log(Level.FINE, "Active {0}, Current focused {1}, losing focus {2} opposite {3}",
573                                                                   new Object JavaDoc[] {activeWindow, currentFocusedWindow,
574                                                                                 losingFocusWindow, oppositeWindow});
575                 if (currentFocusedWindow == null) {
576                     break;
577                 }
578
579                 // Special case -- if the native windowing system posts an
580
// event claiming that the active Window has lost focus to the
581
// focused Window, then discard the event. This is an artifact
582
// of the native windowing system not knowing which Window is
583
// really focused.
584
if (inSendMessage == 0 && losingFocusWindow == activeWindow &&
585                     oppositeWindow == currentFocusedWindow)
586                 {
587                     break;
588                 }
589
590                 Component JavaDoc currentFocusOwner = getGlobalFocusOwner();
591                 if (currentFocusOwner != null) {
592                     // The focus owner should always receive a FOCUS_LOST event
593
// before the Window is defocused.
594
Component JavaDoc oppositeComp = null;
595                     if (oppositeWindow != null) {
596                         oppositeComp = oppositeWindow.getTemporaryLostComponent();
597                         if (oppositeComp == null) {
598                             oppositeComp = oppositeWindow.getMostRecentFocusOwner();
599                         }
600                     }
601                     if (oppositeComp == null) {
602                         oppositeComp = oppositeWindow;
603                     }
604                     sendMessage(currentFocusOwner,
605                                 new FocusEvent JavaDoc(currentFocusOwner,
606                                                FocusEvent.FOCUS_LOST,
607                                                true,
608                                                oppositeComp));
609                 }
610
611                 setGlobalFocusedWindow(null);
612                 if (getGlobalFocusedWindow() != null) {
613                     // Focus change was rejected. Unlikely, but possible.
614
restoreFocus(currentFocusedWindow, null, true);
615                     break;
616                 }
617                 setNativeFocusedWindow(null);
618
619                 we.setSource(currentFocusedWindow);
620                 realOppositeWindow = (oppositeWindow != null)
621                     ? currentFocusedWindow
622                     : null;
623                 typeAheadAssertions(currentFocusedWindow, we);
624
625                 if (oppositeWindow == null) {
626                     // Then we need to deactive the active Window as well.
627
// No need to synthesize in other cases, because
628
// WINDOW_ACTIVATED will handle it if necessary.
629
sendMessage(activeWindow,
630                                 new WindowEvent JavaDoc(activeWindow,
631                                                 WindowEvent.WINDOW_DEACTIVATED,
632                                                 null));
633                     if (getGlobalActiveWindow() != null) {
634                         // Activation change was rejected. Unlikely,
635
// but possible.
636
restoreFocus(currentFocusedWindow, null, true);
637                     }
638                 }
639                 break;
640             }
641
642             case KeyEvent.KEY_TYPED:
643             case KeyEvent.KEY_PRESSED:
644             case KeyEvent.KEY_RELEASED:
645                 return typeAheadAssertions(null, e);
646
647             default:
648                 return false;
649         }
650
651         return true;
652     }
653
654     /**
655      * Called by <code>dispatchEvent</code> if no other
656      * KeyEventDispatcher in the dispatcher chain dispatched the KeyEvent, or
657      * if no other KeyEventDispatchers are registered. If the event has not
658      * been consumed, its target is enabled, and the focus owner is not null,
659      * this method dispatches the event to its target. This method will also
660      * subsequently dispatch the event to all registered
661      * KeyEventPostProcessors. After all this operations are finished,
662      * the event is passed to peers for processing.
663      * <p>
664      * In all cases, this method returns <code>true</code>, since
665      * DefaultKeyboardFocusManager is designed so that neither
666      * <code>dispatchEvent</code>, nor the AWT event dispatcher, should take
667      * further action on the event in any situation.
668      *
669      * @param e the KeyEvent to be dispatched
670      * @return <code>true</code>
671      * @see Component#dispatchEvent
672      */

673     public boolean dispatchKeyEvent(KeyEvent JavaDoc e) {
674         Component JavaDoc focusOwner = (((AWTEvent JavaDoc)e).isPosted) ? getFocusOwner() : e.getComponent();
675
676         if (focusOwner != null && focusOwner.isShowing() &&
677             focusOwner.isFocusable() && focusOwner.isEnabled()) {
678             if (!e.isConsumed()) {
679                 Component JavaDoc comp = e.getComponent();
680                 if (comp != null && comp.isEnabled()) {
681                     redispatchEvent(comp, e);
682                 }
683             }
684         }
685         boolean stopPostProcessing = false;
686         java.util.List JavaDoc processors = getKeyEventPostProcessors();
687         if (processors != null) {
688             for (java.util.Iterator JavaDoc iter = processors.iterator();
689                  !stopPostProcessing && iter.hasNext(); )
690             {
691                 stopPostProcessing = (((KeyEventPostProcessor JavaDoc)(iter.next())).
692                             postProcessKeyEvent(e));
693             }
694         }
695         if (!stopPostProcessing) {
696             postProcessKeyEvent(e);
697         }
698
699         // Allow the peer to process KeyEvent
700
Component JavaDoc source = e.getComponent();
701         ComponentPeer peer = source.getPeer();
702
703         if (peer == null || peer instanceof LightweightPeer) {
704             // if focus owner is lightweight then its native container
705
// processes event
706
Container JavaDoc target = source.getNativeContainer();
707             if (target != null) {
708                 peer = target.getPeer();
709             }
710         }
711         if (peer != null) {
712             peer.handleEvent(e);
713         }
714
715         return true;
716     }
717
718     /**
719      * This method will be called by <code>dispatchKeyEvent</code>. It will
720      * handle any unconsumed KeyEvents that map to an AWT
721      * <code>MenuShortcut</code> by consuming the event and activating the
722      * shortcut.
723      *
724      * @param e the KeyEvent to post-process
725      * @return <code>true</code>
726      * @see #dispatchKeyEvent
727      * @see MenuShortcut
728      */

729     public boolean postProcessKeyEvent(KeyEvent JavaDoc e) {
730         if (!e.isConsumed()) {
731             Component JavaDoc target = e.getComponent();
732             Container JavaDoc p = (Container JavaDoc)
733                 (target instanceof Container JavaDoc ? target : target.getParent());
734             if (p != null) {
735                 p.postProcessKeyEvent(e);
736             }
737         }
738         return true;
739     }
740
741     private void pumpApprovedKeyEvents() {
742         KeyEvent JavaDoc ke;
743         do {
744             ke = null;
745             synchronized (this) {
746                 if (enqueuedKeyEvents.size() != 0) {
747                     ke = (KeyEvent JavaDoc)enqueuedKeyEvents.getFirst();
748                     if (typeAheadMarkers.size() != 0) {
749                         TypeAheadMarker marker = (TypeAheadMarker)
750                             typeAheadMarkers.getFirst();
751                         if (ke.getWhen() > marker.after) {
752                             ke = null;
753                         }
754                     }
755                     if (ke != null) {
756                         focusLog.log(Level.FINER, "Pumping approved event {0}", new Object JavaDoc[] {ke});
757                         enqueuedKeyEvents.removeFirst();
758                     }
759                 }
760             }
761             if (ke != null) {
762                 preDispatchKeyEvent(ke);
763             }
764         } while (ke != null);
765     }
766
767     /**
768      * Dumps the list of type-ahead queue markers to stderr
769      */

770     void dumpMarkers() {
771         if (focusLog.isLoggable(Level.FINEST)) {
772             focusLog.log(Level.FINEST, ">>> Markers dump, time: {0}", System.currentTimeMillis());
773             synchronized (this) {
774                 if (typeAheadMarkers.size() != 0) {
775                     Iterator JavaDoc iter = typeAheadMarkers.iterator();
776                     while (iter.hasNext()) {
777                         TypeAheadMarker marker = (TypeAheadMarker)iter.next();
778                         focusLog.log(Level.FINEST, " {0}", marker);
779                     }
780                 }
781             }
782         }
783     }
784
785     private boolean typeAheadAssertions(Component JavaDoc target, AWTEvent JavaDoc e) {
786
787         // Clear any pending events here as well as in the FOCUS_GAINED
788
// handler. We need this call here in case a marker was removed in
789
// response to a call to dequeueKeyEvents.
790
pumpApprovedKeyEvents();
791
792         switch (e.getID()) {
793             case KeyEvent.KEY_TYPED:
794             case KeyEvent.KEY_PRESSED:
795             case KeyEvent.KEY_RELEASED: {
796                 KeyEvent JavaDoc ke = (KeyEvent JavaDoc)e;
797                 synchronized (this) {
798                     if (e.isPosted && typeAheadMarkers.size() != 0) {
799                         TypeAheadMarker marker = (TypeAheadMarker)
800                             typeAheadMarkers.getFirst();
801                         if (ke.getWhen() > marker.after) {
802                             focusLog.log(Level.FINER, "Storing event {0} because of marker {1}", new Object JavaDoc[] {ke, marker});
803                             enqueuedKeyEvents.addLast(ke);
804                             return true;
805                         }
806                     }
807                 }
808
809                 // KeyEvent was posted before focus change request
810
return preDispatchKeyEvent(ke);
811             }
812
813             case FocusEvent.FOCUS_GAINED:
814                 focusLog.log(Level.FINEST, "Markers before FOCUS_GAINED on {0}", new Object JavaDoc[] {target});
815                 dumpMarkers();
816                 // Search the marker list for the first marker tied to
817
// the Component which just gained focus. Then remove
818
// that marker, any markers which immediately follow
819
// and are tied to the same component, and all markers
820
// that preceed it. This handles the case where
821
// multiple focus requests were made for the same
822
// Component in a row and when we lost some of the
823
// earlier requests. Since FOCUS_GAINED events will
824
// not be generated for these additional requests, we
825
// need to clear those markers too.
826
synchronized (this) {
827                     boolean found = false;
828                     if (hasMarker(target)) {
829                         for (Iterator JavaDoc iter = typeAheadMarkers.iterator();
830                              iter.hasNext(); )
831                         {
832                             if (((TypeAheadMarker)iter.next()).untilFocused ==
833                                 target)
834                             {
835                                 found = true;
836                             } else if (found) {
837                                 break;
838                             }
839                             iter.remove();
840                         }
841                     } else {
842                         // Exception condition - event without marker
843
focusLog.log(Level.FINER, "Event without marker {0}", e);
844                     }
845                 }
846                 focusLog.log(Level.FINEST, "Markers after FOCUS_GAINED");
847                 dumpMarkers();
848
849                 redispatchEvent(target, e);
850
851                 // Now, dispatch any pending KeyEvents which have been
852
// released because of the FOCUS_GAINED event so that we don't
853
// have to wait for another event to be posted to the queue.
854
pumpApprovedKeyEvents();
855                 return true;
856
857             default:
858                 redispatchEvent(target, e);
859                 return true;
860         }
861     }
862
863     /**
864      * Returns true if there are some marker associated with component <code>comp</code>
865      * in a markers' queue
866      * @since 1.5
867      */

868     private boolean hasMarker(Component JavaDoc comp) {
869         for (Iterator JavaDoc iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
870             if (((TypeAheadMarker)iter.next()).untilFocused == comp) {
871                 return true;
872             }
873         }
874         return false;
875     }
876
877     /**
878      * Clears markers queue
879      * @since 1.5
880      */

881     void clearMarkers() {
882         synchronized(this) {
883             typeAheadMarkers.clear();
884         }
885     }
886
887     private boolean preDispatchKeyEvent(KeyEvent JavaDoc ke) {
888         if (((AWTEvent JavaDoc) ke).isPosted) {
889             Component JavaDoc focusOwner = getFocusOwner();
890             ke.setSource(((focusOwner != null) ? focusOwner : getFocusedWindow()));
891         }
892         if (ke.getSource() == null) {
893             return true;
894         }
895
896         // Explicitly set the current event and most recent timestamp here in
897
// addition to the call in Component.dispatchEventImpl. Because
898
// KeyEvents can be delivered in response to a FOCUS_GAINED event, the
899
// current timestamp may be incorrect. We need to set it here so that
900
// KeyEventDispatchers will use the correct time.
901
EventQueue.setCurrentEventAndMostRecentTime(ke);
902
903         /**
904          * Fix for 4495473.
905          * This fix allows to correctly dispatch events when native
906          * event proxying mechanism is active.
907          * If it is active we should redispatch key events after
908          * we detected its correct target.
909          */

910         if (KeyboardFocusManager.isProxyActive(ke)) {
911             Component JavaDoc source = (Component JavaDoc)ke.getSource();
912             Container JavaDoc target = source.getNativeContainer();
913             if (target != null) {
914                 ComponentPeer peer = target.getPeer();
915                 if (peer != null) {
916                     peer.handleEvent(ke);
917                     /**
918                      * Fix for 4478780 - consume event after it was dispatched by peer.
919                      */

920                     ke.consume();
921                 }
922             }
923             return true;
924         }
925
926         java.util.List JavaDoc dispatchers = getKeyEventDispatchers();
927         if (dispatchers != null) {
928             for (java.util.Iterator JavaDoc iter = dispatchers.iterator();
929                  iter.hasNext(); )
930              {
931                  if (((KeyEventDispatcher JavaDoc)(iter.next())).
932                      dispatchKeyEvent(ke))
933                  {
934                      return true;
935                  }
936              }
937         }
938         return dispatchKeyEvent(ke);
939     }
940
941     private void consumeTraversalKey(KeyEvent JavaDoc e) {
942         e.consume();
943         consumeNextKeyTyped = (e.getID() == KeyEvent.KEY_PRESSED) &&
944                               !e.isActionKey();
945     }
946
947     /*
948      * return true if event was consumed
949      */

950     private boolean consumeProcessedKeyEvent(KeyEvent JavaDoc e) {
951         if ((e.getID() == KeyEvent.KEY_TYPED) && consumeNextKeyTyped) {
952             e.consume();
953             consumeNextKeyTyped = false;
954             return true;
955         }
956         return false;
957     }
958
959     /**
960      * This method initiates a focus traversal operation if and only if the
961      * KeyEvent represents a focus traversal key for the specified
962      * focusedComponent. It is expected that focusedComponent is the current
963      * focus owner, although this need not be the case. If it is not,
964      * focus traversal will nevertheless proceed as if focusedComponent
965      * were the focus owner.
966      *
967      * @param focusedComponent the Component that is the basis for a focus
968      * traversal operation if the specified event represents a focus
969      * traversal key for the Component
970      * @param e the event that may represent a focus traversal key
971      */

972     public void processKeyEvent(Component JavaDoc focusedComponent, KeyEvent JavaDoc e) {
973         // consume processed event if needed
974
if (consumeProcessedKeyEvent(e)) {
975             return;
976         }
977
978         // KEY_TYPED events cannot be focus traversal keys
979
if (e.getID() == KeyEvent.KEY_TYPED) {
980             return;
981         }
982
983         if (focusedComponent.getFocusTraversalKeysEnabled() &&
984             !e.isConsumed())
985         {
986             AWTKeyStroke JavaDoc stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e),
987                 oppStroke = AWTKeyStroke.getAWTKeyStroke(stroke.getKeyCode(),
988                                                  stroke.getModifiers(),
989                                                  !stroke.isOnKeyRelease());
990             Set JavaDoc toTest;
991             boolean contains, containsOpp;
992
993             toTest = focusedComponent.getFocusTraversalKeys(
994                 KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
995             contains = toTest.contains(stroke);
996             containsOpp = toTest.contains(oppStroke);
997
998             if (contains || containsOpp) {
999                 consumeTraversalKey(e);
1000                if (contains) {
1001                    focusNextComponent(focusedComponent);
1002                }
1003                return;
1004            }
1005
1006            toTest = focusedComponent.getFocusTraversalKeys(
1007                KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
1008            contains = toTest.contains(stroke);
1009            containsOpp = toTest.contains(oppStroke);
1010
1011            if (contains || containsOpp) {
1012                consumeTraversalKey(e);
1013                if (contains) {
1014                    focusPreviousComponent(focusedComponent);
1015                }
1016                return;
1017            }
1018
1019            toTest = focusedComponent.getFocusTraversalKeys(
1020                KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
1021            contains = toTest.contains(stroke);
1022            containsOpp = toTest.contains(oppStroke);
1023
1024            if (contains || containsOpp) {
1025                consumeTraversalKey(e);
1026                if (contains) {
1027                    upFocusCycle(focusedComponent);
1028                }
1029                return;
1030            }
1031
1032            if (!((focusedComponent instanceof Container JavaDoc) &&
1033                  ((Container JavaDoc)focusedComponent).isFocusCycleRoot())) {
1034                return;
1035            }
1036
1037            toTest = focusedComponent.getFocusTraversalKeys(
1038                KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
1039            contains = toTest.contains(stroke);
1040            containsOpp = toTest.contains(oppStroke);
1041
1042            if (contains || containsOpp) {
1043                consumeTraversalKey(e);
1044                if (contains) {
1045                    downFocusCycle((Container JavaDoc)focusedComponent);
1046                }
1047            }
1048        }
1049    }
1050
1051    /**
1052     * Delays dispatching of KeyEvents until the specified Component becomes
1053     * the focus owner. KeyEvents with timestamps later than the specified
1054     * timestamp will be enqueued until the specified Component receives a
1055     * FOCUS_GAINED event, or the AWT cancels the delay request by invoking
1056     * <code>dequeueKeyEvents</code> or <code>discardKeyEvents</code>.
1057     *
1058     * @param after timestamp of current event, or the current, system time if
1059     * the current event has no timestamp, or the AWT cannot determine
1060     * which event is currently being handled
1061     * @param untilFocused Component which will receive a FOCUS_GAINED event
1062     * before any pending KeyEvents
1063     * @see #dequeueKeyEvents
1064     * @see #discardKeyEvents
1065     */

1066    protected synchronized void enqueueKeyEvents(long after,
1067                                                 Component JavaDoc untilFocused) {
1068        if (untilFocused == null) {
1069            return;
1070        }
1071        
1072        focusLog.log(Level.FINER, "Enqueue at {0} for {1}",
1073                     new Object JavaDoc[] {after, untilFocused});
1074
1075        int insertionIndex = 0,
1076            i = typeAheadMarkers.size();
1077        ListIterator JavaDoc iter = typeAheadMarkers.listIterator(i);
1078
1079        for (; i > 0; i--) {
1080            TypeAheadMarker marker = (TypeAheadMarker)iter.previous();
1081            if (marker.after <= after) {
1082                insertionIndex = i;
1083                break;
1084            }
1085        }
1086
1087        typeAheadMarkers.add(insertionIndex,
1088                             new TypeAheadMarker(after, untilFocused));
1089    }
1090
1091    /**
1092     * Releases for normal dispatching to the current focus owner all
1093     * KeyEvents which were enqueued because of a call to
1094     * <code>enqueueKeyEvents</code> with the same timestamp and Component.
1095     * If the given timestamp is less than zero, the outstanding enqueue
1096     * request for the given Component with the <b>oldest</b> timestamp (if
1097     * any) should be cancelled.
1098     *
1099     * @param after the timestamp specified in the call to
1100     * <code>enqueueKeyEvents</code>, or any value < 0
1101     * @param untilFocused the Component specified in the call to
1102     * <code>enqueueKeyEvents</code>
1103     * @see #enqueueKeyEvents
1104     * @see #discardKeyEvents
1105     */

1106    protected synchronized void dequeueKeyEvents(long after,
1107                                                 Component JavaDoc untilFocused) {
1108        if (untilFocused == null) {
1109            return;
1110        }
1111
1112        focusLog.log(Level.FINER, "Dequeue at {0} for {1}",
1113                     new Object JavaDoc[] {after, untilFocused});
1114
1115        TypeAheadMarker marker;
1116        ListIterator JavaDoc iter = typeAheadMarkers.listIterator
1117            ((after >= 0) ? typeAheadMarkers.size() : 0);
1118
1119        if (after < 0) {
1120            while (iter.hasNext()) {
1121                marker = (TypeAheadMarker)iter.next();
1122                if (marker.untilFocused == untilFocused)
1123                {
1124                    iter.remove();
1125                    return;
1126                }
1127            }
1128        } else {
1129            while (iter.hasPrevious()) {
1130                marker = (TypeAheadMarker)iter.previous();
1131                if (marker.untilFocused == untilFocused &&
1132                    marker.after == after)
1133                {
1134                    iter.remove();
1135                    return;
1136                }
1137            }
1138        }
1139    }
1140
1141    /**
1142     * Discards all KeyEvents which were enqueued because of one or more calls
1143     * to <code>enqueueKeyEvents</code> with the specified Component, or one of
1144     * its descendants.
1145     *
1146     * @param comp the Component specified in one or more calls to
1147     * <code>enqueueKeyEvents</code>, or a parent of such a Component
1148     * @see #enqueueKeyEvents
1149     * @see #dequeueKeyEvents
1150     */

1151    protected synchronized void discardKeyEvents(Component JavaDoc comp) {
1152        if (comp == null) {
1153            return;
1154        }
1155
1156        long start = -1;
1157
1158        for (Iterator JavaDoc iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
1159            TypeAheadMarker marker = (TypeAheadMarker)iter.next();
1160            Component JavaDoc toTest = marker.untilFocused;
1161            boolean match = (toTest == comp);
1162            while (!match && toTest != null && !(toTest instanceof Window JavaDoc)) {
1163                toTest = toTest.getParent();
1164                match = (toTest == comp);
1165            }
1166            if (match) {
1167                if (start < 0) {
1168                    start = marker.after;
1169                }
1170                iter.remove();
1171            } else if (start >= 0) {
1172                purgeStampedEvents(start, marker.after);
1173                start = -1;
1174            }
1175        }
1176
1177        purgeStampedEvents(start, -1);
1178    }
1179
1180    // Notes:
1181
// * must be called inside a synchronized block
1182
// * if 'start' is < 0, then this function does nothing
1183
// * if 'end' is < 0, then all KeyEvents from 'start' to the end of the
1184
// queue will be removed
1185
private void purgeStampedEvents(long start, long end) {
1186        if (start < 0) {
1187            return;
1188        }
1189
1190        for (Iterator JavaDoc iter = enqueuedKeyEvents.iterator(); iter.hasNext(); ) {
1191            KeyEvent JavaDoc ke = (KeyEvent JavaDoc)iter.next();
1192            long time = ke.getWhen();
1193
1194            if (start < time && (end < 0 || time <= end)) {
1195                iter.remove();
1196            }
1197
1198            if (end >= 0 && time > end) {
1199                break;
1200            }
1201        }
1202    }
1203
1204    /**
1205     * Focuses the Component before aComponent, typically based on a
1206     * FocusTraversalPolicy.
1207     *
1208     * @param aComponent the Component that is the basis for the focus
1209     * traversal operation
1210     * @see FocusTraversalPolicy
1211     * @see Component#transferFocusBackward
1212     */

1213    public void focusPreviousComponent(Component JavaDoc aComponent) {
1214        if (aComponent != null) {
1215            aComponent.transferFocusBackward();
1216        }
1217    }
1218
1219    /**
1220     * Focuses the Component after aComponent, typically based on a
1221     * FocusTraversalPolicy.
1222     *
1223     * @param aComponent the Component that is the basis for the focus
1224     * traversal operation
1225     * @see FocusTraversalPolicy
1226     * @see Component#transferFocus
1227     */

1228    public void focusNextComponent(Component JavaDoc aComponent) {
1229        if (aComponent != null) {
1230            aComponent.transferFocus();
1231        }
1232    }
1233
1234    /**
1235     * Moves the focus up one focus traversal cycle. Typically, the focus owner
1236     * is set to aComponent's focus cycle root, and the current focus cycle
1237     * root is set to the new focus owner's focus cycle root. If, however,
1238     * aComponent's focus cycle root is a Window, then the focus owner is set
1239     * to the focus cycle root's default Component to focus, and the current
1240     * focus cycle root is unchanged.
1241     *
1242     * @param aComponent the Component that is the basis for the focus
1243     * traversal operation
1244     * @see Component#transferFocusUpCycle
1245     */

1246    public void upFocusCycle(Component JavaDoc aComponent) {
1247        if (aComponent != null) {
1248            aComponent.transferFocusUpCycle();
1249        }
1250    }
1251
1252    /**
1253     * Moves the focus down one focus traversal cycle. If aContainer is a focus
1254     * cycle root, then the focus owner is set to aContainer's default
1255     * Component to focus, and the current focus cycle root is set to
1256     * aContainer. If aContainer is not a focus cycle root, then no focus
1257     * traversal operation occurs.
1258     *
1259     * @param aContainer the Container that is the basis for the focus
1260     * traversal operation
1261     * @see Container#transferFocusDownCycle
1262     */

1263    public void downFocusCycle(Container JavaDoc aContainer) {
1264        if (aContainer != null && aContainer.isFocusCycleRoot()) {
1265            aContainer.transferFocusDownCycle();
1266        }
1267    }
1268}
1269
Popular Tags