Path: blob/master/src/java.desktop/share/classes/java/awt/DefaultKeyboardFocusManager.java
41152 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.awt;2627import java.awt.event.FocusEvent;28import java.awt.event.KeyEvent;29import java.awt.event.WindowEvent;30import java.awt.peer.ComponentPeer;31import java.awt.peer.LightweightPeer;32import java.io.Serial;33import java.lang.ref.WeakReference;34import java.security.AccessController;35import java.security.PrivilegedAction;36import java.util.Iterator;37import java.util.LinkedList;38import java.util.ListIterator;39import java.util.Set;4041import sun.awt.AWTAccessor;42import sun.awt.AppContext;43import sun.awt.SunToolkit;44import sun.awt.TimedWindowEvent;45import sun.util.logging.PlatformLogger;4647/**48* The default KeyboardFocusManager for AWT applications. Focus traversal is49* done in response to a Component's focus traversal keys, and using a50* Container's FocusTraversalPolicy.51* <p>52* Please see53* <a href="https://docs.oracle.com/javase/tutorial/uiswing/misc/focus.html">54* How to Use the Focus Subsystem</a>,55* a section in <em>The Java Tutorial</em>, and the56* <a href="doc-files/FocusSpec.html">Focus Specification</a>57* for more information.58*59* @author David Mendenhall60*61* @see FocusTraversalPolicy62* @see Component#setFocusTraversalKeys63* @see Component#getFocusTraversalKeys64* @since 1.465*/66public class DefaultKeyboardFocusManager extends KeyboardFocusManager {67private static final PlatformLogger focusLog = PlatformLogger.getLogger("java.awt.focus.DefaultKeyboardFocusManager");6869// null weak references to not create too many objects70private static final WeakReference<Window> NULL_WINDOW_WR =71new WeakReference<Window>(null);72private static final WeakReference<Component> NULL_COMPONENT_WR =73new WeakReference<Component>(null);74private WeakReference<Window> realOppositeWindowWR = NULL_WINDOW_WR;75private WeakReference<Component> realOppositeComponentWR = NULL_COMPONENT_WR;76private int inSendMessage;77private LinkedList<KeyEvent> enqueuedKeyEvents = new LinkedList<KeyEvent>();78private LinkedList<TypeAheadMarker> typeAheadMarkers = new LinkedList<TypeAheadMarker>();79private boolean consumeNextKeyTyped;80private Component restoreFocusTo;8182private static boolean fxAppThreadIsDispatchThread;8384static {85initStatic();86}8788@SuppressWarnings("removal")89private static void initStatic() {90AWTAccessor.setDefaultKeyboardFocusManagerAccessor(91new AWTAccessor.DefaultKeyboardFocusManagerAccessor() {92public void consumeNextKeyTyped(DefaultKeyboardFocusManager dkfm, KeyEvent e) {93dkfm.consumeNextKeyTyped(e);94}95});96AccessController.doPrivileged(new PrivilegedAction<Object>() {97public Object run() {98fxAppThreadIsDispatchThread =99"true".equals(System.getProperty("javafx.embed.singleThread"));100return null;101}102});103}104105/**106* Constructs a {@code DefaultKeyboardFocusManager}.107*/108public DefaultKeyboardFocusManager() {}109110private static class TypeAheadMarker {111long after;112Component untilFocused;113114TypeAheadMarker(long after, Component untilFocused) {115this.after = after;116this.untilFocused = untilFocused;117}118/**119* Returns string representation of the marker120*/121public String toString() {122return ">>> Marker after " + after + " on " + untilFocused;123}124}125126private Window getOwningFrameDialog(Window window) {127while (window != null && !(window instanceof Frame ||128window instanceof Dialog)) {129window = (Window)window.getParent();130}131return window;132}133134/*135* This series of restoreFocus methods is used for recovering from a136* rejected focus or activation change. Rejections typically occur when137* the user attempts to focus a non-focusable Component or Window.138*/139private void restoreFocus(FocusEvent fe, Window newFocusedWindow) {140Component realOppositeComponent = this.realOppositeComponentWR.get();141Component vetoedComponent = fe.getComponent();142143if (newFocusedWindow != null && restoreFocus(newFocusedWindow,144vetoedComponent, false))145{146} else if (realOppositeComponent != null &&147doRestoreFocus(realOppositeComponent, vetoedComponent, false)) {148} else if (fe.getOppositeComponent() != null &&149doRestoreFocus(fe.getOppositeComponent(), vetoedComponent, false)) {150} else {151clearGlobalFocusOwnerPriv();152}153}154private void restoreFocus(WindowEvent we) {155Window realOppositeWindow = this.realOppositeWindowWR.get();156if (realOppositeWindow != null157&& restoreFocus(realOppositeWindow, null, false))158{159// do nothing, everything is done in restoreFocus()160} else if (we.getOppositeWindow() != null &&161restoreFocus(we.getOppositeWindow(), null, false))162{163// do nothing, everything is done in restoreFocus()164} else {165clearGlobalFocusOwnerPriv();166}167}168private boolean restoreFocus(Window aWindow, Component vetoedComponent,169boolean clearOnFailure) {170restoreFocusTo = null;171Component toFocus =172KeyboardFocusManager.getMostRecentFocusOwner(aWindow);173174if (toFocus != null && toFocus != vetoedComponent) {175if (getHeavyweight(aWindow) != getNativeFocusOwner()) {176// cannot restore focus synchronously177if (!toFocus.isShowing() || !toFocus.canBeFocusOwner()) {178toFocus = toFocus.getNextFocusCandidate();179}180if (toFocus != null && toFocus != vetoedComponent) {181if (!toFocus.requestFocus(false,182FocusEvent.Cause.ROLLBACK)) {183restoreFocusTo = toFocus;184}185return true;186}187} else if (doRestoreFocus(toFocus, vetoedComponent, false)) {188return true;189}190}191if (clearOnFailure) {192clearGlobalFocusOwnerPriv();193return true;194} else {195return false;196}197}198private boolean restoreFocus(Component toFocus, boolean clearOnFailure) {199return doRestoreFocus(toFocus, null, clearOnFailure);200}201private boolean doRestoreFocus(Component toFocus, Component vetoedComponent,202boolean clearOnFailure)203{204boolean success = true;205if (toFocus != vetoedComponent && toFocus.isShowing() && toFocus.canBeFocusOwner() &&206(success = toFocus.requestFocus(false, FocusEvent.Cause.ROLLBACK)))207{208return true;209} else {210if (!success && getGlobalFocusedWindow() != SunToolkit.getContainingWindow(toFocus)) {211restoreFocusTo = toFocus;212return true;213}214Component nextFocus = toFocus.getNextFocusCandidate();215if (nextFocus != null && nextFocus != vetoedComponent &&216nextFocus.requestFocusInWindow(FocusEvent.Cause.ROLLBACK))217{218return true;219} else if (clearOnFailure) {220clearGlobalFocusOwnerPriv();221return true;222} else {223return false;224}225}226}227228/**229* A special type of SentEvent which updates a counter in the target230* KeyboardFocusManager if it is an instance of231* DefaultKeyboardFocusManager.232*/233private static class DefaultKeyboardFocusManagerSentEvent234extends SentEvent235{236/**237* Use serialVersionUID from JDK 1.6 for interoperability.238*/239@Serial240private static final long serialVersionUID = -2924743257508701758L;241242public DefaultKeyboardFocusManagerSentEvent(AWTEvent nested,243AppContext toNotify) {244super(nested, toNotify);245}246public final void dispatch() {247KeyboardFocusManager manager =248KeyboardFocusManager.getCurrentKeyboardFocusManager();249DefaultKeyboardFocusManager defaultManager =250(manager instanceof DefaultKeyboardFocusManager)251? (DefaultKeyboardFocusManager)manager252: null;253254if (defaultManager != null) {255synchronized (defaultManager) {256defaultManager.inSendMessage++;257}258}259260super.dispatch();261262if (defaultManager != null) {263synchronized (defaultManager) {264defaultManager.inSendMessage--;265}266}267}268}269270/**271* Sends a synthetic AWTEvent to a Component. If the Component is in272* the current AppContext, then the event is immediately dispatched.273* If the Component is in a different AppContext, then the event is274* posted to the other AppContext's EventQueue, and this method blocks275* until the event is handled or target AppContext is disposed.276* Returns true if successfully dispatched event, false if failed277* to dispatch.278*/279static boolean sendMessage(Component target, AWTEvent e) {280e.isPosted = true;281AppContext myAppContext = AppContext.getAppContext();282final AppContext targetAppContext = target.appContext;283final SentEvent se =284new DefaultKeyboardFocusManagerSentEvent(e, myAppContext);285286if (myAppContext == targetAppContext) {287se.dispatch();288} else {289if (targetAppContext.isDisposed()) {290return false;291}292SunToolkit.postEvent(targetAppContext, se);293if (EventQueue.isDispatchThread()) {294if (Thread.currentThread() instanceof EventDispatchThread) {295EventDispatchThread edt = (EventDispatchThread)296Thread.currentThread();297edt.pumpEvents(SentEvent.ID, new Conditional() {298public boolean evaluate() {299return !se.dispatched && !targetAppContext.isDisposed();300}301});302} else {303if (fxAppThreadIsDispatchThread) {304Thread fxCheckDispatchThread = new Thread() {305@Override306public void run() {307while (!se.dispatched && !targetAppContext.isDisposed()) {308try {309Thread.sleep(100);310} catch (InterruptedException e) {311break;312}313}314}315};316fxCheckDispatchThread.start();317try {318// check if event is dispatched or disposed319// but since this user app thread is same as320// dispatch thread in fx when run with321// javafx.embed.singleThread=true322// we do not wait infinitely to avoid deadlock323// as dispatch will ultimately be done by this thread324fxCheckDispatchThread.join(500);325} catch (InterruptedException ex) {326}327}328}329} else {330synchronized (se) {331while (!se.dispatched && !targetAppContext.isDisposed()) {332try {333se.wait(1000);334} catch (InterruptedException ie) {335break;336}337}338}339}340}341return se.dispatched;342}343344/*345* Checks if the focus window event follows key events waiting in the type-ahead346* queue (if any). This may happen when a user types ahead in the window, the client347* listeners hang EDT for a while, and the user switches b/w toplevels. In that348* case the focus window events may be dispatched before the type-ahead events349* get handled. This may lead to wrong focus behavior and in order to avoid it,350* the focus window events are reposted to the end of the event queue. See 6981400.351*/352private boolean repostIfFollowsKeyEvents(WindowEvent e) {353if (!(e instanceof TimedWindowEvent)) {354return false;355}356TimedWindowEvent we = (TimedWindowEvent)e;357long time = we.getWhen();358synchronized (this) {359KeyEvent ke = enqueuedKeyEvents.isEmpty() ? null : enqueuedKeyEvents.getFirst();360if (ke != null && time >= ke.getWhen()) {361TypeAheadMarker marker = typeAheadMarkers.isEmpty() ? null : typeAheadMarkers.getFirst();362if (marker != null) {363Window toplevel = marker.untilFocused.getContainingWindow();364// Check that the component awaiting focus belongs to365// the current focused window. See 8015454.366if (toplevel != null && toplevel.isFocused()) {367SunToolkit.postEvent(AppContext.getAppContext(), new SequencedEvent(e));368return true;369}370}371}372}373return false;374}375376/**377* This method is called by the AWT event dispatcher requesting that the378* current KeyboardFocusManager dispatch the specified event on its behalf.379* DefaultKeyboardFocusManagers dispatch all FocusEvents, all WindowEvents380* related to focus, and all KeyEvents. These events are dispatched based381* on the KeyboardFocusManager's notion of the focus owner and the focused382* and active Windows, sometimes overriding the source of the specified383* AWTEvent. If this method returns {@code false}, then the AWT event384* dispatcher will attempt to dispatch the event itself.385*386* @param e the AWTEvent to be dispatched387* @return {@code true} if this method dispatched the event;388* {@code false} otherwise389*/390public boolean dispatchEvent(AWTEvent e) {391if (focusLog.isLoggable(PlatformLogger.Level.FINE) && (e instanceof WindowEvent || e instanceof FocusEvent)) {392focusLog.fine("" + e);393}394switch (e.getID()) {395case WindowEvent.WINDOW_GAINED_FOCUS: {396if (repostIfFollowsKeyEvents((WindowEvent)e)) {397break;398}399400WindowEvent we = (WindowEvent)e;401Window oldFocusedWindow = getGlobalFocusedWindow();402Window newFocusedWindow = we.getWindow();403if (newFocusedWindow == oldFocusedWindow) {404break;405}406407if (!(newFocusedWindow.isFocusableWindow()408&& newFocusedWindow.isVisible()409&& newFocusedWindow.isDisplayable()))410{411// we can not accept focus on such window, so reject it.412restoreFocus(we);413break;414}415// If there exists a current focused window, then notify it416// that it has lost focus.417if (oldFocusedWindow != null) {418boolean isEventDispatched =419sendMessage(oldFocusedWindow,420new WindowEvent(oldFocusedWindow,421WindowEvent.WINDOW_LOST_FOCUS,422newFocusedWindow));423// Failed to dispatch, clear by ourselves424if (!isEventDispatched) {425setGlobalFocusOwner(null);426setGlobalFocusedWindow(null);427}428}429430// Because the native libraries do not post WINDOW_ACTIVATED431// events, we need to synthesize one if the active Window432// changed.433Window newActiveWindow =434getOwningFrameDialog(newFocusedWindow);435Window currentActiveWindow = getGlobalActiveWindow();436if (newActiveWindow != currentActiveWindow) {437sendMessage(newActiveWindow,438new WindowEvent(newActiveWindow,439WindowEvent.WINDOW_ACTIVATED,440currentActiveWindow));441if (newActiveWindow != getGlobalActiveWindow()) {442// Activation change was rejected. Unlikely, but443// possible.444restoreFocus(we);445break;446}447}448449setGlobalFocusedWindow(newFocusedWindow);450451if (newFocusedWindow != getGlobalFocusedWindow()) {452// Focus change was rejected. Will happen if453// newFocusedWindow is not a focusable Window.454restoreFocus(we);455break;456}457458// Restore focus to the Component which last held it. We do459// this here so that client code can override our choice in460// a WINDOW_GAINED_FOCUS handler.461//462// Make sure that the focus change request doesn't change the463// focused Window in case we are no longer the focused Window464// when the request is handled.465if (inSendMessage == 0) {466// Identify which Component should initially gain focus467// in the Window.468//469// * If we're in SendMessage, then this is a synthetic470// WINDOW_GAINED_FOCUS message which was generated by a471// the FOCUS_GAINED handler. Allow the Component to472// which the FOCUS_GAINED message was targeted to473// receive the focus.474// * Otherwise, look up the correct Component here.475// We don't use Window.getMostRecentFocusOwner because476// window is focused now and 'null' will be returned477478479// Calculating of most recent focus owner and focus480// request should be synchronized on KeyboardFocusManager.class481// to prevent from thread race when user will request482// focus between calculation and our request.483// But if focus transfer is synchronous, this synchronization484// may cause deadlock, thus we don't synchronize this block.485Component toFocus = KeyboardFocusManager.486getMostRecentFocusOwner(newFocusedWindow);487boolean isFocusRestore = restoreFocusTo != null &&488toFocus == restoreFocusTo;489if ((toFocus == null) &&490newFocusedWindow.isFocusableWindow())491{492toFocus = newFocusedWindow.getFocusTraversalPolicy().493getInitialComponent(newFocusedWindow);494}495Component tempLost = null;496synchronized(KeyboardFocusManager.class) {497tempLost = newFocusedWindow.setTemporaryLostComponent(null);498}499500// The component which last has the focus when this window was focused501// should receive focus first502if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {503focusLog.finer("tempLost {0}, toFocus {1}",504tempLost, toFocus);505}506if (tempLost != null) {507tempLost.requestFocusInWindow(508isFocusRestore && tempLost == toFocus ?509FocusEvent.Cause.ROLLBACK :510FocusEvent.Cause.ACTIVATION);511}512513if (toFocus != null && toFocus != tempLost) {514// If there is a component which requested focus when this window515// was inactive it expects to receive focus after activation.516toFocus.requestFocusInWindow(FocusEvent.Cause.ACTIVATION);517}518}519restoreFocusTo = null;520521Window realOppositeWindow = this.realOppositeWindowWR.get();522if (realOppositeWindow != we.getOppositeWindow()) {523we = new WindowEvent(newFocusedWindow,524WindowEvent.WINDOW_GAINED_FOCUS,525realOppositeWindow);526}527return typeAheadAssertions(newFocusedWindow, we);528}529530case WindowEvent.WINDOW_ACTIVATED: {531WindowEvent we = (WindowEvent)e;532Window oldActiveWindow = getGlobalActiveWindow();533Window newActiveWindow = we.getWindow();534if (oldActiveWindow == newActiveWindow) {535break;536}537538// If there exists a current active window, then notify it that539// it has lost activation.540if (oldActiveWindow != null) {541boolean isEventDispatched =542sendMessage(oldActiveWindow,543new WindowEvent(oldActiveWindow,544WindowEvent.WINDOW_DEACTIVATED,545newActiveWindow));546// Failed to dispatch, clear by ourselves547if (!isEventDispatched) {548setGlobalActiveWindow(null);549}550if (getGlobalActiveWindow() != null) {551// Activation change was rejected. Unlikely, but552// possible.553break;554}555}556557setGlobalActiveWindow(newActiveWindow);558559if (newActiveWindow != getGlobalActiveWindow()) {560// Activation change was rejected. Unlikely, but561// possible.562break;563}564565return typeAheadAssertions(newActiveWindow, we);566}567568case FocusEvent.FOCUS_GAINED: {569restoreFocusTo = null;570FocusEvent fe = (FocusEvent)e;571Component oldFocusOwner = getGlobalFocusOwner();572Component newFocusOwner = fe.getComponent();573if (oldFocusOwner == newFocusOwner) {574if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {575focusLog.fine("Skipping {0} because focus owner is the same", e);576}577// We can't just drop the event - there could be578// type-ahead markers associated with it.579dequeueKeyEvents(-1, newFocusOwner);580break;581}582583// If there exists a current focus owner, then notify it that584// it has lost focus.585if (oldFocusOwner != null) {586boolean isEventDispatched =587sendMessage(oldFocusOwner,588new FocusEvent(oldFocusOwner,589FocusEvent.FOCUS_LOST,590fe.isTemporary(),591newFocusOwner, fe.getCause()));592// Failed to dispatch, clear by ourselves593if (!isEventDispatched) {594setGlobalFocusOwner(null);595if (!fe.isTemporary()) {596setGlobalPermanentFocusOwner(null);597}598}599}600601// Because the native windowing system has a different notion602// of the current focus and activation states, it is possible603// that a Component outside of the focused Window receives a604// FOCUS_GAINED event. We synthesize a WINDOW_GAINED_FOCUS605// event in that case.606final Window newFocusedWindow = SunToolkit.getContainingWindow(newFocusOwner);607final Window currentFocusedWindow = getGlobalFocusedWindow();608if (newFocusedWindow != null &&609newFocusedWindow != currentFocusedWindow)610{611sendMessage(newFocusedWindow,612new WindowEvent(newFocusedWindow,613WindowEvent.WINDOW_GAINED_FOCUS,614currentFocusedWindow));615if (newFocusedWindow != getGlobalFocusedWindow()) {616// Focus change was rejected. Will happen if617// newFocusedWindow is not a focusable Window.618619// Need to recover type-ahead, but don't bother620// restoring focus. That was done by the621// WINDOW_GAINED_FOCUS handler622dequeueKeyEvents(-1, newFocusOwner);623break;624}625}626627if (!(newFocusOwner.isFocusable() && newFocusOwner.isShowing() &&628// Refuse focus on a disabled component if the focus event629// isn't of UNKNOWN reason (i.e. not a result of a direct request630// but traversal, activation or system generated).631(newFocusOwner.isEnabled() || fe.getCause().equals(FocusEvent.Cause.UNKNOWN))))632{633// we should not accept focus on such component, so reject it.634dequeueKeyEvents(-1, newFocusOwner);635if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {636// If FOCUS_GAINED is for a disposed component (however637// it shouldn't happen) its toplevel parent is null. In this638// case we have to try to restore focus in the current focused639// window (for the details: 6607170).640if (newFocusedWindow == null) {641restoreFocus(fe, currentFocusedWindow);642} else {643restoreFocus(fe, newFocusedWindow);644}645setMostRecentFocusOwner(newFocusedWindow, null); // see: 8013773646}647break;648}649650setGlobalFocusOwner(newFocusOwner);651652if (newFocusOwner != getGlobalFocusOwner()) {653// Focus change was rejected. Will happen if654// newFocusOwner is not focus traversable.655dequeueKeyEvents(-1, newFocusOwner);656if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {657restoreFocus(fe, newFocusedWindow);658}659break;660}661662if (!fe.isTemporary()) {663setGlobalPermanentFocusOwner(newFocusOwner);664665if (newFocusOwner != getGlobalPermanentFocusOwner()) {666// Focus change was rejected. Unlikely, but possible.667dequeueKeyEvents(-1, newFocusOwner);668if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {669restoreFocus(fe, newFocusedWindow);670}671break;672}673}674675setNativeFocusOwner(getHeavyweight(newFocusOwner));676677Component realOppositeComponent = this.realOppositeComponentWR.get();678if (realOppositeComponent != null &&679realOppositeComponent != fe.getOppositeComponent()) {680fe = new FocusEvent(newFocusOwner,681FocusEvent.FOCUS_GAINED,682fe.isTemporary(),683realOppositeComponent, fe.getCause());684((AWTEvent) fe).isPosted = true;685}686return typeAheadAssertions(newFocusOwner, fe);687}688689case FocusEvent.FOCUS_LOST: {690FocusEvent fe = (FocusEvent)e;691Component currentFocusOwner = getGlobalFocusOwner();692if (currentFocusOwner == null) {693if (focusLog.isLoggable(PlatformLogger.Level.FINE))694focusLog.fine("Skipping {0} because focus owner is null", e);695break;696}697// Ignore cases where a Component loses focus to itself.698// If we make a mistake because of retargeting, then the699// FOCUS_GAINED handler will correct it.700if (currentFocusOwner == fe.getOppositeComponent()) {701if (focusLog.isLoggable(PlatformLogger.Level.FINE))702focusLog.fine("Skipping {0} because current focus owner is equal to opposite", e);703break;704}705706setGlobalFocusOwner(null);707708if (getGlobalFocusOwner() != null) {709// Focus change was rejected. Unlikely, but possible.710restoreFocus(currentFocusOwner, true);711break;712}713714if (!fe.isTemporary()) {715setGlobalPermanentFocusOwner(null);716717if (getGlobalPermanentFocusOwner() != null) {718// Focus change was rejected. Unlikely, but possible.719restoreFocus(currentFocusOwner, true);720break;721}722} else {723Window owningWindow = currentFocusOwner.getContainingWindow();724if (owningWindow != null) {725owningWindow.setTemporaryLostComponent(currentFocusOwner);726}727}728729setNativeFocusOwner(null);730731fe.setSource(currentFocusOwner);732733realOppositeComponentWR = (fe.getOppositeComponent() != null)734? new WeakReference<Component>(currentFocusOwner)735: NULL_COMPONENT_WR;736737return typeAheadAssertions(currentFocusOwner, fe);738}739740case WindowEvent.WINDOW_DEACTIVATED: {741WindowEvent we = (WindowEvent)e;742Window currentActiveWindow = getGlobalActiveWindow();743if (currentActiveWindow == null) {744break;745}746747if (currentActiveWindow != e.getSource()) {748// The event is lost in time.749// Allow listeners to precess the event but do not750// change any global states751break;752}753754setGlobalActiveWindow(null);755if (getGlobalActiveWindow() != null) {756// Activation change was rejected. Unlikely, but possible.757break;758}759760we.setSource(currentActiveWindow);761return typeAheadAssertions(currentActiveWindow, we);762}763764case WindowEvent.WINDOW_LOST_FOCUS: {765if (repostIfFollowsKeyEvents((WindowEvent)e)) {766break;767}768769WindowEvent we = (WindowEvent)e;770Window currentFocusedWindow = getGlobalFocusedWindow();771Window losingFocusWindow = we.getWindow();772Window activeWindow = getGlobalActiveWindow();773Window oppositeWindow = we.getOppositeWindow();774if (focusLog.isLoggable(PlatformLogger.Level.FINE))775focusLog.fine("Active {0}, Current focused {1}, losing focus {2} opposite {3}",776activeWindow, currentFocusedWindow,777losingFocusWindow, oppositeWindow);778if (currentFocusedWindow == null) {779break;780}781782// Special case -- if the native windowing system posts an783// event claiming that the active Window has lost focus to the784// focused Window, then discard the event. This is an artifact785// of the native windowing system not knowing which Window is786// really focused.787if (inSendMessage == 0 && losingFocusWindow == activeWindow &&788oppositeWindow == currentFocusedWindow)789{790break;791}792793Component currentFocusOwner = getGlobalFocusOwner();794if (currentFocusOwner != null) {795// The focus owner should always receive a FOCUS_LOST event796// before the Window is defocused.797Component oppositeComp = null;798if (oppositeWindow != null) {799oppositeComp = oppositeWindow.getTemporaryLostComponent();800if (oppositeComp == null) {801oppositeComp = oppositeWindow.getMostRecentFocusOwner();802}803}804if (oppositeComp == null) {805oppositeComp = oppositeWindow;806}807sendMessage(currentFocusOwner,808new FocusEvent(currentFocusOwner,809FocusEvent.FOCUS_LOST,810true,811oppositeComp, FocusEvent.Cause.ACTIVATION));812}813814setGlobalFocusedWindow(null);815if (getGlobalFocusedWindow() != null) {816// Focus change was rejected. Unlikely, but possible.817restoreFocus(currentFocusedWindow, null, true);818break;819}820821we.setSource(currentFocusedWindow);822realOppositeWindowWR = (oppositeWindow != null)823? new WeakReference<Window>(currentFocusedWindow)824: NULL_WINDOW_WR;825typeAheadAssertions(currentFocusedWindow, we);826827if (oppositeWindow == null && activeWindow != null) {828// Then we need to deactivate the active Window as well.829// No need to synthesize in other cases, because830// WINDOW_ACTIVATED will handle it if necessary.831sendMessage(activeWindow,832new WindowEvent(activeWindow,833WindowEvent.WINDOW_DEACTIVATED,834null));835if (getGlobalActiveWindow() != null) {836// Activation change was rejected. Unlikely,837// but possible.838restoreFocus(currentFocusedWindow, null, true);839}840}841break;842}843844case KeyEvent.KEY_TYPED:845case KeyEvent.KEY_PRESSED:846case KeyEvent.KEY_RELEASED:847return typeAheadAssertions(null, e);848849default:850return false;851}852853return true;854}855856/**857* Called by {@code dispatchEvent} if no other858* KeyEventDispatcher in the dispatcher chain dispatched the KeyEvent, or859* if no other KeyEventDispatchers are registered. If the event has not860* been consumed, its target is enabled, and the focus owner is not null,861* this method dispatches the event to its target. This method will also862* subsequently dispatch the event to all registered863* KeyEventPostProcessors. After all this operations are finished,864* the event is passed to peers for processing.865* <p>866* In all cases, this method returns {@code true}, since867* DefaultKeyboardFocusManager is designed so that neither868* {@code dispatchEvent}, nor the AWT event dispatcher, should take869* further action on the event in any situation.870*871* @param e the KeyEvent to be dispatched872* @return {@code true}873* @see Component#dispatchEvent874*/875public boolean dispatchKeyEvent(KeyEvent e) {876Component focusOwner = (((AWTEvent)e).isPosted) ? getFocusOwner() : e.getComponent();877878if (focusOwner != null && focusOwner.isShowing() && focusOwner.canBeFocusOwner()) {879if (!e.isConsumed()) {880Component comp = e.getComponent();881if (comp != null && comp.isEnabled()) {882redispatchEvent(comp, e);883}884}885}886boolean stopPostProcessing = false;887java.util.List<KeyEventPostProcessor> processors = getKeyEventPostProcessors();888if (processors != null) {889for (java.util.Iterator<KeyEventPostProcessor> iter = processors.iterator();890!stopPostProcessing && iter.hasNext(); )891{892stopPostProcessing = iter.next().893postProcessKeyEvent(e);894}895}896if (!stopPostProcessing) {897postProcessKeyEvent(e);898}899900// Allow the peer to process KeyEvent901Component source = e.getComponent();902ComponentPeer peer = source.peer;903904if (peer == null || peer instanceof LightweightPeer) {905// if focus owner is lightweight then its native container906// processes event907Container target = source.getNativeContainer();908if (target != null) {909peer = target.peer;910}911}912if (peer != null) {913peer.handleEvent(e);914}915916return true;917}918919/**920* This method will be called by {@code dispatchKeyEvent}. It will921* handle any unconsumed KeyEvents that map to an AWT922* {@code MenuShortcut} by consuming the event and activating the923* shortcut.924*925* @param e the KeyEvent to post-process926* @return {@code true}927* @see #dispatchKeyEvent928* @see MenuShortcut929*/930public boolean postProcessKeyEvent(KeyEvent e) {931if (!e.isConsumed()) {932Component target = e.getComponent();933Container p = (Container)934(target instanceof Container ? target : target.getParent());935if (p != null) {936p.postProcessKeyEvent(e);937}938}939return true;940}941942private void pumpApprovedKeyEvents() {943KeyEvent ke;944do {945ke = null;946synchronized (this) {947if (enqueuedKeyEvents.size() != 0) {948ke = enqueuedKeyEvents.getFirst();949if (typeAheadMarkers.size() != 0) {950TypeAheadMarker marker = typeAheadMarkers.getFirst();951// Fixed 5064013: may appears that the events have the same time952// if (ke.getWhen() >= marker.after) {953// The fix is rolled out.954955if (ke.getWhen() > marker.after) {956ke = null;957}958}959if (ke != null) {960if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {961focusLog.finer("Pumping approved event {0}", ke);962}963enqueuedKeyEvents.removeFirst();964}965}966}967if (ke != null) {968preDispatchKeyEvent(ke);969}970} while (ke != null);971}972973/**974* Dumps the list of type-ahead queue markers to stderr975*/976void dumpMarkers() {977if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {978focusLog.finest(">>> Markers dump, time: {0}", System.currentTimeMillis());979synchronized (this) {980if (typeAheadMarkers.size() != 0) {981for (TypeAheadMarker marker : typeAheadMarkers) {982focusLog.finest(" {0}", marker);983}984}985}986}987}988989private boolean typeAheadAssertions(Component target, AWTEvent e) {990991// Clear any pending events here as well as in the FOCUS_GAINED992// handler. We need this call here in case a marker was removed in993// response to a call to dequeueKeyEvents.994pumpApprovedKeyEvents();995996switch (e.getID()) {997case KeyEvent.KEY_TYPED:998case KeyEvent.KEY_PRESSED:999case KeyEvent.KEY_RELEASED: {1000KeyEvent ke = (KeyEvent)e;1001synchronized (this) {1002if (e.isPosted && typeAheadMarkers.size() != 0) {1003TypeAheadMarker marker = typeAheadMarkers.getFirst();1004// Fixed 5064013: may appears that the events have the same time1005// if (ke.getWhen() >= marker.after) {1006// The fix is rolled out.10071008if (ke.getWhen() > marker.after) {1009if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {1010focusLog.finer("Storing event {0} because of marker {1}", ke, marker);1011}1012enqueuedKeyEvents.addLast(ke);1013return true;1014}1015}1016}10171018// KeyEvent was posted before focus change request1019return preDispatchKeyEvent(ke);1020}10211022case FocusEvent.FOCUS_GAINED:1023if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {1024focusLog.finest("Markers before FOCUS_GAINED on {0}", target);1025}1026dumpMarkers();1027// Search the marker list for the first marker tied to1028// the Component which just gained focus. Then remove1029// that marker, any markers which immediately follow1030// and are tied to the same component, and all markers1031// that precede it. This handles the case where1032// multiple focus requests were made for the same1033// Component in a row and when we lost some of the1034// earlier requests. Since FOCUS_GAINED events will1035// not be generated for these additional requests, we1036// need to clear those markers too.1037synchronized (this) {1038boolean found = false;1039if (hasMarker(target)) {1040for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator();1041iter.hasNext(); )1042{1043if (iter.next().untilFocused == target) {1044found = true;1045} else if (found) {1046break;1047}1048iter.remove();1049}1050} else {1051// Exception condition - event without marker1052if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {1053focusLog.finer("Event without marker {0}", e);1054}1055}1056}1057focusLog.finest("Markers after FOCUS_GAINED");1058dumpMarkers();10591060redispatchEvent(target, e);10611062// Now, dispatch any pending KeyEvents which have been1063// released because of the FOCUS_GAINED event so that we don't1064// have to wait for another event to be posted to the queue.1065pumpApprovedKeyEvents();1066return true;10671068default:1069redispatchEvent(target, e);1070return true;1071}1072}10731074/**1075* Returns true if there are some marker associated with component {@code comp}1076* in a markers' queue1077* @since 1.51078*/1079private boolean hasMarker(Component comp) {1080for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {1081if (iter.next().untilFocused == comp) {1082return true;1083}1084}1085return false;1086}10871088/**1089* Clears markers queue1090* @since 1.51091*/1092void clearMarkers() {1093synchronized(this) {1094typeAheadMarkers.clear();1095}1096}10971098@SuppressWarnings("deprecation")1099private boolean preDispatchKeyEvent(KeyEvent ke) {1100if (((AWTEvent) ke).isPosted) {1101Component focusOwner = getFocusOwner();1102ke.setSource(((focusOwner != null) ? focusOwner : getFocusedWindow()));1103}1104if (ke.getSource() == null) {1105return true;1106}11071108// Explicitly set the key event timestamp here (not in Component.dispatchEventImpl):1109// - A key event is anyway passed to this method which starts its actual dispatching.1110// - If a key event is put to the type ahead queue, its time stamp should not be registered1111// until its dispatching actually starts (by this method).1112EventQueue.setCurrentEventAndMostRecentTime(ke);11131114/**1115* Fix for 4495473.1116* This fix allows to correctly dispatch events when native1117* event proxying mechanism is active.1118* If it is active we should redispatch key events after1119* we detected its correct target.1120*/1121if (KeyboardFocusManager.isProxyActive(ke)) {1122Component source = (Component)ke.getSource();1123Container target = source.getNativeContainer();1124if (target != null) {1125ComponentPeer peer = target.peer;1126if (peer != null) {1127peer.handleEvent(ke);1128/**1129* Fix for 4478780 - consume event after it was dispatched by peer.1130*/1131ke.consume();1132}1133}1134return true;1135}11361137java.util.List<KeyEventDispatcher> dispatchers = getKeyEventDispatchers();1138if (dispatchers != null) {1139for (java.util.Iterator<KeyEventDispatcher> iter = dispatchers.iterator();1140iter.hasNext(); )1141{1142if (iter.next().1143dispatchKeyEvent(ke))1144{1145return true;1146}1147}1148}1149return dispatchKeyEvent(ke);1150}11511152/*1153* @param e is a KEY_PRESSED event that can be used1154* to track the next KEY_TYPED related.1155*/1156private void consumeNextKeyTyped(KeyEvent e) {1157consumeNextKeyTyped = true;1158}11591160private void consumeTraversalKey(KeyEvent e) {1161e.consume();1162consumeNextKeyTyped = (e.getID() == KeyEvent.KEY_PRESSED) &&1163!e.isActionKey();1164}11651166/*1167* return true if event was consumed1168*/1169private boolean consumeProcessedKeyEvent(KeyEvent e) {1170if ((e.getID() == KeyEvent.KEY_TYPED) && consumeNextKeyTyped) {1171e.consume();1172consumeNextKeyTyped = false;1173return true;1174}1175return false;1176}11771178/**1179* This method initiates a focus traversal operation if and only if the1180* KeyEvent represents a focus traversal key for the specified1181* focusedComponent. It is expected that focusedComponent is the current1182* focus owner, although this need not be the case. If it is not,1183* focus traversal will nevertheless proceed as if focusedComponent1184* were the focus owner.1185*1186* @param focusedComponent the Component that is the basis for a focus1187* traversal operation if the specified event represents a focus1188* traversal key for the Component1189* @param e the event that may represent a focus traversal key1190*/1191public void processKeyEvent(Component focusedComponent, KeyEvent e) {1192// consume processed event if needed1193if (consumeProcessedKeyEvent(e)) {1194return;1195}11961197// KEY_TYPED events cannot be focus traversal keys1198if (e.getID() == KeyEvent.KEY_TYPED) {1199return;1200}12011202if (focusedComponent.getFocusTraversalKeysEnabled() &&1203!e.isConsumed())1204{1205AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e),1206oppStroke = AWTKeyStroke.getAWTKeyStroke(stroke.getKeyCode(),1207stroke.getModifiers(),1208!stroke.isOnKeyRelease());1209Set<AWTKeyStroke> toTest;1210boolean contains, containsOpp;12111212toTest = focusedComponent.getFocusTraversalKeys(1213KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);1214contains = toTest.contains(stroke);1215containsOpp = toTest.contains(oppStroke);12161217if (contains || containsOpp) {1218consumeTraversalKey(e);1219if (contains) {1220focusNextComponent(focusedComponent);1221}1222return;1223} else if (e.getID() == KeyEvent.KEY_PRESSED) {1224// Fix for 6637607: consumeNextKeyTyped should be reset.1225consumeNextKeyTyped = false;1226}12271228toTest = focusedComponent.getFocusTraversalKeys(1229KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);1230contains = toTest.contains(stroke);1231containsOpp = toTest.contains(oppStroke);12321233if (contains || containsOpp) {1234consumeTraversalKey(e);1235if (contains) {1236focusPreviousComponent(focusedComponent);1237}1238return;1239}12401241toTest = focusedComponent.getFocusTraversalKeys(1242KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);1243contains = toTest.contains(stroke);1244containsOpp = toTest.contains(oppStroke);12451246if (contains || containsOpp) {1247consumeTraversalKey(e);1248if (contains) {1249upFocusCycle(focusedComponent);1250}1251return;1252}12531254if (!((focusedComponent instanceof Container) &&1255((Container)focusedComponent).isFocusCycleRoot())) {1256return;1257}12581259toTest = focusedComponent.getFocusTraversalKeys(1260KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);1261contains = toTest.contains(stroke);1262containsOpp = toTest.contains(oppStroke);12631264if (contains || containsOpp) {1265consumeTraversalKey(e);1266if (contains) {1267downFocusCycle((Container)focusedComponent);1268}1269}1270}1271}12721273/**1274* Delays dispatching of KeyEvents until the specified Component becomes1275* the focus owner. KeyEvents with timestamps later than the specified1276* timestamp will be enqueued until the specified Component receives a1277* FOCUS_GAINED event, or the AWT cancels the delay request by invoking1278* {@code dequeueKeyEvents} or {@code discardKeyEvents}.1279*1280* @param after timestamp of current event, or the current, system time if1281* the current event has no timestamp, or the AWT cannot determine1282* which event is currently being handled1283* @param untilFocused Component which will receive a FOCUS_GAINED event1284* before any pending KeyEvents1285* @see #dequeueKeyEvents1286* @see #discardKeyEvents1287*/1288protected synchronized void enqueueKeyEvents(long after,1289Component untilFocused) {1290if (untilFocused == null) {1291return;1292}12931294if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {1295focusLog.finer("Enqueue at {0} for {1}",1296after, untilFocused);1297}12981299int insertionIndex = 0,1300i = typeAheadMarkers.size();1301ListIterator<TypeAheadMarker> iter = typeAheadMarkers.listIterator(i);13021303for (; i > 0; i--) {1304TypeAheadMarker marker = iter.previous();1305if (marker.after <= after) {1306insertionIndex = i;1307break;1308}1309}13101311typeAheadMarkers.add(insertionIndex,1312new TypeAheadMarker(after, untilFocused));1313}13141315/**1316* Releases for normal dispatching to the current focus owner all1317* KeyEvents which were enqueued because of a call to1318* {@code enqueueKeyEvents} with the same timestamp and Component.1319* If the given timestamp is less than zero, the outstanding enqueue1320* request for the given Component with the <b>oldest</b> timestamp (if1321* any) should be cancelled.1322*1323* @param after the timestamp specified in the call to1324* {@code enqueueKeyEvents}, or any value < 01325* @param untilFocused the Component specified in the call to1326* {@code enqueueKeyEvents}1327* @see #enqueueKeyEvents1328* @see #discardKeyEvents1329*/1330protected synchronized void dequeueKeyEvents(long after,1331Component untilFocused) {1332if (untilFocused == null) {1333return;1334}13351336if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {1337focusLog.finer("Dequeue at {0} for {1}",1338after, untilFocused);1339}13401341TypeAheadMarker marker;1342ListIterator<TypeAheadMarker> iter = typeAheadMarkers.listIterator1343((after >= 0) ? typeAheadMarkers.size() : 0);13441345if (after < 0) {1346while (iter.hasNext()) {1347marker = iter.next();1348if (marker.untilFocused == untilFocused)1349{1350iter.remove();1351return;1352}1353}1354} else {1355while (iter.hasPrevious()) {1356marker = iter.previous();1357if (marker.untilFocused == untilFocused &&1358marker.after == after)1359{1360iter.remove();1361return;1362}1363}1364}1365}13661367/**1368* Discards all KeyEvents which were enqueued because of one or more calls1369* to {@code enqueueKeyEvents} with the specified Component, or one of1370* its descendants.1371*1372* @param comp the Component specified in one or more calls to1373* {@code enqueueKeyEvents}, or a parent of such a Component1374* @see #enqueueKeyEvents1375* @see #dequeueKeyEvents1376*/1377protected synchronized void discardKeyEvents(Component comp) {1378if (comp == null) {1379return;1380}13811382long start = -1;13831384for (Iterator<TypeAheadMarker> iter = typeAheadMarkers.iterator(); iter.hasNext(); ) {1385TypeAheadMarker marker = iter.next();1386Component toTest = marker.untilFocused;1387boolean match = (toTest == comp);1388while (!match && toTest != null && !(toTest instanceof Window)) {1389toTest = toTest.getParent();1390match = (toTest == comp);1391}1392if (match) {1393if (start < 0) {1394start = marker.after;1395}1396iter.remove();1397} else if (start >= 0) {1398purgeStampedEvents(start, marker.after);1399start = -1;1400}1401}14021403purgeStampedEvents(start, -1);1404}14051406// Notes:1407// * must be called inside a synchronized block1408// * if 'start' is < 0, then this function does nothing1409// * if 'end' is < 0, then all KeyEvents from 'start' to the end of the1410// queue will be removed1411private void purgeStampedEvents(long start, long end) {1412if (start < 0) {1413return;1414}14151416for (Iterator<KeyEvent> iter = enqueuedKeyEvents.iterator(); iter.hasNext(); ) {1417KeyEvent ke = iter.next();1418long time = ke.getWhen();14191420if (start < time && (end < 0 || time <= end)) {1421iter.remove();1422}14231424if (end >= 0 && time > end) {1425break;1426}1427}1428}14291430/**1431* Focuses the Component before aComponent, typically based on a1432* FocusTraversalPolicy.1433*1434* @param aComponent the Component that is the basis for the focus1435* traversal operation1436* @see FocusTraversalPolicy1437* @see Component#transferFocusBackward1438*/1439public void focusPreviousComponent(Component aComponent) {1440if (aComponent != null) {1441aComponent.transferFocusBackward();1442}1443}14441445/**1446* Focuses the Component after aComponent, typically based on a1447* FocusTraversalPolicy.1448*1449* @param aComponent the Component that is the basis for the focus1450* traversal operation1451* @see FocusTraversalPolicy1452* @see Component#transferFocus1453*/1454public void focusNextComponent(Component aComponent) {1455if (aComponent != null) {1456aComponent.transferFocus();1457}1458}14591460/**1461* Moves the focus up one focus traversal cycle. Typically, the focus owner1462* is set to aComponent's focus cycle root, and the current focus cycle1463* root is set to the new focus owner's focus cycle root. If, however,1464* aComponent's focus cycle root is a Window, then the focus owner is set1465* to the focus cycle root's default Component to focus, and the current1466* focus cycle root is unchanged.1467*1468* @param aComponent the Component that is the basis for the focus1469* traversal operation1470* @see Component#transferFocusUpCycle1471*/1472public void upFocusCycle(Component aComponent) {1473if (aComponent != null) {1474aComponent.transferFocusUpCycle();1475}1476}14771478/**1479* Moves the focus down one focus traversal cycle. If aContainer is a focus1480* cycle root, then the focus owner is set to aContainer's default1481* Component to focus, and the current focus cycle root is set to1482* aContainer. If aContainer is not a focus cycle root, then no focus1483* traversal operation occurs.1484*1485* @param aContainer the Container that is the basis for the focus1486* traversal operation1487* @see Container#transferFocusDownCycle1488*/1489public void downFocusCycle(Container aContainer) {1490if (aContainer != null && aContainer.isFocusCycleRoot()) {1491aContainer.transferFocusDownCycle();1492}1493}1494}149514961497