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