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