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