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