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