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