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