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