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