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