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 
  44 /**
  45  * The default KeyboardFocusManager for AWT applications. Focus traversal is
  46  * done in response to a Component's focus traversal keys, and using a
  47  * Container's FocusTraversalPolicy.
  48  * <p>
  49  * Please see
  50  * <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/focus.html">
  51  * How to Use the Focus Subsystem</a>,
  52  * a section in <em>The Java Tutorial</em>, and the
  53  * <a href="../../java/awt/doc-files/FocusSpec.html">Focus Specification</a>
  54  * for more information.
  55  *
  56  * @author David Mendenhall
  57  *
  58  * @see FocusTraversalPolicy
  59  * @see Component#setFocusTraversalKeys
  60  * @see Component#getFocusTraversalKeys
  61  * @since 1.4
  62  */
  63 public class DefaultKeyboardFocusManager extends KeyboardFocusManager {
  64     private static final PlatformLogger focusLog = PlatformLogger.getLogger("java.awt.focus.DefaultKeyboardFocusManager");
  65 
  66     // null weak references to not create too many objects
  67     private static final WeakReference<Window> NULL_WINDOW_WR =
  68         new WeakReference<Window>(null);
  69     private static final WeakReference<Component> NULL_COMPONENT_WR =
  70         new WeakReference<Component>(null);
  71     private WeakReference<Window> realOppositeWindowWR = NULL_WINDOW_WR;
  72     private WeakReference<Component> realOppositeComponentWR = NULL_COMPONENT_WR;
  73     private int inSendMessage;
  74     private LinkedList enqueuedKeyEvents = new LinkedList(),
  75         typeAheadMarkers = new LinkedList();
  76     private boolean consumeNextKeyTyped;
  77 
  78     private static class TypeAheadMarker {
  79         long after;
  80         Component untilFocused;
  81 
  82         TypeAheadMarker(long after, Component untilFocused) {
  83             this.after = after;
  84             this.untilFocused = untilFocused;
  85         }
  86         /**
  87          * Returns string representation of the marker
  88          */
  89         public String toString() {
  90             return ">>> Marker after " + after + " on " + untilFocused;
  91         }
  92     }
  93 
  94     private Window getOwningFrameDialog(Window window) {
  95         while (window != null && !(window instanceof Frame ||
  96                                    window instanceof Dialog)) {
  97             window = (Window)window.getParent();
  98         }
  99         return window;
 100     }
 101 
 102     /*
 103      * This series of restoreFocus methods is used for recovering from a
 104      * rejected focus or activation change. Rejections typically occur when
 105      * the user attempts to focus a non-focusable Component or Window.
 106      */
 107     private void restoreFocus(FocusEvent fe, Window newFocusedWindow) {
 108         Component realOppositeComponent = this.realOppositeComponentWR.get();
 109         Component vetoedComponent = fe.getComponent();
 110 
 111         if (newFocusedWindow != null && restoreFocus(newFocusedWindow,
 112                                                      vetoedComponent, false))
 113         {
 114         } else if (realOppositeComponent != null &&
 115                    doRestoreFocus(realOppositeComponent, vetoedComponent, false)) {
 116         } else if (fe.getOppositeComponent() != null &&
 117                    doRestoreFocus(fe.getOppositeComponent(), vetoedComponent, false)) {
 118         } else {
 119             clearGlobalFocusOwner();
 120         }
 121     }
 122     private void restoreFocus(WindowEvent we) {
 123         Window realOppositeWindow = this.realOppositeWindowWR.get();
 124         if (realOppositeWindow != null
 125             && restoreFocus(realOppositeWindow, null, false))
 126         {
 127             // do nothing, everything is done in restoreFocus()
 128         } else if (we.getOppositeWindow() != null &&
 129                    restoreFocus(we.getOppositeWindow(), null, false))
 130         {
 131             // do nothing, everything is done in restoreFocus()
 132         } else {
 133             clearGlobalFocusOwner();
 134         }
 135     }
 136     private boolean restoreFocus(Window aWindow, Component vetoedComponent,
 137                                  boolean clearOnFailure) {
 138         Component toFocus =
 139             KeyboardFocusManager.getMostRecentFocusOwner(aWindow);
 140 
 141         if (toFocus != null && toFocus != vetoedComponent && doRestoreFocus(toFocus, vetoedComponent, false)) {
 142             return true;
 143         } else if (clearOnFailure) {
 144             clearGlobalFocusOwner();
 145             return true;
 146         } else {
 147             return false;
 148         }
 149     }
 150     private boolean restoreFocus(Component toFocus, boolean clearOnFailure) {
 151         return doRestoreFocus(toFocus, null, clearOnFailure);
 152     }
 153     private boolean doRestoreFocus(Component toFocus, Component vetoedComponent,
 154                                    boolean clearOnFailure)
 155     {
 156         if (toFocus != vetoedComponent && toFocus.isShowing() && toFocus.canBeFocusOwner() &&
 157             toFocus.requestFocus(false, CausedFocusEvent.Cause.ROLLBACK))
 158         {
 159             return true;
 160         } else {
 161             Component nextFocus = toFocus.getNextFocusCandidate();
 162             if (nextFocus != null && nextFocus != vetoedComponent &&
 163                 nextFocus.requestFocusInWindow(CausedFocusEvent.Cause.ROLLBACK))
 164             {
 165                 return true;
 166             } else if (clearOnFailure) {
 167                 clearGlobalFocusOwner();
 168                 return true;
 169             } else {
 170                 return false;
 171             }
 172         }
 173     }
 174 
 175     /**
 176      * A special type of SentEvent which updates a counter in the target
 177      * KeyboardFocusManager if it is an instance of
 178      * DefaultKeyboardFocusManager.
 179      */
 180     private static class DefaultKeyboardFocusManagerSentEvent
 181         extends SentEvent
 182     {
 183         /*
 184          * serialVersionUID
 185          */
 186         private static final long serialVersionUID = -2924743257508701758L;
 187 
 188         public DefaultKeyboardFocusManagerSentEvent(AWTEvent nested,
 189                                                     AppContext toNotify) {
 190             super(nested, toNotify);
 191         }
 192         public final void dispatch() {
 193             KeyboardFocusManager manager =
 194                 KeyboardFocusManager.getCurrentKeyboardFocusManager();
 195             DefaultKeyboardFocusManager defaultManager =
 196                 (manager instanceof DefaultKeyboardFocusManager)
 197                 ? (DefaultKeyboardFocusManager)manager
 198                 : null;
 199 
 200             if (defaultManager != null) {
 201                 synchronized (defaultManager) {
 202                     defaultManager.inSendMessage++;
 203                 }
 204             }
 205 
 206             super.dispatch();
 207 
 208             if (defaultManager != null) {
 209                 synchronized (defaultManager) {
 210                     defaultManager.inSendMessage--;
 211                 }
 212             }
 213         }
 214     }
 215 
 216     /**
 217      * Sends a synthetic AWTEvent to a Component. If the Component is in
 218      * the current AppContext, then the event is immediately dispatched.
 219      * If the Component is in a different AppContext, then the event is
 220      * posted to the other AppContext's EventQueue, and this method blocks
 221      * until the event is handled or target AppContext is disposed.
 222      * Returns true if successfuly dispatched event, false if failed
 223      * to dispatch.
 224      */
 225     static boolean sendMessage(Component target, AWTEvent e) {
 226         e.isPosted = true;
 227         AppContext myAppContext = AppContext.getAppContext();
 228         final AppContext targetAppContext = target.appContext;
 229         final SentEvent se =
 230             new DefaultKeyboardFocusManagerSentEvent(e, myAppContext);
 231 
 232         if (myAppContext == targetAppContext) {
 233             se.dispatch();
 234         } else {
 235             if (targetAppContext.isDisposed()) {
 236                 return false;
 237             }
 238             SunToolkit.postEvent(targetAppContext, se);
 239             if (EventQueue.isDispatchThread()) {
 240                 EventDispatchThread edt = (EventDispatchThread)
 241                     Thread.currentThread();
 242                 edt.pumpEvents(SentEvent.ID, new Conditional() {
 243                         public boolean evaluate() {
 244                             return !se.dispatched && !targetAppContext.isDisposed();
 245                         }
 246                     });
 247             } else {
 248                 synchronized (se) {
 249                     while (!se.dispatched && !targetAppContext.isDisposed()) {
 250                         try {
 251                             se.wait(1000);
 252                         } catch (InterruptedException ie) {
 253                             break;
 254                         }
 255                     }
 256                 }
 257             }
 258         }
 259         return se.dispatched;
 260     }
 261 

























 262     /**
 263      * This method is called by the AWT event dispatcher requesting that the
 264      * current KeyboardFocusManager dispatch the specified event on its behalf.
 265      * DefaultKeyboardFocusManagers dispatch all FocusEvents, all WindowEvents
 266      * related to focus, and all KeyEvents. These events are dispatched based
 267      * on the KeyboardFocusManager's notion of the focus owner and the focused
 268      * and active Windows, sometimes overriding the source of the specified
 269      * AWTEvent. If this method returns <code>false</code>, then the AWT event
 270      * dispatcher will attempt to dispatch the event itself.
 271      *
 272      * @param e the AWTEvent to be dispatched
 273      * @return <code>true</code> if this method dispatched the event;
 274      *         <code>false</code> otherwise
 275      */
 276     public boolean dispatchEvent(AWTEvent e) {
 277         if (focusLog.isLoggable(PlatformLogger.FINE) && (e instanceof WindowEvent || e instanceof FocusEvent)) focusLog.fine("" + e);
 278         switch (e.getID()) {
 279             case WindowEvent.WINDOW_GAINED_FOCUS: {




 280                 WindowEvent we = (WindowEvent)e;
 281                 Window oldFocusedWindow = getGlobalFocusedWindow();
 282                 Window newFocusedWindow = we.getWindow();
 283                 if (newFocusedWindow == oldFocusedWindow) {
 284                     break;
 285                 }
 286 
 287                 if (!(newFocusedWindow.isFocusableWindow()
 288                       && newFocusedWindow.isVisible()
 289                       && newFocusedWindow.isDisplayable()))
 290                 {
 291                     // we can not accept focus on such window, so reject it.
 292                     restoreFocus(we);
 293                     break;
 294                 }
 295                 // If there exists a current focused window, then notify it
 296                 // that it has lost focus.
 297                 if (oldFocusedWindow != null) {
 298                     boolean isEventDispatched =
 299                         sendMessage(oldFocusedWindow,
 300                                 new WindowEvent(oldFocusedWindow,
 301                                                 WindowEvent.WINDOW_LOST_FOCUS,
 302                                                 newFocusedWindow));
 303                     // Failed to dispatch, clear by ourselfves
 304                     if (!isEventDispatched) {
 305                         setGlobalFocusOwner(null);
 306                         setGlobalFocusedWindow(null);
 307                     }
 308                 }
 309 
 310                 // Because the native libraries do not post WINDOW_ACTIVATED
 311                 // events, we need to synthesize one if the active Window
 312                 // changed.
 313                 Window newActiveWindow =
 314                     getOwningFrameDialog(newFocusedWindow);
 315                 Window currentActiveWindow = getGlobalActiveWindow();
 316                 if (newActiveWindow != currentActiveWindow) {
 317                     sendMessage(newActiveWindow,
 318                                 new WindowEvent(newActiveWindow,
 319                                                 WindowEvent.WINDOW_ACTIVATED,
 320                                                 currentActiveWindow));
 321                     if (newActiveWindow != getGlobalActiveWindow()) {
 322                         // Activation change was rejected. Unlikely, but
 323                         // possible.
 324                         restoreFocus(we);
 325                         break;
 326                     }
 327                 }
 328 
 329                 setGlobalFocusedWindow(newFocusedWindow);
 330 
 331                 if (newFocusedWindow != getGlobalFocusedWindow()) {
 332                     // Focus change was rejected. Will happen if
 333                     // newFocusedWindow is not a focusable Window.
 334                     restoreFocus(we);
 335                     break;
 336                 }
 337 
 338                 // Restore focus to the Component which last held it. We do
 339                 // this here so that client code can override our choice in
 340                 // a WINDOW_GAINED_FOCUS handler.
 341                 //
 342                 // Make sure that the focus change request doesn't change the
 343                 // focused Window in case we are no longer the focused Window
 344                 // when the request is handled.
 345                 if (inSendMessage == 0) {
 346                     // Identify which Component should initially gain focus
 347                     // in the Window.
 348                     //
 349                     // * If we're in SendMessage, then this is a synthetic
 350                     //   WINDOW_GAINED_FOCUS message which was generated by a
 351                     //   the FOCUS_GAINED handler. Allow the Component to
 352                     //   which the FOCUS_GAINED message was targeted to
 353                     //   receive the focus.
 354                     // * Otherwise, look up the correct Component here.
 355                     //   We don't use Window.getMostRecentFocusOwner because
 356                     //   window is focused now and 'null' will be returned
 357 
 358 
 359                     // Calculating of most recent focus owner and focus
 360                     // request should be synchronized on KeyboardFocusManager.class
 361                     // to prevent from thread race when user will request
 362                     // focus between calculation and our request.
 363                     // But if focus transfer is synchronous, this synchronization
 364                     // may cause deadlock, thus we don't synchronize this block.
 365                     Component toFocus = KeyboardFocusManager.
 366                         getMostRecentFocusOwner(newFocusedWindow);
 367                     if ((toFocus == null) &&
 368                         newFocusedWindow.isFocusableWindow())
 369                     {
 370                         toFocus = newFocusedWindow.getFocusTraversalPolicy().
 371                             getInitialComponent(newFocusedWindow);
 372                     }
 373                     Component tempLost = null;
 374                     synchronized(KeyboardFocusManager.class) {
 375                         tempLost = newFocusedWindow.setTemporaryLostComponent(null);
 376                     }
 377 
 378                     // The component which last has the focus when this window was focused
 379                     // should receive focus first
 380                     if (focusLog.isLoggable(PlatformLogger.FINER)) {
 381                         focusLog.finer("tempLost {0}, toFocus {1}",
 382                                        tempLost, toFocus);
 383                     }
 384                     if (tempLost != null) {
 385                         tempLost.requestFocusInWindow(CausedFocusEvent.Cause.ACTIVATION);
 386                     }
 387 
 388                     if (toFocus != null && toFocus != tempLost) {
 389                         // If there is a component which requested focus when this window
 390                         // was inactive it expects to receive focus after activation.
 391                         toFocus.requestFocusInWindow(CausedFocusEvent.Cause.ACTIVATION);
 392                     }
 393                 }
 394 
 395                 Window realOppositeWindow = this.realOppositeWindowWR.get();
 396                 if (realOppositeWindow != we.getOppositeWindow()) {
 397                     we = new WindowEvent(newFocusedWindow,
 398                                          WindowEvent.WINDOW_GAINED_FOCUS,
 399                                          realOppositeWindow);
 400                 }
 401                 return typeAheadAssertions(newFocusedWindow, we);
 402             }
 403 
 404             case WindowEvent.WINDOW_ACTIVATED: {
 405                 WindowEvent we = (WindowEvent)e;
 406                 Window oldActiveWindow = getGlobalActiveWindow();
 407                 Window newActiveWindow = we.getWindow();
 408                 if (oldActiveWindow == newActiveWindow) {
 409                     break;
 410                 }
 411 
 412                 // If there exists a current active window, then notify it that
 413                 // it has lost activation.
 414                 if (oldActiveWindow != null) {
 415                     boolean isEventDispatched =
 416                         sendMessage(oldActiveWindow,
 417                                 new WindowEvent(oldActiveWindow,
 418                                                 WindowEvent.WINDOW_DEACTIVATED,
 419                                                 newActiveWindow));
 420                     // Failed to dispatch, clear by ourselfves
 421                     if (!isEventDispatched) {
 422                         setGlobalActiveWindow(null);
 423                     }
 424                     if (getGlobalActiveWindow() != null) {
 425                         // Activation change was rejected. Unlikely, but
 426                         // possible.
 427                         break;
 428                     }
 429                 }
 430 
 431                 setGlobalActiveWindow(newActiveWindow);
 432 
 433                 if (newActiveWindow != getGlobalActiveWindow()) {
 434                     // Activation change was rejected. Unlikely, but
 435                     // possible.
 436                     break;
 437                 }
 438 
 439                 return typeAheadAssertions(newActiveWindow, we);
 440             }
 441 
 442             case FocusEvent.FOCUS_GAINED: {
 443                 FocusEvent fe = (FocusEvent)e;
 444                 CausedFocusEvent.Cause cause = (fe instanceof CausedFocusEvent) ?
 445                     ((CausedFocusEvent)fe).getCause() : CausedFocusEvent.Cause.UNKNOWN;
 446                 Component oldFocusOwner = getGlobalFocusOwner();
 447                 Component newFocusOwner = fe.getComponent();
 448                 if (oldFocusOwner == newFocusOwner) {
 449                     if (focusLog.isLoggable(PlatformLogger.FINE)) {
 450                         focusLog.fine("Skipping {0} because focus owner is the same", e);
 451                     }
 452                     // We can't just drop the event - there could be
 453                     // type-ahead markers associated with it.
 454                     dequeueKeyEvents(-1, newFocusOwner);
 455                     break;
 456                 }
 457 
 458                 // If there exists a current focus owner, then notify it that
 459                 // it has lost focus.
 460                 if (oldFocusOwner != null) {
 461                     boolean isEventDispatched =
 462                         sendMessage(oldFocusOwner,
 463                                     new CausedFocusEvent(oldFocusOwner,
 464                                                    FocusEvent.FOCUS_LOST,
 465                                                    fe.isTemporary(),
 466                                                    newFocusOwner, cause));
 467                     // Failed to dispatch, clear by ourselfves
 468                     if (!isEventDispatched) {
 469                         setGlobalFocusOwner(null);
 470                         if (!fe.isTemporary()) {
 471                             setGlobalPermanentFocusOwner(null);
 472                         }
 473                     }
 474                 }
 475 
 476                 // Because the native windowing system has a different notion
 477                 // of the current focus and activation states, it is possible
 478                 // that a Component outside of the focused Window receives a
 479                 // FOCUS_GAINED event. We synthesize a WINDOW_GAINED_FOCUS
 480                 // event in that case.
 481                 final Window newFocusedWindow = SunToolkit.getContainingWindow(newFocusOwner);
 482                 final Window currentFocusedWindow = getGlobalFocusedWindow();
 483                 if (newFocusedWindow != null &&
 484                     newFocusedWindow != currentFocusedWindow)
 485                 {
 486                     sendMessage(newFocusedWindow,
 487                                 new WindowEvent(newFocusedWindow,
 488                                         WindowEvent.WINDOW_GAINED_FOCUS,
 489                                                 currentFocusedWindow));
 490                     if (newFocusedWindow != getGlobalFocusedWindow()) {
 491                         // Focus change was rejected. Will happen if
 492                         // newFocusedWindow is not a focusable Window.
 493 
 494                         // Need to recover type-ahead, but don't bother
 495                         // restoring focus. That was done by the
 496                         // WINDOW_GAINED_FOCUS handler
 497                         dequeueKeyEvents(-1, newFocusOwner);
 498                         break;
 499                     }
 500                 }
 501 
 502                 if (!(newFocusOwner.isFocusable() && newFocusOwner.isShowing() &&
 503                     // Refuse focus on a disabled component if the focus event
 504                     // isn't of UNKNOWN reason (i.e. not a result of a direct request
 505                     // but traversal, activation or system generated).
 506                     (newFocusOwner.isEnabled() || cause.equals(CausedFocusEvent.Cause.UNKNOWN))))
 507                 {
 508                     // we should not accept focus on such component, so reject it.
 509                     dequeueKeyEvents(-1, newFocusOwner);
 510                     if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
 511                         // If FOCUS_GAINED is for a disposed component (however
 512                         // it shouldn't happen) its toplevel parent is null. In this
 513                         // case we have to try to restore focus in the current focused
 514                         // window (for the details: 6607170).
 515                         if (newFocusedWindow == null) {
 516                             restoreFocus(fe, currentFocusedWindow);
 517                         } else {
 518                             restoreFocus(fe, newFocusedWindow);
 519                         }
 520                     }
 521                     break;
 522                 }
 523 
 524                 setGlobalFocusOwner(newFocusOwner);
 525 
 526                 if (newFocusOwner != getGlobalFocusOwner()) {
 527                     // Focus change was rejected. Will happen if
 528                     // newFocusOwner is not focus traversable.
 529                     dequeueKeyEvents(-1, newFocusOwner);
 530                     if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
 531                         restoreFocus(fe, (Window)newFocusedWindow);
 532                     }
 533                     break;
 534                 }
 535 
 536                 if (!fe.isTemporary()) {
 537                     setGlobalPermanentFocusOwner(newFocusOwner);
 538 
 539                     if (newFocusOwner != getGlobalPermanentFocusOwner()) {
 540                         // Focus change was rejected. Unlikely, but possible.
 541                         dequeueKeyEvents(-1, newFocusOwner);
 542                         if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
 543                             restoreFocus(fe, (Window)newFocusedWindow);
 544                         }
 545                         break;
 546                     }
 547                 }
 548 
 549                 setNativeFocusOwner(getHeavyweight(newFocusOwner));
 550 
 551                 Component realOppositeComponent = this.realOppositeComponentWR.get();
 552                 if (realOppositeComponent != null &&
 553                     realOppositeComponent != fe.getOppositeComponent()) {
 554                     fe = new CausedFocusEvent(newFocusOwner,
 555                                         FocusEvent.FOCUS_GAINED,
 556                                         fe.isTemporary(),
 557                                         realOppositeComponent, cause);
 558                     ((AWTEvent) fe).isPosted = true;
 559                 }
 560                 return typeAheadAssertions(newFocusOwner, fe);
 561             }
 562 
 563             case FocusEvent.FOCUS_LOST: {
 564                 FocusEvent fe = (FocusEvent)e;
 565                 Component currentFocusOwner = getGlobalFocusOwner();
 566                 if (currentFocusOwner == null) {
 567                     if (focusLog.isLoggable(PlatformLogger.FINE))
 568                         focusLog.fine("Skipping {0} because focus owner is null", e);
 569                     break;
 570                 }
 571                 // Ignore cases where a Component loses focus to itself.
 572                 // If we make a mistake because of retargeting, then the
 573                 // FOCUS_GAINED handler will correct it.
 574                 if (currentFocusOwner == fe.getOppositeComponent()) {
 575                     if (focusLog.isLoggable(PlatformLogger.FINE))
 576                         focusLog.fine("Skipping {0} because current focus owner is equal to opposite", e);
 577                     break;
 578                 }
 579 
 580                 setGlobalFocusOwner(null);
 581 
 582                 if (getGlobalFocusOwner() != null) {
 583                     // Focus change was rejected. Unlikely, but possible.
 584                     restoreFocus(currentFocusOwner, true);
 585                     break;
 586                 }
 587 
 588                 if (!fe.isTemporary()) {
 589                     setGlobalPermanentFocusOwner(null);
 590 
 591                     if (getGlobalPermanentFocusOwner() != null) {
 592                         // Focus change was rejected. Unlikely, but possible.
 593                         restoreFocus(currentFocusOwner, true);
 594                         break;
 595                     }
 596                 } else {
 597                     Window owningWindow = currentFocusOwner.getContainingWindow();
 598                     if (owningWindow != null) {
 599                         owningWindow.setTemporaryLostComponent(currentFocusOwner);
 600                     }
 601                 }
 602 
 603                 setNativeFocusOwner(null);
 604 
 605                 fe.setSource(currentFocusOwner);
 606 
 607                 realOppositeComponentWR = (fe.getOppositeComponent() != null)
 608                     ? new WeakReference<Component>(currentFocusOwner)
 609                     : NULL_COMPONENT_WR;
 610 
 611                 return typeAheadAssertions(currentFocusOwner, fe);
 612             }
 613 
 614             case WindowEvent.WINDOW_DEACTIVATED: {
 615                 WindowEvent we = (WindowEvent)e;
 616                 Window currentActiveWindow = getGlobalActiveWindow();
 617                 if (currentActiveWindow == null) {
 618                     break;
 619                 }
 620 
 621                 if (currentActiveWindow != e.getSource()) {
 622                     // The event is lost in time.
 623                     // Allow listeners to precess the event but do not
 624                     // change any global states
 625                     break;
 626                 }
 627 
 628                 setGlobalActiveWindow(null);
 629                 if (getGlobalActiveWindow() != null) {
 630                     // Activation change was rejected. Unlikely, but possible.
 631                     break;
 632                 }
 633 
 634                 we.setSource(currentActiveWindow);
 635                 return typeAheadAssertions(currentActiveWindow, we);
 636             }
 637 
 638             case WindowEvent.WINDOW_LOST_FOCUS: {




 639                 WindowEvent we = (WindowEvent)e;
 640                 Window currentFocusedWindow = getGlobalFocusedWindow();
 641                 Window losingFocusWindow = we.getWindow();
 642                 Window activeWindow = getGlobalActiveWindow();
 643                 Window oppositeWindow = we.getOppositeWindow();
 644                 if (focusLog.isLoggable(PlatformLogger.FINE))
 645                     focusLog.fine("Active {0}, Current focused {1}, losing focus {2} opposite {3}",
 646                                   activeWindow, currentFocusedWindow,
 647                                   losingFocusWindow, oppositeWindow);
 648                 if (currentFocusedWindow == null) {
 649                     break;
 650                 }
 651 
 652                 // Special case -- if the native windowing system posts an
 653                 // event claiming that the active Window has lost focus to the
 654                 // focused Window, then discard the event. This is an artifact
 655                 // of the native windowing system not knowing which Window is
 656                 // really focused.
 657                 if (inSendMessage == 0 && losingFocusWindow == activeWindow &&
 658                     oppositeWindow == currentFocusedWindow)
 659                 {
 660                     break;
 661                 }
 662 
 663                 Component currentFocusOwner = getGlobalFocusOwner();
 664                 if (currentFocusOwner != null) {
 665                     // The focus owner should always receive a FOCUS_LOST event
 666                     // before the Window is defocused.
 667                     Component oppositeComp = null;
 668                     if (oppositeWindow != null) {
 669                         oppositeComp = oppositeWindow.getTemporaryLostComponent();
 670                         if (oppositeComp == null) {
 671                             oppositeComp = oppositeWindow.getMostRecentFocusOwner();
 672                         }
 673                     }
 674                     if (oppositeComp == null) {
 675                         oppositeComp = oppositeWindow;
 676                     }
 677                     sendMessage(currentFocusOwner,
 678                                 new CausedFocusEvent(currentFocusOwner,
 679                                                FocusEvent.FOCUS_LOST,
 680                                                true,
 681                                                oppositeComp, CausedFocusEvent.Cause.ACTIVATION));
 682                 }
 683 
 684                 setGlobalFocusedWindow(null);
 685                 if (getGlobalFocusedWindow() != null) {
 686                     // Focus change was rejected. Unlikely, but possible.
 687                     restoreFocus(currentFocusedWindow, null, true);
 688                     break;
 689                 }
 690 
 691                 we.setSource(currentFocusedWindow);
 692                 realOppositeWindowWR = (oppositeWindow != null)
 693                     ? new WeakReference<Window>(currentFocusedWindow)
 694                     : NULL_WINDOW_WR;
 695                 typeAheadAssertions(currentFocusedWindow, we);
 696 
 697                 if (oppositeWindow == null) {
 698                     // Then we need to deactive the active Window as well.
 699                     // No need to synthesize in other cases, because
 700                     // WINDOW_ACTIVATED will handle it if necessary.
 701                     sendMessage(activeWindow,
 702                                 new WindowEvent(activeWindow,
 703                                                 WindowEvent.WINDOW_DEACTIVATED,
 704                                                 null));
 705                     if (getGlobalActiveWindow() != null) {
 706                         // Activation change was rejected. Unlikely,
 707                         // but possible.
 708                         restoreFocus(currentFocusedWindow, null, true);
 709                     }
 710                 }
 711                 break;
 712             }
 713 
 714             case KeyEvent.KEY_TYPED:
 715             case KeyEvent.KEY_PRESSED:
 716             case KeyEvent.KEY_RELEASED:
 717                 return typeAheadAssertions(null, e);
 718 
 719             default:
 720                 return false;
 721         }
 722 
 723         return true;
 724     }
 725 
 726     /**
 727      * Called by <code>dispatchEvent</code> if no other
 728      * KeyEventDispatcher in the dispatcher chain dispatched the KeyEvent, or
 729      * if no other KeyEventDispatchers are registered. If the event has not
 730      * been consumed, its target is enabled, and the focus owner is not null,
 731      * this method dispatches the event to its target. This method will also
 732      * subsequently dispatch the event to all registered
 733      * KeyEventPostProcessors. After all this operations are finished,
 734      * the event is passed to peers for processing.
 735      * <p>
 736      * In all cases, this method returns <code>true</code>, since
 737      * DefaultKeyboardFocusManager is designed so that neither
 738      * <code>dispatchEvent</code>, nor the AWT event dispatcher, should take
 739      * further action on the event in any situation.
 740      *
 741      * @param e the KeyEvent to be dispatched
 742      * @return <code>true</code>
 743      * @see Component#dispatchEvent
 744      */
 745     public boolean dispatchKeyEvent(KeyEvent e) {
 746         Component focusOwner = (((AWTEvent)e).isPosted) ? getFocusOwner() : e.getComponent();
 747 
 748         if (focusOwner != null && focusOwner.isShowing() && focusOwner.canBeFocusOwner()) {
 749             if (!e.isConsumed()) {
 750                 Component comp = e.getComponent();
 751                 if (comp != null && comp.isEnabled()) {
 752                     redispatchEvent(comp, e);
 753                 }
 754             }
 755         }
 756         boolean stopPostProcessing = false;
 757         java.util.List processors = getKeyEventPostProcessors();
 758         if (processors != null) {
 759             for (java.util.Iterator iter = processors.iterator();
 760                  !stopPostProcessing && iter.hasNext(); )
 761             {
 762                 stopPostProcessing = (((KeyEventPostProcessor)(iter.next())).
 763                             postProcessKeyEvent(e));
 764             }
 765         }
 766         if (!stopPostProcessing) {
 767             postProcessKeyEvent(e);
 768         }
 769 
 770         // Allow the peer to process KeyEvent
 771         Component source = e.getComponent();
 772         ComponentPeer peer = source.getPeer();
 773 
 774         if (peer == null || peer instanceof LightweightPeer) {
 775             // if focus owner is lightweight then its native container
 776             // processes event
 777             Container target = source.getNativeContainer();
 778             if (target != null) {
 779                 peer = target.getPeer();
 780             }
 781         }
 782         if (peer != null) {
 783             peer.handleEvent(e);
 784         }
 785 
 786         return true;
 787     }
 788 
 789     /**
 790      * This method will be called by <code>dispatchKeyEvent</code>. It will
 791      * handle any unconsumed KeyEvents that map to an AWT
 792      * <code>MenuShortcut</code> by consuming the event and activating the
 793      * shortcut.
 794      *
 795      * @param e the KeyEvent to post-process
 796      * @return <code>true</code>
 797      * @see #dispatchKeyEvent
 798      * @see MenuShortcut
 799      */
 800     public boolean postProcessKeyEvent(KeyEvent e) {
 801         if (!e.isConsumed()) {
 802             Component target = e.getComponent();
 803             Container p = (Container)
 804                 (target instanceof Container ? target : target.getParent());
 805             if (p != null) {
 806                 p.postProcessKeyEvent(e);
 807             }
 808         }
 809         return true;
 810     }
 811 
 812     private void pumpApprovedKeyEvents() {
 813         KeyEvent ke;
 814         do {
 815             ke = null;
 816             synchronized (this) {
 817                 if (enqueuedKeyEvents.size() != 0) {
 818                     ke = (KeyEvent)enqueuedKeyEvents.getFirst();
 819                     if (typeAheadMarkers.size() != 0) {
 820                         TypeAheadMarker marker = (TypeAheadMarker)
 821                             typeAheadMarkers.getFirst();
 822                         // Fixed 5064013: may appears that the events have the same time
 823                         // if (ke.getWhen() >= marker.after) {
 824                         // The fix is rolled out.
 825 
 826                         if (ke.getWhen() > marker.after) {
 827                             ke = null;
 828                         }
 829                     }
 830                     if (ke != null) {
 831                         focusLog.finer("Pumping approved event {0}", ke);
 832                         enqueuedKeyEvents.removeFirst();
 833                     }
 834                 }
 835             }
 836             if (ke != null) {
 837                 preDispatchKeyEvent(ke);
 838             }
 839         } while (ke != null);
 840     }
 841 
 842     /**
 843      * Dumps the list of type-ahead queue markers to stderr
 844      */
 845     void dumpMarkers() {
 846         if (focusLog.isLoggable(PlatformLogger.FINEST)) {
 847             focusLog.finest(">>> Markers dump, time: {0}", System.currentTimeMillis());
 848             synchronized (this) {
 849                 if (typeAheadMarkers.size() != 0) {
 850                     Iterator iter = typeAheadMarkers.iterator();
 851                     while (iter.hasNext()) {
 852                         TypeAheadMarker marker = (TypeAheadMarker)iter.next();
 853                         focusLog.finest("    {0}", marker);
 854                     }
 855                 }
 856             }
 857         }
 858     }
 859 
 860     private boolean typeAheadAssertions(Component target, AWTEvent e) {
 861 
 862         // Clear any pending events here as well as in the FOCUS_GAINED
 863         // handler. We need this call here in case a marker was removed in
 864         // response to a call to dequeueKeyEvents.
 865         pumpApprovedKeyEvents();
 866 
 867         switch (e.getID()) {
 868             case KeyEvent.KEY_TYPED:
 869             case KeyEvent.KEY_PRESSED:
 870             case KeyEvent.KEY_RELEASED: {
 871                 KeyEvent ke = (KeyEvent)e;
 872                 synchronized (this) {
 873                     if (e.isPosted && typeAheadMarkers.size() != 0) {
 874                         TypeAheadMarker marker = (TypeAheadMarker)
 875                             typeAheadMarkers.getFirst();
 876                         // Fixed 5064013: may appears that the events have the same time
 877                         // if (ke.getWhen() >= marker.after) {
 878                         // The fix is rolled out.
 879 
 880                         if (ke.getWhen() > marker.after) {
 881                             focusLog.finer("Storing event {0} because of marker {1}", ke, marker);
 882                             enqueuedKeyEvents.addLast(ke);
 883                             return true;
 884                         }
 885                     }
 886                 }
 887 
 888                 // KeyEvent was posted before focus change request
 889                 return preDispatchKeyEvent(ke);
 890             }
 891 
 892             case FocusEvent.FOCUS_GAINED:
 893                 focusLog.finest("Markers before FOCUS_GAINED on {0}", target);
 894                 dumpMarkers();
 895                 // Search the marker list for the first marker tied to
 896                 // the Component which just gained focus. Then remove
 897                 // that marker, any markers which immediately follow
 898                 // and are tied to the same component, and all markers
 899                 // that preceed it. This handles the case where
 900                 // multiple focus requests were made for the same
 901                 // Component in a row and when we lost some of the
 902                 // earlier requests. Since FOCUS_GAINED events will
 903                 // not be generated for these additional requests, we
 904                 // need to clear those markers too.
 905                 synchronized (this) {
 906                     boolean found = false;
 907                     if (hasMarker(target)) {
 908                         for (Iterator iter = typeAheadMarkers.iterator();
 909                              iter.hasNext(); )
 910                         {
 911                             if (((TypeAheadMarker)iter.next()).untilFocused ==
 912                                 target)
 913                             {
 914                                 found = true;
 915                             } else if (found) {
 916                                 break;
 917                             }
 918                             iter.remove();
 919                         }
 920                     } else {
 921                         // Exception condition - event without marker
 922                         focusLog.finer("Event without marker {0}", e);
 923                     }
 924                 }
 925                 focusLog.finest("Markers after FOCUS_GAINED");
 926                 dumpMarkers();
 927 
 928                 redispatchEvent(target, e);
 929 
 930                 // Now, dispatch any pending KeyEvents which have been
 931                 // released because of the FOCUS_GAINED event so that we don't
 932                 // have to wait for another event to be posted to the queue.
 933                 pumpApprovedKeyEvents();
 934                 return true;
 935 
 936             default:
 937                 redispatchEvent(target, e);
 938                 return true;
 939         }
 940     }
 941 
 942     /**
 943      * Returns true if there are some marker associated with component <code>comp</code>
 944      * in a markers' queue
 945      * @since 1.5
 946      */
 947     private boolean hasMarker(Component comp) {
 948         for (Iterator iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
 949             if (((TypeAheadMarker)iter.next()).untilFocused == comp) {
 950                 return true;
 951             }
 952         }
 953         return false;
 954     }
 955 
 956     /**
 957      * Clears markers queue
 958      * @since 1.5
 959      */
 960     void clearMarkers() {
 961         synchronized(this) {
 962             typeAheadMarkers.clear();
 963         }
 964     }
 965 
 966     private boolean preDispatchKeyEvent(KeyEvent ke) {
 967         if (((AWTEvent) ke).isPosted) {
 968             Component focusOwner = getFocusOwner();
 969             ke.setSource(((focusOwner != null) ? focusOwner : getFocusedWindow()));
 970         }
 971         if (ke.getSource() == null) {
 972             return true;
 973         }
 974 
 975         // Explicitly set the current event and most recent timestamp here in
 976         // addition to the call in Component.dispatchEventImpl. Because
 977         // KeyEvents can be delivered in response to a FOCUS_GAINED event, the
 978         // current timestamp may be incorrect. We need to set it here so that
 979         // KeyEventDispatchers will use the correct time.
 980         EventQueue.setCurrentEventAndMostRecentTime(ke);
 981 
 982         /**
 983          * Fix for 4495473.
 984          * This fix allows to correctly dispatch events when native
 985          * event proxying mechanism is active.
 986          * If it is active we should redispatch key events after
 987          * we detected its correct target.
 988          */
 989         if (KeyboardFocusManager.isProxyActive(ke)) {
 990             Component source = (Component)ke.getSource();
 991             Container target = source.getNativeContainer();
 992             if (target != null) {
 993                 ComponentPeer peer = target.getPeer();
 994                 if (peer != null) {
 995                     peer.handleEvent(ke);
 996                     /**
 997                      * Fix for 4478780 - consume event after it was dispatched by peer.
 998                      */
 999                     ke.consume();
1000                 }
1001             }
1002             return true;
1003         }
1004 
1005         java.util.List dispatchers = getKeyEventDispatchers();
1006         if (dispatchers != null) {
1007             for (java.util.Iterator iter = dispatchers.iterator();
1008                  iter.hasNext(); )
1009              {
1010                  if (((KeyEventDispatcher)(iter.next())).
1011                      dispatchKeyEvent(ke))
1012                  {
1013                      return true;
1014                  }
1015              }
1016         }
1017         return dispatchKeyEvent(ke);
1018     }
1019 
1020     /*
1021      * @param e is a KEY_PRESSED event that can be used
1022      *          to track the next KEY_TYPED related.
1023      */
1024     private void consumeNextKeyTyped(KeyEvent e) {
1025         consumeNextKeyTyped = true;
1026     }
1027 
1028     private void consumeTraversalKey(KeyEvent e) {
1029         e.consume();
1030         consumeNextKeyTyped = (e.getID() == KeyEvent.KEY_PRESSED) &&
1031                               !e.isActionKey();
1032     }
1033 
1034     /*
1035      * return true if event was consumed
1036      */
1037     private boolean consumeProcessedKeyEvent(KeyEvent e) {
1038         if ((e.getID() == KeyEvent.KEY_TYPED) && consumeNextKeyTyped) {
1039             e.consume();
1040             consumeNextKeyTyped = false;
1041             return true;
1042         }
1043         return false;
1044     }
1045 
1046     /**
1047      * This method initiates a focus traversal operation if and only if the
1048      * KeyEvent represents a focus traversal key for the specified
1049      * focusedComponent. It is expected that focusedComponent is the current
1050      * focus owner, although this need not be the case. If it is not,
1051      * focus traversal will nevertheless proceed as if focusedComponent
1052      * were the focus owner.
1053      *
1054      * @param focusedComponent the Component that is the basis for a focus
1055      *        traversal operation if the specified event represents a focus
1056      *        traversal key for the Component
1057      * @param e the event that may represent a focus traversal key
1058      */
1059     public void processKeyEvent(Component focusedComponent, KeyEvent e) {
1060         // consume processed event if needed
1061         if (consumeProcessedKeyEvent(e)) {
1062             return;
1063         }
1064 
1065         // KEY_TYPED events cannot be focus traversal keys
1066         if (e.getID() == KeyEvent.KEY_TYPED) {
1067             return;
1068         }
1069 
1070         if (focusedComponent.getFocusTraversalKeysEnabled() &&
1071             !e.isConsumed())
1072         {
1073             AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e),
1074                 oppStroke = AWTKeyStroke.getAWTKeyStroke(stroke.getKeyCode(),
1075                                                  stroke.getModifiers(),
1076                                                  !stroke.isOnKeyRelease());
1077             Set toTest;
1078             boolean contains, containsOpp;
1079 
1080             toTest = focusedComponent.getFocusTraversalKeys(
1081                 KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
1082             contains = toTest.contains(stroke);
1083             containsOpp = toTest.contains(oppStroke);
1084 
1085             if (contains || containsOpp) {
1086                 consumeTraversalKey(e);
1087                 if (contains) {
1088                     focusNextComponent(focusedComponent);
1089                 }
1090                 return;
1091             } else if (e.getID() == KeyEvent.KEY_PRESSED) {
1092                 // Fix for 6637607: consumeNextKeyTyped should be reset.
1093                 consumeNextKeyTyped = false;
1094             }
1095 
1096             toTest = focusedComponent.getFocusTraversalKeys(
1097                 KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
1098             contains = toTest.contains(stroke);
1099             containsOpp = toTest.contains(oppStroke);
1100 
1101             if (contains || containsOpp) {
1102                 consumeTraversalKey(e);
1103                 if (contains) {
1104                     focusPreviousComponent(focusedComponent);
1105                 }
1106                 return;
1107             }
1108 
1109             toTest = focusedComponent.getFocusTraversalKeys(
1110                 KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
1111             contains = toTest.contains(stroke);
1112             containsOpp = toTest.contains(oppStroke);
1113 
1114             if (contains || containsOpp) {
1115                 consumeTraversalKey(e);
1116                 if (contains) {
1117                     upFocusCycle(focusedComponent);
1118                 }
1119                 return;
1120             }
1121 
1122             if (!((focusedComponent instanceof Container) &&
1123                   ((Container)focusedComponent).isFocusCycleRoot())) {
1124                 return;
1125             }
1126 
1127             toTest = focusedComponent.getFocusTraversalKeys(
1128                 KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
1129             contains = toTest.contains(stroke);
1130             containsOpp = toTest.contains(oppStroke);
1131 
1132             if (contains || containsOpp) {
1133                 consumeTraversalKey(e);
1134                 if (contains) {
1135                     downFocusCycle((Container)focusedComponent);
1136                 }
1137             }
1138         }
1139     }
1140 
1141     /**
1142      * Delays dispatching of KeyEvents until the specified Component becomes
1143      * the focus owner. KeyEvents with timestamps later than the specified
1144      * timestamp will be enqueued until the specified Component receives a
1145      * FOCUS_GAINED event, or the AWT cancels the delay request by invoking
1146      * <code>dequeueKeyEvents</code> or <code>discardKeyEvents</code>.
1147      *
1148      * @param after timestamp of current event, or the current, system time if
1149      *        the current event has no timestamp, or the AWT cannot determine
1150      *        which event is currently being handled
1151      * @param untilFocused Component which will receive a FOCUS_GAINED event
1152      *        before any pending KeyEvents
1153      * @see #dequeueKeyEvents
1154      * @see #discardKeyEvents
1155      */
1156     protected synchronized void enqueueKeyEvents(long after,
1157                                                  Component untilFocused) {
1158         if (untilFocused == null) {
1159             return;
1160         }
1161 
1162         focusLog.finer("Enqueue at {0} for {1}",
1163                        after, untilFocused);
1164 
1165         int insertionIndex = 0,
1166             i = typeAheadMarkers.size();
1167         ListIterator iter = typeAheadMarkers.listIterator(i);
1168 
1169         for (; i > 0; i--) {
1170             TypeAheadMarker marker = (TypeAheadMarker)iter.previous();
1171             if (marker.after <= after) {
1172                 insertionIndex = i;
1173                 break;
1174             }
1175         }
1176 
1177         typeAheadMarkers.add(insertionIndex,
1178                              new TypeAheadMarker(after, untilFocused));
1179     }
1180 
1181     /**
1182      * Releases for normal dispatching to the current focus owner all
1183      * KeyEvents which were enqueued because of a call to
1184      * <code>enqueueKeyEvents</code> with the same timestamp and Component.
1185      * If the given timestamp is less than zero, the outstanding enqueue
1186      * request for the given Component with the <b>oldest</b> timestamp (if
1187      * any) should be cancelled.
1188      *
1189      * @param after the timestamp specified in the call to
1190      *        <code>enqueueKeyEvents</code>, or any value < 0
1191      * @param untilFocused the Component specified in the call to
1192      *        <code>enqueueKeyEvents</code>
1193      * @see #enqueueKeyEvents
1194      * @see #discardKeyEvents
1195      */
1196     protected synchronized void dequeueKeyEvents(long after,
1197                                                  Component untilFocused) {
1198         if (untilFocused == null) {
1199             return;
1200         }
1201 
1202         focusLog.finer("Dequeue at {0} for {1}",
1203                        after, untilFocused);
1204 
1205         TypeAheadMarker marker;
1206         ListIterator iter = typeAheadMarkers.listIterator
1207             ((after >= 0) ? typeAheadMarkers.size() : 0);
1208 
1209         if (after < 0) {
1210             while (iter.hasNext()) {
1211                 marker = (TypeAheadMarker)iter.next();
1212                 if (marker.untilFocused == untilFocused)
1213                 {
1214                     iter.remove();
1215                     return;
1216                 }
1217             }
1218         } else {
1219             while (iter.hasPrevious()) {
1220                 marker = (TypeAheadMarker)iter.previous();
1221                 if (marker.untilFocused == untilFocused &&
1222                     marker.after == after)
1223                 {
1224                     iter.remove();
1225                     return;
1226                 }
1227             }
1228         }
1229     }
1230 
1231     /**
1232      * Discards all KeyEvents which were enqueued because of one or more calls
1233      * to <code>enqueueKeyEvents</code> with the specified Component, or one of
1234      * its descendants.
1235      *
1236      * @param comp the Component specified in one or more calls to
1237      *        <code>enqueueKeyEvents</code>, or a parent of such a Component
1238      * @see #enqueueKeyEvents
1239      * @see #dequeueKeyEvents
1240      */
1241     protected synchronized void discardKeyEvents(Component comp) {
1242         if (comp == null) {
1243             return;
1244         }
1245 
1246         long start = -1;
1247 
1248         for (Iterator iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {
1249             TypeAheadMarker marker = (TypeAheadMarker)iter.next();
1250             Component toTest = marker.untilFocused;
1251             boolean match = (toTest == comp);
1252             while (!match && toTest != null && !(toTest instanceof Window)) {
1253                 toTest = toTest.getParent();
1254                 match = (toTest == comp);
1255             }
1256             if (match) {
1257                 if (start < 0) {
1258                     start = marker.after;
1259                 }
1260                 iter.remove();
1261             } else if (start >= 0) {
1262                 purgeStampedEvents(start, marker.after);
1263                 start = -1;
1264             }
1265         }
1266 
1267         purgeStampedEvents(start, -1);
1268     }
1269 
1270     // Notes:
1271     //   * must be called inside a synchronized block
1272     //   * if 'start' is < 0, then this function does nothing
1273     //   * if 'end' is < 0, then all KeyEvents from 'start' to the end of the
1274     //     queue will be removed
1275     private void purgeStampedEvents(long start, long end) {
1276         if (start < 0) {
1277             return;
1278         }
1279 
1280         for (Iterator iter = enqueuedKeyEvents.iterator(); iter.hasNext(); ) {
1281             KeyEvent ke = (KeyEvent)iter.next();
1282             long time = ke.getWhen();
1283 
1284             if (start < time && (end < 0 || time <= end)) {
1285                 iter.remove();
1286             }
1287 
1288             if (end >= 0 && time > end) {
1289                 break;
1290             }
1291         }
1292     }
1293 
1294     /**
1295      * Focuses the Component before aComponent, typically based on a
1296      * FocusTraversalPolicy.
1297      *
1298      * @param aComponent the Component that is the basis for the focus
1299      *        traversal operation
1300      * @see FocusTraversalPolicy
1301      * @see Component#transferFocusBackward
1302      */
1303     public void focusPreviousComponent(Component aComponent) {
1304         if (aComponent != null) {
1305             aComponent.transferFocusBackward();
1306         }
1307     }
1308 
1309     /**
1310      * Focuses the Component after aComponent, typically based on a
1311      * FocusTraversalPolicy.
1312      *
1313      * @param aComponent the Component that is the basis for the focus
1314      *        traversal operation
1315      * @see FocusTraversalPolicy
1316      * @see Component#transferFocus
1317      */
1318     public void focusNextComponent(Component aComponent) {
1319         if (aComponent != null) {
1320             aComponent.transferFocus();
1321         }
1322     }
1323 
1324     /**
1325      * Moves the focus up one focus traversal cycle. Typically, the focus owner
1326      * is set to aComponent's focus cycle root, and the current focus cycle
1327      * root is set to the new focus owner's focus cycle root. If, however,
1328      * aComponent's focus cycle root is a Window, then the focus owner is set
1329      * to the focus cycle root's default Component to focus, and the current
1330      * focus cycle root is unchanged.
1331      *
1332      * @param aComponent the Component that is the basis for the focus
1333      *        traversal operation
1334      * @see Component#transferFocusUpCycle
1335      */
1336     public void upFocusCycle(Component aComponent) {
1337         if (aComponent != null) {
1338             aComponent.transferFocusUpCycle();
1339         }
1340     }
1341 
1342     /**
1343      * Moves the focus down one focus traversal cycle. If aContainer is a focus
1344      * cycle root, then the focus owner is set to aContainer's default
1345      * Component to focus, and the current focus cycle root is set to
1346      * aContainer. If aContainer is not a focus cycle root, then no focus
1347      * traversal operation occurs.
1348      *
1349      * @param aContainer the Container that is the basis for the focus
1350      *        traversal operation
1351      * @see Container#transferFocusDownCycle
1352      */
1353     public void downFocusCycle(Container aContainer) {
1354         if (aContainer != null && aContainer.isFocusCycleRoot()) {
1355             aContainer.transferFocusDownCycle();
1356         }
1357     }
1358 }
--- EOF ---