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