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