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