View Javadoc
1   /*
2    * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package java.awt;
26  
27  import java.awt.event.FocusEvent;
28  import java.awt.event.KeyEvent;
29  import java.awt.event.WindowEvent;
30  import java.awt.peer.ComponentPeer;
31  import java.awt.peer.LightweightPeer;
32  import java.lang.ref.WeakReference;
33  import java.util.LinkedList;
34  import java.util.Iterator;
35  import java.util.ListIterator;
36  import java.util.Set;
37  
38  import sun.util.logging.PlatformLogger;
39  
40  import sun.awt.AppContext;
41  import sun.awt.SunToolkit;
42  import sun.awt.AWTAccessor;
43  import sun.awt.CausedFocusEvent;
44  import sun.awt.TimedWindowEvent;
45  
46  /**
47   * The default KeyboardFocusManager for AWT applications. Focus traversal is
48   * done in response to a Component's focus traversal keys, and using a
49   * Container's FocusTraversalPolicy.
50   * <p>
51   * Please see
52   * <a href="http://docs.oracle.com/javase/tutorial/uiswing/misc/focus.html">
53   * How to Use the Focus Subsystem</a>,
54   * a section in <em>The Java Tutorial</em>, and the
55   * <a href="../../java/awt/doc-files/FocusSpec.html">Focus Specification</a>
56   * for more information.
57   *
58   * @author David Mendenhall
59   *
60   * @see FocusTraversalPolicy
61   * @see Component#setFocusTraversalKeys
62   * @see Component#getFocusTraversalKeys
63   * @since 1.4
64   */
65  public class DefaultKeyboardFocusManager extends KeyboardFocusManager {
66      private static final PlatformLogger focusLog = PlatformLogger.getLogger("java.awt.focus.DefaultKeyboardFocusManager");
67  
68      // null weak references to not create too many objects
69      private static final WeakReference<Window> NULL_WINDOW_WR =
70          new WeakReference<Window>(null);
71      private static final WeakReference<Component> NULL_COMPONENT_WR =
72          new WeakReference<Component>(null);
73      private WeakReference<Window> realOppositeWindowWR = NULL_WINDOW_WR;
74      private WeakReference<Component> realOppositeComponentWR = NULL_COMPONENT_WR;
75      private int inSendMessage;
76      private LinkedList<KeyEvent> enqueuedKeyEvents = new LinkedList<KeyEvent>();
77      private LinkedList<TypeAheadMarker> typeAheadMarkers = new LinkedList<TypeAheadMarker>();
78      private boolean consumeNextKeyTyped;
79  
80      static {
81          AWTAccessor.setDefaultKeyboardFocusManagerAccessor(
82              new AWTAccessor.DefaultKeyboardFocusManagerAccessor() {
83                  public void consumeNextKeyTyped(DefaultKeyboardFocusManager dkfm, KeyEvent e) {
84                      dkfm.consumeNextKeyTyped(e);
85                  }
86              });
87      }
88  
89      private static class TypeAheadMarker {
90          long after;
91          Component untilFocused;
92  
93          TypeAheadMarker(long after, Component untilFocused) {
94              this.after = after;
95              this.untilFocused = untilFocused;
96          }
97          /**
98           * Returns string representation of the marker
99           */
100         public String toString() {
101             return ">>> Marker after " + after + " on " + untilFocused;
102         }
103     }
104 
105     private Window getOwningFrameDialog(Window window) {
106         while (window != null && !(window instanceof Frame ||
107                                    window instanceof Dialog)) {
108             window = (Window)window.getParent();
109         }
110         return window;
111     }
112 
113     /*
114      * This series of restoreFocus methods is used for recovering from a
115      * rejected focus or activation change. Rejections typically occur when
116      * the user attempts to focus a non-focusable Component or Window.
117      */
118     private void restoreFocus(FocusEvent fe, Window newFocusedWindow) {
119         Component realOppositeComponent = this.realOppositeComponentWR.get();
120         Component vetoedComponent = fe.getComponent();
121 
122         if (newFocusedWindow != null && restoreFocus(newFocusedWindow,
123                                                      vetoedComponent, false))
124         {
125         } else if (realOppositeComponent != null &&
126                    doRestoreFocus(realOppositeComponent, vetoedComponent, false)) {
127         } else if (fe.getOppositeComponent() != null &&
128                    doRestoreFocus(fe.getOppositeComponent(), vetoedComponent, false)) {
129         } else {
130             clearGlobalFocusOwnerPriv();
131         }
132     }
133     private void restoreFocus(WindowEvent we) {
134         Window realOppositeWindow = this.realOppositeWindowWR.get();
135         if (realOppositeWindow != null
136             && restoreFocus(realOppositeWindow, null, false))
137         {
138             // do nothing, everything is done in restoreFocus()
139         } else if (we.getOppositeWindow() != null &&
140                    restoreFocus(we.getOppositeWindow(), null, false))
141         {
142             // do nothing, everything is done in restoreFocus()
143         } else {
144             clearGlobalFocusOwnerPriv();
145         }
146     }
147     private boolean restoreFocus(Window aWindow, Component vetoedComponent,
148                                  boolean clearOnFailure) {
149         Component toFocus =
150             KeyboardFocusManager.getMostRecentFocusOwner(aWindow);
151 
152         if (toFocus != null && toFocus != vetoedComponent && doRestoreFocus(toFocus, vetoedComponent, false)) {
153             return true;
154         } else if (clearOnFailure) {
155             clearGlobalFocusOwnerPriv();
156             return true;
157         } else {
158             return false;
159         }
160     }
161     private boolean restoreFocus(Component toFocus, boolean clearOnFailure) {
162         return doRestoreFocus(toFocus, null, clearOnFailure);
163     }
164     private boolean doRestoreFocus(Component toFocus, Component vetoedComponent,
165                                    boolean clearOnFailure)
166     {
167         if (toFocus != vetoedComponent && toFocus.isShowing() && toFocus.canBeFocusOwner() &&
168             toFocus.requestFocus(false, CausedFocusEvent.Cause.ROLLBACK))
169         {
170             return true;
171         } else {
172             Component nextFocus = toFocus.getNextFocusCandidate();
173             if (nextFocus != null && nextFocus != vetoedComponent &&
174                 nextFocus.requestFocusInWindow(CausedFocusEvent.Cause.ROLLBACK))
175             {
176                 return true;
177             } else if (clearOnFailure) {
178                 clearGlobalFocusOwnerPriv();
179                 return true;
180             } else {
181                 return false;
182             }
183         }
184     }
185 
186     /**
187      * A special type of SentEvent which updates a counter in the target
188      * KeyboardFocusManager if it is an instance of
189      * DefaultKeyboardFocusManager.
190      */
191     private static class DefaultKeyboardFocusManagerSentEvent
192         extends SentEvent
193     {
194         /*
195          * serialVersionUID
196          */
197         private static final long serialVersionUID = -2924743257508701758L;
198 
199         public DefaultKeyboardFocusManagerSentEvent(AWTEvent nested,
200                                                     AppContext toNotify) {
201             super(nested, toNotify);
202         }
203         public final void dispatch() {
204             KeyboardFocusManager manager =
205                 KeyboardFocusManager.getCurrentKeyboardFocusManager();
206             DefaultKeyboardFocusManager defaultManager =
207                 (manager instanceof DefaultKeyboardFocusManager)
208                 ? (DefaultKeyboardFocusManager)manager
209                 : null;
210 
211             if (defaultManager != null) {
212                 synchronized (defaultManager) {
213                     defaultManager.inSendMessage++;
214                 }
215             }
216 
217             super.dispatch();
218 
219             if (defaultManager != null) {
220                 synchronized (defaultManager) {
221                     defaultManager.inSendMessage--;
222                 }
223             }
224         }
225     }
226 
227     /**
228      * Sends a synthetic AWTEvent to a Component. If the Component is in
229      * the current AppContext, then the event is immediately dispatched.
230      * If the Component is in a different AppContext, then the event is
231      * posted to the other AppContext's EventQueue, and this method blocks
232      * until the event is handled or target AppContext is disposed.
233      * Returns true if successfuly dispatched event, false if failed
234      * to dispatch.
235      */
236     static boolean sendMessage(Component target, AWTEvent e) {
237         e.isPosted = true;
238         AppContext myAppContext = AppContext.getAppContext();
239         final AppContext targetAppContext = target.appContext;
240         final SentEvent se =
241             new DefaultKeyboardFocusManagerSentEvent(e, myAppContext);
242 
243         if (myAppContext == targetAppContext) {
244             se.dispatch();
245         } else {
246             if (targetAppContext.isDisposed()) {
247                 return false;
248             }
249             SunToolkit.postEvent(targetAppContext, se);
250             if (EventQueue.isDispatchThread()) {
251                 EventDispatchThread edt = (EventDispatchThread)
252                     Thread.currentThread();
253                 edt.pumpEvents(SentEvent.ID, new Conditional() {
254                         public boolean evaluate() {
255                             return !se.dispatched && !targetAppContext.isDisposed();
256                         }
257                     });
258             } else {
259                 synchronized (se) {
260                     while (!se.dispatched && !targetAppContext.isDisposed()) {
261                         try {
262                             se.wait(1000);
263                         } catch (InterruptedException ie) {
264                             break;
265                         }
266                     }
267                 }
268             }
269         }
270         return se.dispatched;
271     }
272 
273     /*
274      * Checks if the focus window event follows key events waiting in the type-ahead
275      * queue (if any). This may happen when a user types ahead in the window, the client
276      * listeners hang EDT for a while, and the user switches b/w toplevels. In that
277      * case the focus window events may be dispatched before the type-ahead events
278      * get handled. This may lead to wrong focus behavior and in order to avoid it,
279      * the focus window events are reposted to the end of the event queue. See 6981400.
280      */
281     private boolean repostIfFollowsKeyEvents(WindowEvent e) {
282         if (!(e instanceof TimedWindowEvent)) {
283             return false;
284         }
285         TimedWindowEvent we = (TimedWindowEvent)e;
286         long time = we.getWhen();
287         synchronized (this) {
288             KeyEvent ke = enqueuedKeyEvents.isEmpty() ? null : enqueuedKeyEvents.getFirst();
289             if (ke != null && time >= ke.getWhen()) {
290                 TypeAheadMarker marker = typeAheadMarkers.getFirst();
291                 if (marker != null) {
292                     Window toplevel = marker.untilFocused.getContainingWindow();
293                     // Check that the component awaiting focus belongs to
294                     // the current focused window. See 8015454.
295                     if (toplevel != null && toplevel.isFocused()) {
296                         SunToolkit.postEvent(AppContext.getAppContext(), new SequencedEvent(e));
297                         return true;
298                     }
299                 }
300             }
301         }
302         return false;
303     }
304 
305     /**
306      * This method is called by the AWT event dispatcher requesting that the
307      * current KeyboardFocusManager dispatch the specified event on its behalf.
308      * DefaultKeyboardFocusManagers dispatch all FocusEvents, all WindowEvents
309      * related to focus, and all KeyEvents. These events are dispatched based
310      * on the KeyboardFocusManager's notion of the focus owner and the focused
311      * and active Windows, sometimes overriding the source of the specified
312      * AWTEvent. If this method returns <code>false</code>, then the AWT event
313      * dispatcher will attempt to dispatch the event itself.
314      *
315      * @param e the AWTEvent to be dispatched
316      * @return <code>true</code> if this method dispatched the event;
317      *         <code>false</code> otherwise
318      */
319     public boolean dispatchEvent(AWTEvent e) {
320         if (focusLog.isLoggable(PlatformLogger.Level.FINE) && (e instanceof WindowEvent || e instanceof FocusEvent)) {
321             focusLog.fine("" + e);
322         }
323         switch (e.getID()) {
324             case WindowEvent.WINDOW_GAINED_FOCUS: {
325                 if (repostIfFollowsKeyEvents((WindowEvent)e)) {
326                     break;
327                 }
328 
329                 WindowEvent we = (WindowEvent)e;
330                 Window oldFocusedWindow = getGlobalFocusedWindow();
331                 Window newFocusedWindow = we.getWindow();
332                 if (newFocusedWindow == oldFocusedWindow) {
333                     break;
334                 }
335 
336                 if (!(newFocusedWindow.isFocusableWindow()
337                       && newFocusedWindow.isVisible()
338                       && newFocusedWindow.isDisplayable()))
339                 {
340                     // we can not accept focus on such window, so reject it.
341                     restoreFocus(we);
342                     break;
343                 }
344                 // If there exists a current focused window, then notify it
345                 // that it has lost focus.
346                 if (oldFocusedWindow != null) {
347                     boolean isEventDispatched =
348                         sendMessage(oldFocusedWindow,
349                                 new WindowEvent(oldFocusedWindow,
350                                                 WindowEvent.WINDOW_LOST_FOCUS,
351                                                 newFocusedWindow));
352                     // Failed to dispatch, clear by ourselfves
353                     if (!isEventDispatched) {
354                         setGlobalFocusOwner(null);
355                         setGlobalFocusedWindow(null);
356                     }
357                 }
358 
359                 // Because the native libraries do not post WINDOW_ACTIVATED
360                 // events, we need to synthesize one if the active Window
361                 // changed.
362                 Window newActiveWindow =
363                     getOwningFrameDialog(newFocusedWindow);
364                 Window currentActiveWindow = getGlobalActiveWindow();
365                 if (newActiveWindow != currentActiveWindow) {
366                     sendMessage(newActiveWindow,
367                                 new WindowEvent(newActiveWindow,
368                                                 WindowEvent.WINDOW_ACTIVATED,
369                                                 currentActiveWindow));
370                     if (newActiveWindow != getGlobalActiveWindow()) {
371                         // Activation change was rejected. Unlikely, but
372                         // possible.
373                         restoreFocus(we);
374                         break;
375                     }
376                 }
377 
378                 setGlobalFocusedWindow(newFocusedWindow);
379 
380                 if (newFocusedWindow != getGlobalFocusedWindow()) {
381                     // Focus change was rejected. Will happen if
382                     // newFocusedWindow is not a focusable Window.
383                     restoreFocus(we);
384                     break;
385                 }
386 
387                 // Restore focus to the Component which last held it. We do
388                 // this here so that client code can override our choice in
389                 // a WINDOW_GAINED_FOCUS handler.
390                 //
391                 // Make sure that the focus change request doesn't change the
392                 // focused Window in case we are no longer the focused Window
393                 // when the request is handled.
394                 if (inSendMessage == 0) {
395                     // Identify which Component should initially gain focus
396                     // in the Window.
397                     //
398                     // * If we're in SendMessage, then this is a synthetic
399                     //   WINDOW_GAINED_FOCUS message which was generated by a
400                     //   the FOCUS_GAINED handler. Allow the Component to
401                     //   which the FOCUS_GAINED message was targeted to
402                     //   receive the focus.
403                     // * Otherwise, look up the correct Component here.
404                     //   We don't use Window.getMostRecentFocusOwner because
405                     //   window is focused now and 'null' will be returned
406 
407 
408                     // Calculating of most recent focus owner and focus
409                     // request should be synchronized on KeyboardFocusManager.class
410                     // to prevent from thread race when user will request
411                     // focus between calculation and our request.
412                     // But if focus transfer is synchronous, this synchronization
413                     // may cause deadlock, thus we don't synchronize this block.
414                     Component toFocus = KeyboardFocusManager.
415                         getMostRecentFocusOwner(newFocusedWindow);
416                     if ((toFocus == null) &&
417                         newFocusedWindow.isFocusableWindow())
418                     {
419                         toFocus = newFocusedWindow.getFocusTraversalPolicy().
420                             getInitialComponent(newFocusedWindow);
421                     }
422                     Component tempLost = null;
423                     synchronized(KeyboardFocusManager.class) {
424                         tempLost = newFocusedWindow.setTemporaryLostComponent(null);
425                     }
426 
427                     // The component which last has the focus when this window was focused
428                     // should receive focus first
429                     if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
430                         focusLog.finer("tempLost {0}, toFocus {1}",
431                                        tempLost, toFocus);
432                     }
433                     if (tempLost != null) {
434                         tempLost.requestFocusInWindow(CausedFocusEvent.Cause.ACTIVATION);
435                     }
436 
437                     if (toFocus != null && toFocus != tempLost) {
438                         // If there is a component which requested focus when this window
439                         // was inactive it expects to receive focus after activation.
440                         toFocus.requestFocusInWindow(CausedFocusEvent.Cause.ACTIVATION);
441                     }
442                 }
443 
444                 Window realOppositeWindow = this.realOppositeWindowWR.get();
445                 if (realOppositeWindow != we.getOppositeWindow()) {
446                     we = new WindowEvent(newFocusedWindow,
447                                          WindowEvent.WINDOW_GAINED_FOCUS,
448                                          realOppositeWindow);
449                 }
450                 return typeAheadAssertions(newFocusedWindow, we);
451             }
452 
453             case WindowEvent.WINDOW_ACTIVATED: {
454                 WindowEvent we = (WindowEvent)e;
455                 Window oldActiveWindow = getGlobalActiveWindow();
456                 Window newActiveWindow = we.getWindow();
457                 if (oldActiveWindow == newActiveWindow) {
458                     break;
459                 }
460 
461                 // If there exists a current active window, then notify it that
462                 // it has lost activation.
463                 if (oldActiveWindow != null) {
464                     boolean isEventDispatched =
465                         sendMessage(oldActiveWindow,
466                                 new WindowEvent(oldActiveWindow,
467                                                 WindowEvent.WINDOW_DEACTIVATED,
468                                                 newActiveWindow));
469                     // Failed to dispatch, clear by ourselfves
470                     if (!isEventDispatched) {
471                         setGlobalActiveWindow(null);
472                     }
473                     if (getGlobalActiveWindow() != null) {
474                         // Activation change was rejected. Unlikely, but
475                         // possible.
476                         break;
477                     }
478                 }
479 
480                 setGlobalActiveWindow(newActiveWindow);
481 
482                 if (newActiveWindow != getGlobalActiveWindow()) {
483                     // Activation change was rejected. Unlikely, but
484                     // possible.
485                     break;
486                 }
487 
488                 return typeAheadAssertions(newActiveWindow, we);
489             }
490 
491             case FocusEvent.FOCUS_GAINED: {
492                 FocusEvent fe = (FocusEvent)e;
493                 CausedFocusEvent.Cause cause = (fe instanceof CausedFocusEvent) ?
494                     ((CausedFocusEvent)fe).getCause() : CausedFocusEvent.Cause.UNKNOWN;
495                 Component oldFocusOwner = getGlobalFocusOwner();
496                 Component newFocusOwner = fe.getComponent();
497                 if (oldFocusOwner == newFocusOwner) {
498                     if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
499                         focusLog.fine("Skipping {0} because focus owner is the same", e);
500                     }
501                     // We can't just drop the event - there could be
502                     // type-ahead markers associated with it.
503                     dequeueKeyEvents(-1, newFocusOwner);
504                     break;
505                 }
506 
507                 // If there exists a current focus owner, then notify it that
508                 // it has lost focus.
509                 if (oldFocusOwner != null) {
510                     boolean isEventDispatched =
511                         sendMessage(oldFocusOwner,
512                                     new CausedFocusEvent(oldFocusOwner,
513                                                    FocusEvent.FOCUS_LOST,
514                                                    fe.isTemporary(),
515                                                    newFocusOwner, cause));
516                     // Failed to dispatch, clear by ourselfves
517                     if (!isEventDispatched) {
518                         setGlobalFocusOwner(null);
519                         if (!fe.isTemporary()) {
520                             setGlobalPermanentFocusOwner(null);
521                         }
522                     }
523                 }
524 
525                 // Because the native windowing system has a different notion
526                 // of the current focus and activation states, it is possible
527                 // that a Component outside of the focused Window receives a
528                 // FOCUS_GAINED event. We synthesize a WINDOW_GAINED_FOCUS
529                 // event in that case.
530                 final Window newFocusedWindow = SunToolkit.getContainingWindow(newFocusOwner);
531                 final Window currentFocusedWindow = getGlobalFocusedWindow();
532                 if (newFocusedWindow != null &&
533                     newFocusedWindow != currentFocusedWindow)
534                 {
535                     sendMessage(newFocusedWindow,
536                                 new WindowEvent(newFocusedWindow,
537                                         WindowEvent.WINDOW_GAINED_FOCUS,
538                                                 currentFocusedWindow));
539                     if (newFocusedWindow != getGlobalFocusedWindow()) {
540                         // Focus change was rejected. Will happen if
541                         // newFocusedWindow is not a focusable Window.
542 
543                         // Need to recover type-ahead, but don't bother
544                         // restoring focus. That was done by the
545                         // WINDOW_GAINED_FOCUS handler
546                         dequeueKeyEvents(-1, newFocusOwner);
547                         break;
548                     }
549                 }
550 
551                 if (!(newFocusOwner.isFocusable() && newFocusOwner.isShowing() &&
552                     // Refuse focus on a disabled component if the focus event
553                     // isn't of UNKNOWN reason (i.e. not a result of a direct request
554                     // but traversal, activation or system generated).
555                     (newFocusOwner.isEnabled() || cause.equals(CausedFocusEvent.Cause.UNKNOWN))))
556                 {
557                     // we should not accept focus on such component, so reject it.
558                     dequeueKeyEvents(-1, newFocusOwner);
559                     if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
560                         // If FOCUS_GAINED is for a disposed component (however
561                         // it shouldn't happen) its toplevel parent is null. In this
562                         // case we have to try to restore focus in the current focused
563                         // window (for the details: 6607170).
564                         if (newFocusedWindow == null) {
565                             restoreFocus(fe, currentFocusedWindow);
566                         } else {
567                             restoreFocus(fe, newFocusedWindow);
568                         }
569                         setMostRecentFocusOwner(newFocusedWindow, null); // see: 8013773
570                     }
571                     break;
572                 }
573 
574                 setGlobalFocusOwner(newFocusOwner);
575 
576                 if (newFocusOwner != getGlobalFocusOwner()) {
577                     // Focus change was rejected. Will happen if
578                     // newFocusOwner is not focus traversable.
579                     dequeueKeyEvents(-1, newFocusOwner);
580                     if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
581                         restoreFocus(fe, (Window)newFocusedWindow);
582                     }
583                     break;
584                 }
585 
586                 if (!fe.isTemporary()) {
587                     setGlobalPermanentFocusOwner(newFocusOwner);
588 
589                     if (newFocusOwner != getGlobalPermanentFocusOwner()) {
590                         // Focus change was rejected. Unlikely, but possible.
591                         dequeueKeyEvents(-1, newFocusOwner);
592                         if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
593                             restoreFocus(fe, (Window)newFocusedWindow);
594                         }
595                         break;
596                     }
597                 }
598 
599                 setNativeFocusOwner(getHeavyweight(newFocusOwner));
600 
601                 Component realOppositeComponent = this.realOppositeComponentWR.get();
602                 if (realOppositeComponent != null &&
603                     realOppositeComponent != fe.getOppositeComponent()) {
604                     fe = new CausedFocusEvent(newFocusOwner,
605                                         FocusEvent.FOCUS_GAINED,
606                                         fe.isTemporary(),
607                                         realOppositeComponent, cause);
608                     ((AWTEvent) fe).isPosted = true;
609                 }
610                 return typeAheadAssertions(newFocusOwner, fe);
611             }
612 
613             case FocusEvent.FOCUS_LOST: {
614                 FocusEvent fe = (FocusEvent)e;
615                 Component currentFocusOwner = getGlobalFocusOwner();
616                 if (currentFocusOwner == null) {
617                     if (focusLog.isLoggable(PlatformLogger.Level.FINE))
618                         focusLog.fine("Skipping {0} because focus owner is null", e);
619                     break;
620                 }
621                 // Ignore cases where a Component loses focus to itself.
622                 // If we make a mistake because of retargeting, then the
623                 // FOCUS_GAINED handler will correct it.
624                 if (currentFocusOwner == fe.getOppositeComponent()) {
625                     if (focusLog.isLoggable(PlatformLogger.Level.FINE))
626                         focusLog.fine("Skipping {0} because current focus owner is equal to opposite", e);
627                     break;
628                 }
629 
630                 setGlobalFocusOwner(null);
631 
632                 if (getGlobalFocusOwner() != null) {
633                     // Focus change was rejected. Unlikely, but possible.
634                     restoreFocus(currentFocusOwner, true);
635                     break;
636                 }
637 
638                 if (!fe.isTemporary()) {
639                     setGlobalPermanentFocusOwner(null);
640 
641                     if (getGlobalPermanentFocusOwner() != null) {
642                         // Focus change was rejected. Unlikely, but possible.
643                         restoreFocus(currentFocusOwner, true);
644                         break;
645                     }
646                 } else {
647                     Window owningWindow = currentFocusOwner.getContainingWindow();
648                     if (owningWindow != null) {
649                         owningWindow.setTemporaryLostComponent(currentFocusOwner);
650                     }
651                 }
652 
653                 setNativeFocusOwner(null);
654 
655                 fe.setSource(currentFocusOwner);
656 
657                 realOppositeComponentWR = (fe.getOppositeComponent() != null)
658                     ? new WeakReference<Component>(currentFocusOwner)
659                     : NULL_COMPONENT_WR;
660 
661                 return typeAheadAssertions(currentFocusOwner, fe);
662             }
663 
664             case WindowEvent.WINDOW_DEACTIVATED: {
665                 WindowEvent we = (WindowEvent)e;
666                 Window currentActiveWindow = getGlobalActiveWindow();
667                 if (currentActiveWindow == null) {
668                     break;
669                 }
670 
671                 if (currentActiveWindow != e.getSource()) {
672                     // The event is lost in time.
673                     // Allow listeners to precess the event but do not
674                     // change any global states
675                     break;
676                 }
677 
678                 setGlobalActiveWindow(null);
679                 if (getGlobalActiveWindow() != null) {
680                     // Activation change was rejected. Unlikely, but possible.
681                     break;
682                 }
683 
684                 we.setSource(currentActiveWindow);
685                 return typeAheadAssertions(currentActiveWindow, we);
686             }
687 
688             case WindowEvent.WINDOW_LOST_FOCUS: {
689                 if (repostIfFollowsKeyEvents((WindowEvent)e)) {
690                     break;
691                 }
692 
693                 WindowEvent we = (WindowEvent)e;
694                 Window currentFocusedWindow = getGlobalFocusedWindow();
695                 Window losingFocusWindow = we.getWindow();
696                 Window activeWindow = getGlobalActiveWindow();
697                 Window oppositeWindow = we.getOppositeWindow();
698                 if (focusLog.isLoggable(PlatformLogger.Level.FINE))
699                     focusLog.fine("Active {0}, Current focused {1}, losing focus {2} opposite {3}",
700                                   activeWindow, currentFocusedWindow,
701                                   losingFocusWindow, oppositeWindow);
702                 if (currentFocusedWindow == null) {
703                     break;
704                 }
705 
706                 // Special case -- if the native windowing system posts an
707                 // event claiming that the active Window has lost focus to the
708                 // focused Window, then discard the event. This is an artifact
709                 // of the native windowing system not knowing which Window is
710                 // really focused.
711                 if (inSendMessage == 0 && losingFocusWindow == activeWindow &&
712                     oppositeWindow == currentFocusedWindow)
713                 {
714                     break;
715                 }
716 
717                 Component currentFocusOwner = getGlobalFocusOwner();
718                 if (currentFocusOwner != null) {
719                     // The focus owner should always receive a FOCUS_LOST event
720                     // before the Window is defocused.
721                     Component oppositeComp = null;
722                     if (oppositeWindow != null) {
723                         oppositeComp = oppositeWindow.getTemporaryLostComponent();
724                         if (oppositeComp == null) {
725                             oppositeComp = oppositeWindow.getMostRecentFocusOwner();
726                         }
727                     }
728                     if (oppositeComp == null) {
729                         oppositeComp = oppositeWindow;
730                     }
731                     sendMessage(currentFocusOwner,
732                                 new CausedFocusEvent(currentFocusOwner,
733                                                FocusEvent.FOCUS_LOST,
734                                                true,
735                                                oppositeComp, CausedFocusEvent.Cause.ACTIVATION));
736                 }
737 
738                 setGlobalFocusedWindow(null);
739                 if (getGlobalFocusedWindow() != null) {
740                     // Focus change was rejected. Unlikely, but possible.
741                     restoreFocus(currentFocusedWindow, null, true);
742                     break;
743                 }
744 
745                 we.setSource(currentFocusedWindow);
746                 realOppositeWindowWR = (oppositeWindow != null)
747                     ? new WeakReference<Window>(currentFocusedWindow)
748                     : NULL_WINDOW_WR;
749                 typeAheadAssertions(currentFocusedWindow, we);
750 
751                 if (oppositeWindow == null) {
752                     // Then we need to deactive the active Window as well.
753                     // No need to synthesize in other cases, because
754                     // WINDOW_ACTIVATED will handle it if necessary.
755                     sendMessage(activeWindow,
756                                 new WindowEvent(activeWindow,
757                                                 WindowEvent.WINDOW_DEACTIVATED,
758                                                 null));
759                     if (getGlobalActiveWindow() != null) {
760                         // Activation change was rejected. Unlikely,
761                         // but possible.
762                         restoreFocus(currentFocusedWindow, null, true);
763                     }
764                 }
765                 break;
766             }
767 
768             case KeyEvent.KEY_TYPED:
769             case KeyEvent.KEY_PRESSED:
770             case KeyEvent.KEY_RELEASED:
771                 return typeAheadAssertions(null, e);
772 
773             default:
774                 return false;
775         }
776 
777         return true;
778     }
779 
780     /**
781      * Called by <code>dispatchEvent</code> if no other
782      * KeyEventDispatcher in the dispatcher chain dispatched the KeyEvent, or
783      * if no other KeyEventDispatchers are registered. If the event has not
784      * been consumed, its target is enabled, and the focus owner is not null,
785      * this method dispatches the event to its target. This method will also
786      * subsequently dispatch the event to all registered
787      * KeyEventPostProcessors. After all this operations are finished,
788      * the event is passed to peers for processing.
789      * <p>
790      * In all cases, this method returns <code>true</code>, since
791      * DefaultKeyboardFocusManager is designed so that neither
792      * <code>dispatchEvent</code>, nor the AWT event dispatcher, should take
793      * further action on the event in any situation.
794      *
795      * @param e the KeyEvent to be dispatched
796      * @return <code>true</code>
797      * @see Component#dispatchEvent
798      */
799     public boolean dispatchKeyEvent(KeyEvent e) {
800         Component focusOwner = (((AWTEvent)e).isPosted) ? getFocusOwner() : e.getComponent();
801 
802         if (focusOwner != null && focusOwner.isShowing() && focusOwner.canBeFocusOwner()) {
803             if (!e.isConsumed()) {
804                 Component comp = e.getComponent();
805                 if (comp != null && comp.isEnabled()) {
806                     redispatchEvent(comp, e);
807                 }
808             }
809         }
810         boolean stopPostProcessing = false;
811         java.util.List<KeyEventPostProcessor> processors = getKeyEventPostProcessors();
812         if (processors != null) {
813             for (java.util.Iterator<KeyEventPostProcessor> iter = processors.iterator();
814                  !stopPostProcessing && iter.hasNext(); )
815             {
816                 stopPostProcessing = iter.next().
817                             postProcessKeyEvent(e);
818             }
819         }
820         if (!stopPostProcessing) {
821             postProcessKeyEvent(e);
822         }
823 
824         // Allow the peer to process KeyEvent
825         Component source = e.getComponent();
826         ComponentPeer peer = source.getPeer();
827 
828         if (peer == null || peer instanceof LightweightPeer) {
829             // if focus owner is lightweight then its native container
830             // processes event
831             Container target = source.getNativeContainer();
832             if (target != null) {
833                 peer = target.getPeer();
834             }
835         }
836         if (peer != null) {
837             peer.handleEvent(e);
838         }
839 
840         return true;
841     }
842 
843     /**
844      * This method will be called by <code>dispatchKeyEvent</code>. It will
845      * handle any unconsumed KeyEvents that map to an AWT
846      * <code>MenuShortcut</code> by consuming the event and activating the
847      * shortcut.
848      *
849      * @param e the KeyEvent to post-process
850      * @return <code>true</code>
851      * @see #dispatchKeyEvent
852      * @see MenuShortcut
853      */
854     public boolean postProcessKeyEvent(KeyEvent e) {
855         if (!e.isConsumed()) {
856             Component target = e.getComponent();
857             Container p = (Container)
858                 (target instanceof Container ? target : target.getParent());
859             if (p != null) {
860                 p.postProcessKeyEvent(e);
861             }
862         }
863         return true;
864     }
865 
866     private void pumpApprovedKeyEvents() {
867         KeyEvent ke;
868         do {
869             ke = null;
870             synchronized (this) {
871                 if (enqueuedKeyEvents.size() != 0) {
872                     ke = enqueuedKeyEvents.getFirst();
873                     if (typeAheadMarkers.size() != 0) {
874                         TypeAheadMarker marker = typeAheadMarkers.getFirst();
875                         // Fixed 5064013: may appears that the events have the same time
876                         // if (ke.getWhen() >= marker.after) {
877                         // The fix is rolled out.
878 
879                         if (ke.getWhen() > marker.after) {
880                             ke = null;
881                         }
882                     }
883                     if (ke != null) {
884                         if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
885                             focusLog.finer("Pumping approved event {0}", ke);
886                         }
887                         enqueuedKeyEvents.removeFirst();
888                     }
889                 }
890             }
891             if (ke != null) {
892                 preDispatchKeyEvent(ke);
893             }
894         } while (ke != null);
895     }
896 
897     /**
898      * Dumps the list of type-ahead queue markers to stderr
899      */
900     void dumpMarkers() {
901         if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {
902             focusLog.finest(">>> Markers dump, time: {0}", System.currentTimeMillis());
903             synchronized (this) {
904                 if (typeAheadMarkers.size() != 0) {
905                     Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator();
906                     while (iter.hasNext()) {
907                         TypeAheadMarker marker = iter.next();
908                         focusLog.finest("    {0}", marker);
909                     }
910                 }
911             }
912         }
913     }
914 
915     private boolean typeAheadAssertions(Component target, AWTEvent e) {
916 
917         // Clear any pending events here as well as in the FOCUS_GAINED
918         // handler. We need this call here in case a marker was removed in
919         // response to a call to dequeueKeyEvents.
920         pumpApprovedKeyEvents();
921 
922         switch (e.getID()) {
923             case KeyEvent.KEY_TYPED:
924             case KeyEvent.KEY_PRESSED:
925             case KeyEvent.KEY_RELEASED: {
926                 KeyEvent ke = (KeyEvent)e;
927                 synchronized (this) {
928                     if (e.isPosted && typeAheadMarkers.size() != 0) {
929                         TypeAheadMarker marker = typeAheadMarkers.getFirst();
930                         // Fixed 5064013: may appears that the events have the same time
931                         // if (ke.getWhen() >= marker.after) {
932                         // The fix is rolled out.
933 
934                         if (ke.getWhen() > marker.after) {
935                             if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
936                                 focusLog.finer("Storing event {0} because of marker {1}", ke, marker);
937                             }
938                             enqueuedKeyEvents.addLast(ke);
939                             return true;
940                         }
941                     }
942                 }
943 
944                 // KeyEvent was posted before focus change request
945                 return preDispatchKeyEvent(ke);
946             }
947 
948             case FocusEvent.FOCUS_GAINED:
949                 if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {
950                     focusLog.finest("Markers before FOCUS_GAINED on {0}", target);
951                 }
952                 dumpMarkers();
953                 // Search the marker list for the first marker tied to
954                 // the Component which just gained focus. Then remove
955                 // that marker, any markers which immediately follow
956                 // and are tied to the same component, and all markers
957                 // that preceed it. This handles the case where
958                 // multiple focus requests were made for the same
959                 // Component in a row and when we lost some of the
960                 // earlier requests. Since FOCUS_GAINED events will
961                 // not be generated for these additional requests, we
962                 // need to clear those markers too.
963                 synchronized (this) {
964                     boolean found = false;
965                     if (hasMarker(target)) {
966                         for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator();
967                              iter.hasNext(); )
968                         {
969                             if (iter.next().untilFocused == target) {
970                                 found = true;
971                             } else if (found) {
972                                 break;
973                             }
974                             iter.remove();
975                         }
976                     } else {
977                         // Exception condition - event without marker
978                         if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
979                             focusLog.finer("Event without marker {0}", e);
980                         }
981                     }
982                 }
983                 focusLog.finest("Markers after FOCUS_GAINED");
984                 dumpMarkers();
985 
986                 redispatchEvent(target, e);
987 
988                 // Now, dispatch any pending KeyEvents which have been
989                 // released because of the FOCUS_GAINED event so that we don't
990                 // have to wait for another event to be posted to the queue.
991                 pumpApprovedKeyEvents();
992                 return true;
993 
994             default:
995                 redispatchEvent(target, e);
996                 return true;
997         }
998     }
999 
1000     /**
1001      * Returns true if there are some marker associated with component <code>comp</code>
1002      * in a markers' queue
1003      * @since 1.5
1004      */
1005     private boolean hasMarker(Component comp) {
1006         for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
1007             if (iter.next().untilFocused == comp) {
1008                 return true;
1009             }
1010         }
1011         return false;
1012     }
1013 
1014     /**
1015      * Clears markers queue
1016      * @since 1.5
1017      */
1018     void clearMarkers() {
1019         synchronized(this) {
1020             typeAheadMarkers.clear();
1021         }
1022     }
1023 
1024     private boolean preDispatchKeyEvent(KeyEvent ke) {
1025         if (((AWTEvent) ke).isPosted) {
1026             Component focusOwner = getFocusOwner();
1027             ke.setSource(((focusOwner != null) ? focusOwner : getFocusedWindow()));
1028         }
1029         if (ke.getSource() == null) {
1030             return true;
1031         }
1032 
1033         // Explicitly set the key event timestamp here (not in Component.dispatchEventImpl):
1034         // - A key event is anyway passed to this method which starts its actual dispatching.
1035         // - If a key event is put to the type ahead queue, its time stamp should not be registered
1036         //   until its dispatching actually starts (by this method).
1037         EventQueue.setCurrentEventAndMostRecentTime(ke);
1038 
1039         /**
1040          * Fix for 4495473.
1041          * This fix allows to correctly dispatch events when native
1042          * event proxying mechanism is active.
1043          * If it is active we should redispatch key events after
1044          * we detected its correct target.
1045          */
1046         if (KeyboardFocusManager.isProxyActive(ke)) {
1047             Component source = (Component)ke.getSource();
1048             Container target = source.getNativeContainer();
1049             if (target != null) {
1050                 ComponentPeer peer = target.getPeer();
1051                 if (peer != null) {
1052                     peer.handleEvent(ke);
1053                     /**
1054                      * Fix for 4478780 - consume event after it was dispatched by peer.
1055                      */
1056                     ke.consume();
1057                 }
1058             }
1059             return true;
1060         }
1061 
1062         java.util.List<KeyEventDispatcher> dispatchers = getKeyEventDispatchers();
1063         if (dispatchers != null) {
1064             for (java.util.Iterator<KeyEventDispatcher> iter = dispatchers.iterator();
1065                  iter.hasNext(); )
1066              {
1067                  if (iter.next().
1068                      dispatchKeyEvent(ke))
1069                  {
1070                      return true;
1071                  }
1072              }
1073         }
1074         return dispatchKeyEvent(ke);
1075     }
1076 
1077     /*
1078      * @param e is a KEY_PRESSED event that can be used
1079      *          to track the next KEY_TYPED related.
1080      */
1081     private void consumeNextKeyTyped(KeyEvent e) {
1082         consumeNextKeyTyped = true;
1083     }
1084 
1085     private void consumeTraversalKey(KeyEvent e) {
1086         e.consume();
1087         consumeNextKeyTyped = (e.getID() == KeyEvent.KEY_PRESSED) &&
1088                               !e.isActionKey();
1089     }
1090 
1091     /*
1092      * return true if event was consumed
1093      */
1094     private boolean consumeProcessedKeyEvent(KeyEvent e) {
1095         if ((e.getID() == KeyEvent.KEY_TYPED) && consumeNextKeyTyped) {
1096             e.consume();
1097             consumeNextKeyTyped = false;
1098             return true;
1099         }
1100         return false;
1101     }
1102 
1103     /**
1104      * This method initiates a focus traversal operation if and only if the
1105      * KeyEvent represents a focus traversal key for the specified
1106      * focusedComponent. It is expected that focusedComponent is the current
1107      * focus owner, although this need not be the case. If it is not,
1108      * focus traversal will nevertheless proceed as if focusedComponent
1109      * were the focus owner.
1110      *
1111      * @param focusedComponent the Component that is the basis for a focus
1112      *        traversal operation if the specified event represents a focus
1113      *        traversal key for the Component
1114      * @param e the event that may represent a focus traversal key
1115      */
1116     public void processKeyEvent(Component focusedComponent, KeyEvent e) {
1117         // consume processed event if needed
1118         if (consumeProcessedKeyEvent(e)) {
1119             return;
1120         }
1121 
1122         // KEY_TYPED events cannot be focus traversal keys
1123         if (e.getID() == KeyEvent.KEY_TYPED) {
1124             return;
1125         }
1126 
1127         if (focusedComponent.getFocusTraversalKeysEnabled() &&
1128             !e.isConsumed())
1129         {
1130             AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e),
1131                 oppStroke = AWTKeyStroke.getAWTKeyStroke(stroke.getKeyCode(),
1132                                                  stroke.getModifiers(),
1133                                                  !stroke.isOnKeyRelease());
1134             Set<AWTKeyStroke> toTest;
1135             boolean contains, containsOpp;
1136 
1137             toTest = focusedComponent.getFocusTraversalKeys(
1138                 KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
1139             contains = toTest.contains(stroke);
1140             containsOpp = toTest.contains(oppStroke);
1141 
1142             if (contains || containsOpp) {
1143                 consumeTraversalKey(e);
1144                 if (contains) {
1145                     focusNextComponent(focusedComponent);
1146                 }
1147                 return;
1148             } else if (e.getID() == KeyEvent.KEY_PRESSED) {
1149                 // Fix for 6637607: consumeNextKeyTyped should be reset.
1150                 consumeNextKeyTyped = false;
1151             }
1152 
1153             toTest = focusedComponent.getFocusTraversalKeys(
1154                 KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
1155             contains = toTest.contains(stroke);
1156             containsOpp = toTest.contains(oppStroke);
1157 
1158             if (contains || containsOpp) {
1159                 consumeTraversalKey(e);
1160                 if (contains) {
1161                     focusPreviousComponent(focusedComponent);
1162                 }
1163                 return;
1164             }
1165 
1166             toTest = focusedComponent.getFocusTraversalKeys(
1167                 KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
1168             contains = toTest.contains(stroke);
1169             containsOpp = toTest.contains(oppStroke);
1170 
1171             if (contains || containsOpp) {
1172                 consumeTraversalKey(e);
1173                 if (contains) {
1174                     upFocusCycle(focusedComponent);
1175                 }
1176                 return;
1177             }
1178 
1179             if (!((focusedComponent instanceof Container) &&
1180                   ((Container)focusedComponent).isFocusCycleRoot())) {
1181                 return;
1182             }
1183 
1184             toTest = focusedComponent.getFocusTraversalKeys(
1185                 KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
1186             contains = toTest.contains(stroke);
1187             containsOpp = toTest.contains(oppStroke);
1188 
1189             if (contains || containsOpp) {
1190                 consumeTraversalKey(e);
1191                 if (contains) {
1192                     downFocusCycle((Container)focusedComponent);
1193                 }
1194             }
1195         }
1196     }
1197 
1198     /**
1199      * Delays dispatching of KeyEvents until the specified Component becomes
1200      * the focus owner. KeyEvents with timestamps later than the specified
1201      * timestamp will be enqueued until the specified Component receives a
1202      * FOCUS_GAINED event, or the AWT cancels the delay request by invoking
1203      * <code>dequeueKeyEvents</code> or <code>discardKeyEvents</code>.
1204      *
1205      * @param after timestamp of current event, or the current, system time if
1206      *        the current event has no timestamp, or the AWT cannot determine
1207      *        which event is currently being handled
1208      * @param untilFocused Component which will receive a FOCUS_GAINED event
1209      *        before any pending KeyEvents
1210      * @see #dequeueKeyEvents
1211      * @see #discardKeyEvents
1212      */
1213     protected synchronized void enqueueKeyEvents(long after,
1214                                                  Component untilFocused) {
1215         if (untilFocused == null) {
1216             return;
1217         }
1218 
1219         if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
1220             focusLog.finer("Enqueue at {0} for {1}",
1221                        after, untilFocused);
1222         }
1223 
1224         int insertionIndex = 0,
1225             i = typeAheadMarkers.size();
1226         ListIterator<TypeAheadMarker> iter = typeAheadMarkers.listIterator(i);
1227 
1228         for (; i > 0; i--) {
1229             TypeAheadMarker marker = iter.previous();
1230             if (marker.after <= after) {
1231                 insertionIndex = i;
1232                 break;
1233             }
1234         }
1235 
1236         typeAheadMarkers.add(insertionIndex,
1237                              new TypeAheadMarker(after, untilFocused));
1238     }
1239 
1240     /**
1241      * Releases for normal dispatching to the current focus owner all
1242      * KeyEvents which were enqueued because of a call to
1243      * <code>enqueueKeyEvents</code> with the same timestamp and Component.
1244      * If the given timestamp is less than zero, the outstanding enqueue
1245      * request for the given Component with the <b>oldest</b> timestamp (if
1246      * any) should be cancelled.
1247      *
1248      * @param after the timestamp specified in the call to
1249      *        <code>enqueueKeyEvents</code>, or any value &lt; 0
1250      * @param untilFocused the Component specified in the call to
1251      *        <code>enqueueKeyEvents</code>
1252      * @see #enqueueKeyEvents
1253      * @see #discardKeyEvents
1254      */
1255     protected synchronized void dequeueKeyEvents(long after,
1256                                                  Component untilFocused) {
1257         if (untilFocused == null) {
1258             return;
1259         }
1260 
1261         if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
1262             focusLog.finer("Dequeue at {0} for {1}",
1263                        after, untilFocused);
1264         }
1265 
1266         TypeAheadMarker marker;
1267         ListIterator<TypeAheadMarker> iter = typeAheadMarkers.listIterator
1268             ((after >= 0) ? typeAheadMarkers.size() : 0);
1269 
1270         if (after < 0) {
1271             while (iter.hasNext()) {
1272                 marker = iter.next();
1273                 if (marker.untilFocused == untilFocused)
1274                 {
1275                     iter.remove();
1276                     return;
1277                 }
1278             }
1279         } else {
1280             while (iter.hasPrevious()) {
1281                 marker = iter.previous();
1282                 if (marker.untilFocused == untilFocused &&
1283                     marker.after == after)
1284                 {
1285                     iter.remove();
1286                     return;
1287                 }
1288             }
1289         }
1290     }
1291 
1292     /**
1293      * Discards all KeyEvents which were enqueued because of one or more calls
1294      * to <code>enqueueKeyEvents</code> with the specified Component, or one of
1295      * its descendants.
1296      *
1297      * @param comp the Component specified in one or more calls to
1298      *        <code>enqueueKeyEvents</code>, or a parent of such a Component
1299      * @see #enqueueKeyEvents
1300      * @see #dequeueKeyEvents
1301      */
1302     protected synchronized void discardKeyEvents(Component comp) {
1303         if (comp == null) {
1304             return;
1305         }
1306 
1307         long start = -1;
1308 
1309         for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
1310             TypeAheadMarker marker = iter.next();
1311             Component toTest = marker.untilFocused;
1312             boolean match = (toTest == comp);
1313             while (!match && toTest != null && !(toTest instanceof Window)) {
1314                 toTest = toTest.getParent();
1315                 match = (toTest == comp);
1316             }
1317             if (match) {
1318                 if (start < 0) {
1319                     start = marker.after;
1320                 }
1321                 iter.remove();
1322             } else if (start >= 0) {
1323                 purgeStampedEvents(start, marker.after);
1324                 start = -1;
1325             }
1326         }
1327 
1328         purgeStampedEvents(start, -1);
1329     }
1330 
1331     // Notes:
1332     //   * must be called inside a synchronized block
1333     //   * if 'start' is < 0, then this function does nothing
1334     //   * if 'end' is < 0, then all KeyEvents from 'start' to the end of the
1335     //     queue will be removed
1336     private void purgeStampedEvents(long start, long end) {
1337         if (start < 0) {
1338             return;
1339         }
1340 
1341         for (Iterator<KeyEvent> iter = enqueuedKeyEvents.iterator(); iter.hasNext(); ) {
1342             KeyEvent ke = iter.next();
1343             long time = ke.getWhen();
1344 
1345             if (start < time && (end < 0 || time <= end)) {
1346                 iter.remove();
1347             }
1348 
1349             if (end >= 0 && time > end) {
1350                 break;
1351             }
1352         }
1353     }
1354 
1355     /**
1356      * Focuses the Component before aComponent, typically based on a
1357      * FocusTraversalPolicy.
1358      *
1359      * @param aComponent the Component that is the basis for the focus
1360      *        traversal operation
1361      * @see FocusTraversalPolicy
1362      * @see Component#transferFocusBackward
1363      */
1364     public void focusPreviousComponent(Component aComponent) {
1365         if (aComponent != null) {
1366             aComponent.transferFocusBackward();
1367         }
1368     }
1369 
1370     /**
1371      * Focuses the Component after aComponent, typically based on a
1372      * FocusTraversalPolicy.
1373      *
1374      * @param aComponent the Component that is the basis for the focus
1375      *        traversal operation
1376      * @see FocusTraversalPolicy
1377      * @see Component#transferFocus
1378      */
1379     public void focusNextComponent(Component aComponent) {
1380         if (aComponent != null) {
1381             aComponent.transferFocus();
1382         }
1383     }
1384 
1385     /**
1386      * Moves the focus up one focus traversal cycle. Typically, the focus owner
1387      * is set to aComponent's focus cycle root, and the current focus cycle
1388      * root is set to the new focus owner's focus cycle root. If, however,
1389      * aComponent's focus cycle root is a Window, then the focus owner is set
1390      * to the focus cycle root's default Component to focus, and the current
1391      * focus cycle root is unchanged.
1392      *
1393      * @param aComponent the Component that is the basis for the focus
1394      *        traversal operation
1395      * @see Component#transferFocusUpCycle
1396      */
1397     public void upFocusCycle(Component aComponent) {
1398         if (aComponent != null) {
1399             aComponent.transferFocusUpCycle();
1400         }
1401     }
1402 
1403     /**
1404      * Moves the focus down one focus traversal cycle. If aContainer is a focus
1405      * cycle root, then the focus owner is set to aContainer's default
1406      * Component to focus, and the current focus cycle root is set to
1407      * aContainer. If aContainer is not a focus cycle root, then no focus
1408      * traversal operation occurs.
1409      *
1410      * @param aContainer the Container that is the basis for the focus
1411      *        traversal operation
1412      * @see Container#transferFocusDownCycle
1413      */
1414     public void downFocusCycle(Container aContainer) {
1415         if (aContainer != null && aContainer.isFocusCycleRoot()) {
1416             aContainer.transferFocusDownCycle();
1417         }
1418     }
1419 }