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