Java tutorial
/* * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.awt; import java.awt.event.FocusEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.awt.peer.KeyboardFocusManagerPeer; import java.awt.peer.LightweightPeer; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; import java.beans.VetoableChangeSupport; import java.lang.ref.WeakReference; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.Set; import java.util.StringTokenizer; import java.util.WeakHashMap; import sun.util.logging.PlatformLogger; import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.awt.KeyboardFocusManagerPeerProvider; import sun.awt.AWTAccessor; /** * The KeyboardFocusManager is responsible for managing the active and focused * Windows, and the current focus owner. The focus owner is defined as the * Component in an application that will typically receive all KeyEvents * generated by the user. The focused Window is the Window that is, or * contains, the focus owner. Only a Frame or a Dialog can be the active * Window. The native windowing system may denote the active Window or its * children with special decorations, such as a highlighted title bar. The * active Window is always either the focused Window, or the first Frame or * Dialog that is an owner of the focused Window. * <p> * The KeyboardFocusManager is both a centralized location for client code to * query for the focus owner and initiate focus changes, and an event * dispatcher for all FocusEvents, WindowEvents related to focus, and * KeyEvents. * <p> * Some browsers partition applets in different code bases into separate * contexts, and establish walls between these contexts. In such a scenario, * there will be one KeyboardFocusManager per context. Other browsers place all * applets into the same context, implying that there will be only a single, * global KeyboardFocusManager for all applets. This behavior is * implementation-dependent. Consult your browser's documentation for more * information. No matter how many contexts there may be, however, there can * never be more than one focus owner, focused Window, or active Window, per * ClassLoader. * <p> * Please see * <a href="https://docs.oracle.com/javase/tutorial/uiswing/misc/focus.html"> * How to Use the Focus Subsystem</a>, * a section in <em>The Java Tutorial</em>, and the * <a href="../../java/awt/doc-files/FocusSpec.html">Focus Specification</a> * for more information. * * @author David Mendenhall * * @see Window * @see Frame * @see Dialog * @see java.awt.event.FocusEvent * @see java.awt.event.WindowEvent * @see java.awt.event.KeyEvent * @since 1.4 */ public abstract class KeyboardFocusManager implements KeyEventDispatcher, KeyEventPostProcessor { // Shared focus engine logger private static final PlatformLogger focusLog = PlatformLogger.getLogger("java.awt.focus.KeyboardFocusManager"); static { /* ensure that the necessary native libraries are loaded */ Toolkit.loadLibraries(); if (!GraphicsEnvironment.isHeadless()) { initIDs(); } AWTAccessor.setKeyboardFocusManagerAccessor(new AWTAccessor.KeyboardFocusManagerAccessor() { public int shouldNativelyFocusHeavyweight(Component heavyweight, Component descendant, boolean temporary, boolean focusedWindowChangeAllowed, long time, FocusEvent.Cause cause) { return KeyboardFocusManager.shouldNativelyFocusHeavyweight(heavyweight, descendant, temporary, focusedWindowChangeAllowed, time, cause); } public boolean processSynchronousLightweightTransfer(Component heavyweight, Component descendant, boolean temporary, boolean focusedWindowChangeAllowed, long time) { return KeyboardFocusManager.processSynchronousLightweightTransfer(heavyweight, descendant, temporary, focusedWindowChangeAllowed, time); } public void removeLastFocusRequest(Component heavyweight) { KeyboardFocusManager.removeLastFocusRequest(heavyweight); } @Override public Component getMostRecentFocusOwner(Window window) { return KeyboardFocusManager.getMostRecentFocusOwner(window); } public void setMostRecentFocusOwner(Window window, Component component) { KeyboardFocusManager.setMostRecentFocusOwner(window, component); } public KeyboardFocusManager getCurrentKeyboardFocusManager(AppContext ctx) { return KeyboardFocusManager.getCurrentKeyboardFocusManager(ctx); } public Container getCurrentFocusCycleRoot() { return KeyboardFocusManager.currentFocusCycleRoot; } }); } transient KeyboardFocusManagerPeer peer; /** * Initialize JNI field and method IDs */ private static native void initIDs(); private static final PlatformLogger log = PlatformLogger.getLogger("java.awt.KeyboardFocusManager"); /** * The identifier for the Forward focus traversal keys. * * @see #setDefaultFocusTraversalKeys * @see #getDefaultFocusTraversalKeys * @see Component#setFocusTraversalKeys * @see Component#getFocusTraversalKeys */ public static final int FORWARD_TRAVERSAL_KEYS = 0; /** * The identifier for the Backward focus traversal keys. * * @see #setDefaultFocusTraversalKeys * @see #getDefaultFocusTraversalKeys * @see Component#setFocusTraversalKeys * @see Component#getFocusTraversalKeys */ public static final int BACKWARD_TRAVERSAL_KEYS = 1; /** * The identifier for the Up Cycle focus traversal keys. * * @see #setDefaultFocusTraversalKeys * @see #getDefaultFocusTraversalKeys * @see Component#setFocusTraversalKeys * @see Component#getFocusTraversalKeys */ public static final int UP_CYCLE_TRAVERSAL_KEYS = 2; /** * The identifier for the Down Cycle focus traversal keys. * * @see #setDefaultFocusTraversalKeys * @see #getDefaultFocusTraversalKeys * @see Component#setFocusTraversalKeys * @see Component#getFocusTraversalKeys */ public static final int DOWN_CYCLE_TRAVERSAL_KEYS = 3; static final int TRAVERSAL_KEY_LENGTH = DOWN_CYCLE_TRAVERSAL_KEYS + 1; /** * Returns the current KeyboardFocusManager instance for the calling * thread's context. * * @return this thread's context's KeyboardFocusManager * @see #setCurrentKeyboardFocusManager */ public static KeyboardFocusManager getCurrentKeyboardFocusManager() { return getCurrentKeyboardFocusManager(AppContext.getAppContext()); } static synchronized KeyboardFocusManager getCurrentKeyboardFocusManager(AppContext appcontext) { KeyboardFocusManager manager = (KeyboardFocusManager) appcontext.get(KeyboardFocusManager.class); if (manager == null) { manager = new DefaultKeyboardFocusManager(); appcontext.put(KeyboardFocusManager.class, manager); } return manager; } /** * Sets the current KeyboardFocusManager instance for the calling thread's * context. If null is specified, then the current KeyboardFocusManager * is replaced with a new instance of DefaultKeyboardFocusManager. * <p> * If a SecurityManager is installed, the calling thread must be granted * the AWTPermission "replaceKeyboardFocusManager" in order to replace the * the current KeyboardFocusManager. If this permission is not granted, * this method will throw a SecurityException, and the current * KeyboardFocusManager will be unchanged. * * @param newManager the new KeyboardFocusManager for this thread's context * @see #getCurrentKeyboardFocusManager * @see DefaultKeyboardFocusManager * @throws SecurityException if the calling thread does not have permission * to replace the current KeyboardFocusManager */ public static void setCurrentKeyboardFocusManager(KeyboardFocusManager newManager) throws SecurityException { checkReplaceKFMPermission(); KeyboardFocusManager oldManager = null; synchronized (KeyboardFocusManager.class) { AppContext appcontext = AppContext.getAppContext(); if (newManager != null) { oldManager = getCurrentKeyboardFocusManager(appcontext); appcontext.put(KeyboardFocusManager.class, newManager); } else { oldManager = getCurrentKeyboardFocusManager(appcontext); appcontext.remove(KeyboardFocusManager.class); } } if (oldManager != null) { oldManager.firePropertyChange("managingFocus", Boolean.TRUE, Boolean.FALSE); } if (newManager != null) { newManager.firePropertyChange("managingFocus", Boolean.FALSE, Boolean.TRUE); } } /** * The Component in an application that will typically receive all * KeyEvents generated by the user. */ private static Component focusOwner; /** * The Component in an application that will regain focus when an * outstanding temporary focus transfer has completed, or the focus owner, * if no outstanding temporary transfer exists. */ private static Component permanentFocusOwner; /** * The Window which is, or contains, the focus owner. */ private static Window focusedWindow; /** * Only a Frame or a Dialog can be the active Window. The native windowing * system may denote the active Window with a special decoration, such as a * highlighted title bar. The active Window is always either the focused * Window, or the first Frame or Dialog which is an owner of the focused * Window. */ private static Window activeWindow; /** * The default FocusTraversalPolicy for all Windows that have no policy of * their own set. If those Windows have focus-cycle-root children that have * no keyboard-traversal policy of their own, then those children will also * inherit this policy (as will, recursively, their focus-cycle-root * children). */ private FocusTraversalPolicy defaultPolicy = new DefaultFocusTraversalPolicy(); /** * The bound property names of each focus traversal key. */ private static final String[] defaultFocusTraversalKeyPropertyNames = { "forwardDefaultFocusTraversalKeys", "backwardDefaultFocusTraversalKeys", "upCycleDefaultFocusTraversalKeys", "downCycleDefaultFocusTraversalKeys" }; /** * The default focus traversal keys. Each array of traversal keys will be * in effect on all Windows that have no such array of their own explicitly * set. Each array will also be inherited, recursively, by any child * Component of those Windows that has no such array of its own explicitly * set. */ @SuppressWarnings({ "unchecked", "rawtypes" }) private Set<AWTKeyStroke>[] defaultFocusTraversalKeys = new Set[4]; /** * The current focus cycle root. If the focus owner is itself a focus cycle * root, then it may be ambiguous as to which Components represent the next * and previous Components to focus during normal focus traversal. In that * case, the current focus cycle root is used to differentiate among the * possibilities. */ private static Container currentFocusCycleRoot; /** * A description of any VetoableChangeListeners which have been registered. */ private VetoableChangeSupport vetoableSupport; /** * A description of any PropertyChangeListeners which have been registered. */ private PropertyChangeSupport changeSupport; /** * This KeyboardFocusManager's KeyEventDispatcher chain. The List does not * include this KeyboardFocusManager unless it was explicitly re-registered * via a call to {@code addKeyEventDispatcher}. If no other * KeyEventDispatchers are registered, this field may be null or refer to * a List of length 0. */ private java.util.LinkedList<KeyEventDispatcher> keyEventDispatchers; /** * This KeyboardFocusManager's KeyEventPostProcessor chain. The List does * not include this KeyboardFocusManager unless it was explicitly * re-registered via a call to {@code addKeyEventPostProcessor}. * If no other KeyEventPostProcessors are registered, this field may be * null or refer to a List of length 0. */ private java.util.LinkedList<KeyEventPostProcessor> keyEventPostProcessors; /** * Maps Windows to those Windows' most recent focus owners. */ private static java.util.Map<Window, WeakReference<Component>> mostRecentFocusOwners = new WeakHashMap<>(); /** * We cache the permission used to verify that the calling thread is * permitted to access the global focus state. */ private static AWTPermission replaceKeyboardFocusManagerPermission; /* * SequencedEvent which is currently dispatched in AppContext. */ transient SequencedEvent currentSequencedEvent = null; final void setCurrentSequencedEvent(SequencedEvent current) { synchronized (SequencedEvent.class) { assert (current == null || currentSequencedEvent == null); currentSequencedEvent = current; } } final SequencedEvent getCurrentSequencedEvent() { synchronized (SequencedEvent.class) { return currentSequencedEvent; } } static Set<AWTKeyStroke> initFocusTraversalKeysSet(String value, Set<AWTKeyStroke> targetSet) { StringTokenizer tokens = new StringTokenizer(value, ","); while (tokens.hasMoreTokens()) { targetSet.add(AWTKeyStroke.getAWTKeyStroke(tokens.nextToken())); } return (targetSet.isEmpty()) ? Collections.emptySet() : Collections.unmodifiableSet(targetSet); } /** * Initializes a KeyboardFocusManager. */ public KeyboardFocusManager() { @SuppressWarnings("deprecation") AWTKeyStroke[][] defaultFocusTraversalKeyStrokes = { { AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0, false), AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, InputEvent.CTRL_DOWN_MASK | InputEvent.CTRL_MASK, false), }, { AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK | InputEvent.SHIFT_MASK, false), AWTKeyStroke .getAWTKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK | InputEvent.SHIFT_MASK | InputEvent.CTRL_DOWN_MASK | InputEvent.CTRL_MASK, false), }, {}, {}, }; for (int i = 0; i < TRAVERSAL_KEY_LENGTH; i++) { Set<AWTKeyStroke> work_set = new HashSet<>(); for (int j = 0; j < defaultFocusTraversalKeyStrokes[i].length; j++) { work_set.add(defaultFocusTraversalKeyStrokes[i][j]); } defaultFocusTraversalKeys[i] = (work_set.isEmpty()) ? Collections.emptySet() : Collections.unmodifiableSet(work_set); } initPeer(); } private void initPeer() { Toolkit tk = Toolkit.getDefaultToolkit(); KeyboardFocusManagerPeerProvider peerProvider = (KeyboardFocusManagerPeerProvider) tk; peer = peerProvider.getKeyboardFocusManagerPeer(); } /** * Returns the focus owner, if the focus owner is in the same context as * the calling thread. The focus owner is defined as the Component in an * application that will typically receive all KeyEvents generated by the * user. KeyEvents which map to the focus owner's focus traversal keys will * not be delivered if focus traversal keys are enabled for the focus * owner. In addition, KeyEventDispatchers may retarget or consume * KeyEvents before they reach the focus owner. * * @return the focus owner, or null if the focus owner is not a member of * the calling thread's context * @see #getGlobalFocusOwner * @see #setGlobalFocusOwner */ public Component getFocusOwner() { synchronized (KeyboardFocusManager.class) { if (focusOwner == null) { return null; } return (focusOwner.appContext == AppContext.getAppContext()) ? focusOwner : null; } } /** * Returns the focus owner, even if the calling thread is in a different * context than the focus owner. The focus owner is defined as the * Component in an application that will typically receive all KeyEvents * generated by the user. KeyEvents which map to the focus owner's focus * traversal keys will not be delivered if focus traversal keys are enabled * for the focus owner. In addition, KeyEventDispatchers may retarget or * consume KeyEvents before they reach the focus owner. * <p> * This method will throw a SecurityException if this KeyboardFocusManager * is not the current KeyboardFocusManager for the calling thread's * context. * * @return the focus owner * @see #getFocusOwner * @see #setGlobalFocusOwner * @throws SecurityException if this KeyboardFocusManager is not the * current KeyboardFocusManager for the calling thread's context * and if the calling thread does not have "replaceKeyboardFocusManager" * permission */ protected Component getGlobalFocusOwner() throws SecurityException { synchronized (KeyboardFocusManager.class) { checkKFMSecurity(); return focusOwner; } } /** * Sets the focus owner. The operation will be cancelled if the Component * is not focusable. The focus owner is defined as the Component in an * application that will typically receive all KeyEvents generated by the * user. KeyEvents which map to the focus owner's focus traversal keys will * not be delivered if focus traversal keys are enabled for the focus * owner. In addition, KeyEventDispatchers may retarget or consume * KeyEvents before they reach the focus owner. * <p> * This method does not actually set the focus to the specified Component. * It merely stores the value to be subsequently returned by * {@code getFocusOwner()}. Use {@code Component.requestFocus()} * or {@code Component.requestFocusInWindow()} to change the focus * owner, subject to platform limitations. * * @param focusOwner the focus owner * @see #getFocusOwner * @see #getGlobalFocusOwner * @see Component#requestFocus() * @see Component#requestFocusInWindow() * @see Component#isFocusable * @throws SecurityException if this KeyboardFocusManager is not the * current KeyboardFocusManager for the calling thread's context * and if the calling thread does not have "replaceKeyboardFocusManager" * permission */ protected void setGlobalFocusOwner(Component focusOwner) throws SecurityException { Component oldFocusOwner = null; boolean shouldFire = false; if (focusOwner == null || focusOwner.isFocusable()) { synchronized (KeyboardFocusManager.class) { checkKFMSecurity(); oldFocusOwner = getFocusOwner(); try { fireVetoableChange("focusOwner", oldFocusOwner, focusOwner); } catch (PropertyVetoException e) { // rejected return; } KeyboardFocusManager.focusOwner = focusOwner; if (focusOwner != null && (getCurrentFocusCycleRoot() == null || !focusOwner.isFocusCycleRoot(getCurrentFocusCycleRoot()))) { Container rootAncestor = focusOwner.getFocusCycleRootAncestor(); if (rootAncestor == null && (focusOwner instanceof Window)) { rootAncestor = (Container) focusOwner; } if (rootAncestor != null) { setGlobalCurrentFocusCycleRootPriv(rootAncestor); } } shouldFire = true; } } if (shouldFire) { firePropertyChange("focusOwner", oldFocusOwner, focusOwner); } } /** * Clears the focus owner at both the Java and native levels if the * focus owner exists and resides in the same context as the calling thread, * otherwise the method returns silently. * <p> * The focus owner component will receive a permanent FOCUS_LOST event. * After this operation completes, the native windowing system will discard * all user-generated KeyEvents until the user selects a new Component to * receive focus, or a Component is given focus explicitly via a call to * {@code requestFocus()}. This operation does not change the focused or * active Windows. * * @see Component#requestFocus() * @see java.awt.event.FocusEvent#FOCUS_LOST * @since 1.8 */ public void clearFocusOwner() { if (getFocusOwner() != null) { clearGlobalFocusOwner(); } } /** * Clears the global focus owner at both the Java and native levels. If * there exists a focus owner, that Component will receive a permanent * FOCUS_LOST event. After this operation completes, the native windowing * system will discard all user-generated KeyEvents until the user selects * a new Component to receive focus, or a Component is given focus * explicitly via a call to {@code requestFocus()}. This operation * does not change the focused or active Windows. * <p> * If a SecurityManager is installed, the calling thread must be granted * the "replaceKeyboardFocusManager" AWTPermission. If this permission is * not granted, this method will throw a SecurityException, and the current * focus owner will not be cleared. * <p> * This method is intended to be used only by KeyboardFocusManager set as * current KeyboardFocusManager for the calling thread's context. It is not * for general client use. * * @see KeyboardFocusManager#clearFocusOwner * @see Component#requestFocus() * @see java.awt.event.FocusEvent#FOCUS_LOST * @throws SecurityException if the calling thread does not have * "replaceKeyboardFocusManager" permission */ public void clearGlobalFocusOwner() throws SecurityException { checkReplaceKFMPermission(); if (!GraphicsEnvironment.isHeadless()) { // Toolkit must be fully initialized, otherwise // _clearGlobalFocusOwner will crash or throw an exception Toolkit.getDefaultToolkit(); _clearGlobalFocusOwner(); } } private void _clearGlobalFocusOwner() { Window activeWindow = markClearGlobalFocusOwner(); peer.clearGlobalFocusOwner(activeWindow); } void clearGlobalFocusOwnerPriv() { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { clearGlobalFocusOwner(); return null; } }); } Component getNativeFocusOwner() { return peer.getCurrentFocusOwner(); } void setNativeFocusOwner(Component comp) { if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) { focusLog.finest("Calling peer {0} setCurrentFocusOwner for {1}", String.valueOf(peer), String.valueOf(comp)); } peer.setCurrentFocusOwner(comp); } Window getNativeFocusedWindow() { return peer.getCurrentFocusedWindow(); } /** * Returns the permanent focus owner, if the permanent focus owner is in * the same context as the calling thread. The permanent focus owner is * defined as the last Component in an application to receive a permanent * FOCUS_GAINED event. The focus owner and permanent focus owner are * equivalent unless a temporary focus change is currently in effect. In * such a situation, the permanent focus owner will again be the focus * owner when the temporary focus change ends. * * @return the permanent focus owner, or null if the permanent focus owner * is not a member of the calling thread's context * @see #getGlobalPermanentFocusOwner * @see #setGlobalPermanentFocusOwner */ public Component getPermanentFocusOwner() { synchronized (KeyboardFocusManager.class) { if (permanentFocusOwner == null) { return null; } return (permanentFocusOwner.appContext == AppContext.getAppContext()) ? permanentFocusOwner : null; } } /** * Returns the permanent focus owner, even if the calling thread is in a * different context than the permanent focus owner. The permanent focus * owner is defined as the last Component in an application to receive a * permanent FOCUS_GAINED event. The focus owner and permanent focus owner * are equivalent unless a temporary focus change is currently in effect. * In such a situation, the permanent focus owner will again be the focus * owner when the temporary focus change ends. * * @return the permanent focus owner * @see #getPermanentFocusOwner * @see #setGlobalPermanentFocusOwner * @throws SecurityException if this KeyboardFocusManager is not the * current KeyboardFocusManager for the calling thread's context * and if the calling thread does not have "replaceKeyboardFocusManager" * permission */ protected Component getGlobalPermanentFocusOwner() throws SecurityException { synchronized (KeyboardFocusManager.class) { checkKFMSecurity(); return permanentFocusOwner; } } /** * Sets the permanent focus owner. The operation will be cancelled if the * Component is not focusable. The permanent focus owner is defined as the * last Component in an application to receive a permanent FOCUS_GAINED * event. The focus owner and permanent focus owner are equivalent unless * a temporary focus change is currently in effect. In such a situation, * the permanent focus owner will again be the focus owner when the * temporary focus change ends. * <p> * This method does not actually set the focus to the specified Component. * It merely stores the value to be subsequently returned by * {@code getPermanentFocusOwner()}. Use * {@code Component.requestFocus()} or * {@code Component.requestFocusInWindow()} to change the focus owner, * subject to platform limitations. * * @param permanentFocusOwner the permanent focus owner * @see #getPermanentFocusOwner * @see #getGlobalPermanentFocusOwner * @see Component#requestFocus() * @see Component#requestFocusInWindow() * @see Component#isFocusable * @throws SecurityException if this KeyboardFocusManager is not the * current KeyboardFocusManager for the calling thread's context * and if the calling thread does not have "replaceKeyboardFocusManager" * permission */ protected void setGlobalPermanentFocusOwner(Component permanentFocusOwner) throws SecurityException { Component oldPermanentFocusOwner = null; boolean shouldFire = false; if (permanentFocusOwner == null || permanentFocusOwner.isFocusable()) { synchronized (KeyboardFocusManager.class) { checkKFMSecurity(); oldPermanentFocusOwner = getPermanentFocusOwner(); try { fireVetoableChange("permanentFocusOwner", oldPermanentFocusOwner, permanentFocusOwner); } catch (PropertyVetoException e) { // rejected return; } KeyboardFocusManager.permanentFocusOwner = permanentFocusOwner; KeyboardFocusManager.setMostRecentFocusOwner(permanentFocusOwner); shouldFire = true; } } if (shouldFire) { firePropertyChange("permanentFocusOwner", oldPermanentFocusOwner, permanentFocusOwner); } } /** * Returns the focused Window, if the focused Window is in the same context * as the calling thread. The focused Window is the Window that is or * contains the focus owner. * * @return the focused Window, or null if the focused Window is not a * member of the calling thread's context * @see #getGlobalFocusedWindow * @see #setGlobalFocusedWindow */ public Window getFocusedWindow() { synchronized (KeyboardFocusManager.class) { if (focusedWindow == null) { return null; } return (focusedWindow.appContext == AppContext.getAppContext()) ? focusedWindow : null; } } /** * Returns the focused Window, even if the calling thread is in a different * context than the focused Window. The focused Window is the Window that * is or contains the focus owner. * * @return the focused Window * @see #getFocusedWindow * @see #setGlobalFocusedWindow * @throws SecurityException if this KeyboardFocusManager is not the * current KeyboardFocusManager for the calling thread's context * and if the calling thread does not have "replaceKeyboardFocusManager" * permission */ protected Window getGlobalFocusedWindow() throws SecurityException { synchronized (KeyboardFocusManager.class) { checkKFMSecurity(); return focusedWindow; } } /** * Sets the focused Window. The focused Window is the Window that is or * contains the focus owner. The operation will be cancelled if the * specified Window to focus is not a focusable Window. * <p> * This method does not actually change the focused Window as far as the * native windowing system is concerned. It merely stores the value to be * subsequently returned by {@code getFocusedWindow()}. Use * {@code Component.requestFocus()} or * {@code Component.requestFocusInWindow()} to change the focused * Window, subject to platform limitations. * * @param focusedWindow the focused Window * @see #getFocusedWindow * @see #getGlobalFocusedWindow * @see Component#requestFocus() * @see Component#requestFocusInWindow() * @see Window#isFocusableWindow * @throws SecurityException if this KeyboardFocusManager is not the * current KeyboardFocusManager for the calling thread's context * and if the calling thread does not have "replaceKeyboardFocusManager" * permission */ protected void setGlobalFocusedWindow(Window focusedWindow) throws SecurityException { Window oldFocusedWindow = null; boolean shouldFire = false; if (focusedWindow == null || focusedWindow.isFocusableWindow()) { synchronized (KeyboardFocusManager.class) { checkKFMSecurity(); oldFocusedWindow = getFocusedWindow(); try { fireVetoableChange("focusedWindow", oldFocusedWindow, focusedWindow); } catch (PropertyVetoException e) { // rejected return; } KeyboardFocusManager.focusedWindow = focusedWindow; shouldFire = true; } } if (shouldFire) { firePropertyChange("focusedWindow", oldFocusedWindow, focusedWindow); } } /** * Returns the active Window, if the active Window is in the same context * as the calling thread. Only a Frame or a Dialog can be the active * Window. The native windowing system may denote the active Window or its * children with special decorations, such as a highlighted title bar. * The active Window is always either the focused Window, or the first * Frame or Dialog that is an owner of the focused Window. * * @return the active Window, or null if the active Window is not a member * of the calling thread's context * @see #getGlobalActiveWindow * @see #setGlobalActiveWindow */ public Window getActiveWindow() { synchronized (KeyboardFocusManager.class) { if (activeWindow == null) { return null; } return (activeWindow.appContext == AppContext.getAppContext()) ? activeWindow : null; } } /** * Returns the active Window, even if the calling thread is in a different * context than the active Window. Only a Frame or a Dialog can be the * active Window. The native windowing system may denote the active Window * or its children with special decorations, such as a highlighted title * bar. The active Window is always either the focused Window, or the first * Frame or Dialog that is an owner of the focused Window. * * @return the active Window * @see #getActiveWindow * @see #setGlobalActiveWindow * @throws SecurityException if this KeyboardFocusManager is not the * current KeyboardFocusManager for the calling thread's context * and if the calling thread does not have "replaceKeyboardFocusManager" * permission */ protected Window getGlobalActiveWindow() throws SecurityException { synchronized (KeyboardFocusManager.class) { checkKFMSecurity(); return activeWindow; } } /** * Sets the active Window. Only a Frame or a Dialog can be the active * Window. The native windowing system may denote the active Window or its * children with special decorations, such as a highlighted title bar. The * active Window is always either the focused Window, or the first Frame or * Dialog that is an owner of the focused Window. * <p> * This method does not actually change the active Window as far as the * native windowing system is concerned. It merely stores the value to be * subsequently returned by {@code getActiveWindow()}. Use * {@code Component.requestFocus()} or * {@code Component.requestFocusInWindow()} to change the active * Window, subject to platform limitations. * * @param activeWindow the active Window * @see #getActiveWindow * @see #getGlobalActiveWindow * @see Component#requestFocus() * @see Component#requestFocusInWindow() * @throws SecurityException if this KeyboardFocusManager is not the * current KeyboardFocusManager for the calling thread's context * and if the calling thread does not have "replaceKeyboardFocusManager" * permission */ protected void setGlobalActiveWindow(Window activeWindow) throws SecurityException { Window oldActiveWindow; synchronized (KeyboardFocusManager.class) { checkKFMSecurity(); oldActiveWindow = getActiveWindow(); if (focusLog.isLoggable(PlatformLogger.Level.FINER)) { focusLog.finer( "Setting global active window to " + activeWindow + ", old active " + oldActiveWindow); } try { fireVetoableChange("activeWindow", oldActiveWindow, activeWindow); } catch (PropertyVetoException e) { // rejected return; } KeyboardFocusManager.activeWindow = activeWindow; } firePropertyChange("activeWindow", oldActiveWindow, activeWindow); } /** * Returns the default FocusTraversalPolicy. Top-level components * use this value on their creation to initialize their own focus traversal * policy by explicit call to Container.setFocusTraversalPolicy. * * @return the default FocusTraversalPolicy. null will never be returned. * @see #setDefaultFocusTraversalPolicy * @see Container#setFocusTraversalPolicy * @see Container#getFocusTraversalPolicy */ public synchronized FocusTraversalPolicy getDefaultFocusTraversalPolicy() { return defaultPolicy; } /** * Sets the default FocusTraversalPolicy. Top-level components * use this value on their creation to initialize their own focus traversal * policy by explicit call to Container.setFocusTraversalPolicy. * Note: this call doesn't affect already created components as they have * their policy initialized. Only new components will use this policy as * their default policy. * * @param defaultPolicy the new, default FocusTraversalPolicy * @see #getDefaultFocusTraversalPolicy * @see Container#setFocusTraversalPolicy * @see Container#getFocusTraversalPolicy * @throws IllegalArgumentException if defaultPolicy is null */ public void setDefaultFocusTraversalPolicy(FocusTraversalPolicy defaultPolicy) { if (defaultPolicy == null) { throw new IllegalArgumentException("default focus traversal policy cannot be null"); } FocusTraversalPolicy oldPolicy; synchronized (this) { oldPolicy = this.defaultPolicy; this.defaultPolicy = defaultPolicy; } firePropertyChange("defaultFocusTraversalPolicy", oldPolicy, defaultPolicy); } /** * Sets the default focus traversal keys for a given traversal operation. * This traversal key {@code Set} will be in effect on all * {@code Window}s that have no such {@code Set} of * their own explicitly defined. This {@code Set} will also be * inherited, recursively, by any child {@code Component} of * those {@code Windows} that has * no such {@code Set} of its own explicitly defined. * <p> * The default values for the default focus traversal keys are * implementation-dependent. Sun recommends that all implementations for a * particular native platform use the same default values. The * recommendations for Windows and Unix are listed below. These * recommendations are used in the Sun AWT implementations. * * <table class="striped"> * <caption>Recommended default values for focus traversal keys</caption> * <thead> * <tr> * <th scope="col">Identifier * <th scope="col">Meaning * <th scope="col">Default * </thead> * <tbody> * <tr> * <th scope="row">{@code KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS} * <td>Normal forward keyboard traversal * <td>{@code TAB} on {@code KEY_PRESSED}, {@code CTRL-TAB} on * {@code KEY_PRESSED} * <tr> * <th scope="row">{@code KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS} * <td>Normal reverse keyboard traversal * <td>{@code SHIFT-TAB} on {@code KEY_PRESSED}, {@code CTRL-SHIFT-TAB} * on {@code KEY_PRESSED} * <tr> * <th scope="row">{@code KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS} * <td>Go up one focus traversal cycle * <td>none * <tr> * <th scope="row">{@code KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS} * <td>Go down one focus traversal cycle * <td>none * </tbody> * </table> * * To disable a traversal key, use an empty {@code Set}; * {@code Collections.EMPTY_SET} is recommended. * <p> * Using the {@code AWTKeyStroke} API, client code can * specify on which of two * specific {@code KeyEvent}s, {@code KEY_PRESSED} or * {@code KEY_RELEASED}, the focus traversal operation will * occur. Regardless of which {@code KeyEvent} is specified, * however, all {@code KeyEvent}s related to the focus * traversal key, including the associated {@code KEY_TYPED} * event, will be consumed, and will not be dispatched * to any {@code Component}. It is a runtime error to * specify a {@code KEY_TYPED} event as * mapping to a focus traversal operation, or to map the same event to * multiple default focus traversal operations. * <p> * This method may throw a {@code ClassCastException} if any {@code Object} * in {@code keystrokes} is not an {@code AWTKeyStroke}. * * @param id one of * {@code KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS}, * {@code KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS}, * {@code KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS}, or * {@code KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS} * @param keystrokes the Set of {@code AWTKeyStroke}s for the * specified operation * @see #getDefaultFocusTraversalKeys * @see Component#setFocusTraversalKeys * @see Component#getFocusTraversalKeys * @throws IllegalArgumentException if id is not one of * {@code KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS}, * {@code KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS}, * {@code KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS}, or * {@code KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS}, * or if keystrokes is {@code null}, * or if keystrokes contains {@code null}, * or if any keystroke * represents a {@code KEY_TYPED} event, * or if any keystroke already maps * to another default focus traversal operation */ public void setDefaultFocusTraversalKeys(int id, Set<? extends AWTKeyStroke> keystrokes) { if (id < 0 || id >= TRAVERSAL_KEY_LENGTH) { throw new IllegalArgumentException("invalid focus traversal key identifier"); } if (keystrokes == null) { throw new IllegalArgumentException("cannot set null Set of default focus traversal keys"); } Set<AWTKeyStroke> oldKeys; synchronized (this) { for (AWTKeyStroke keystroke : keystrokes) { if (keystroke == null) { throw new IllegalArgumentException("cannot set null focus traversal key"); } if (keystroke.getKeyChar() != KeyEvent.CHAR_UNDEFINED) { throw new IllegalArgumentException("focus traversal keys cannot map to KEY_TYPED events"); } // Check to see if key already maps to another traversal // operation for (int i = 0; i < TRAVERSAL_KEY_LENGTH; i++) { if (i == id) { continue; } if (defaultFocusTraversalKeys[i].contains(keystroke)) { throw new IllegalArgumentException("focus traversal keys must be unique for a Component"); } } } oldKeys = defaultFocusTraversalKeys[id]; defaultFocusTraversalKeys[id] = Collections.unmodifiableSet(new HashSet<>(keystrokes)); } firePropertyChange(defaultFocusTraversalKeyPropertyNames[id], oldKeys, keystrokes); } /** * Returns a Set of default focus traversal keys for a given traversal * operation. This traversal key Set will be in effect on all Windows that * have no such Set of their own explicitly defined. This Set will also be * inherited, recursively, by any child Component of those Windows that has * no such Set of its own explicitly defined. (See * {@code setDefaultFocusTraversalKeys} for a full description of each * operation.) * * @param id one of KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, or * KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS * @return the {@code Set} of {@code AWTKeyStroke}s * for the specified operation; the {@code Set} * will be unmodifiable, and may be empty; {@code null} * will never be returned * @see #setDefaultFocusTraversalKeys * @see Component#setFocusTraversalKeys * @see Component#getFocusTraversalKeys * @throws IllegalArgumentException if id is not one of * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, or * KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS */ public Set<AWTKeyStroke> getDefaultFocusTraversalKeys(int id) { if (id < 0 || id >= TRAVERSAL_KEY_LENGTH) { throw new IllegalArgumentException("invalid focus traversal key identifier"); } // Okay to return Set directly because it is an unmodifiable view return defaultFocusTraversalKeys[id]; } /** * Returns the current focus cycle root, if the current focus cycle root is * in the same context as the calling thread. If the focus owner is itself * a focus cycle root, then it may be ambiguous as to which Components * represent the next and previous Components to focus during normal focus * traversal. In that case, the current focus cycle root is used to * differentiate among the possibilities. * <p> * This method is intended to be used only by KeyboardFocusManagers and * focus implementations. It is not for general client use. * * @return the current focus cycle root, or null if the current focus cycle * root is not a member of the calling thread's context * @see #getGlobalCurrentFocusCycleRoot * @see #setGlobalCurrentFocusCycleRoot */ public Container getCurrentFocusCycleRoot() { synchronized (KeyboardFocusManager.class) { if (currentFocusCycleRoot == null) { return null; } return (currentFocusCycleRoot.appContext == AppContext.getAppContext()) ? currentFocusCycleRoot : null; } } /** * Returns the current focus cycle root, even if the calling thread is in a * different context than the current focus cycle root. If the focus owner * is itself a focus cycle root, then it may be ambiguous as to which * Components represent the next and previous Components to focus during * normal focus traversal. In that case, the current focus cycle root is * used to differentiate among the possibilities. * * @return the current focus cycle root, or null if the current focus cycle * root is not a member of the calling thread's context * @see #getCurrentFocusCycleRoot * @see #setGlobalCurrentFocusCycleRoot * @throws SecurityException if this KeyboardFocusManager is not the * current KeyboardFocusManager for the calling thread's context * and if the calling thread does not have "replaceKeyboardFocusManager" * permission */ protected Container getGlobalCurrentFocusCycleRoot() throws SecurityException { synchronized (KeyboardFocusManager.class) { checkKFMSecurity(); return currentFocusCycleRoot; } } /** * Sets the current focus cycle root. If the focus owner is itself a focus * cycle root, then it may be ambiguous as to which Components represent * the next and previous Components to focus during normal focus traversal. * In that case, the current focus cycle root is used to differentiate * among the possibilities. * <p> * If a SecurityManager is installed, the calling thread must be granted * the "replaceKeyboardFocusManager" AWTPermission. If this permission is * not granted, this method will throw a SecurityException, and the current * focus cycle root will not be changed. * <p> * This method is intended to be used only by KeyboardFocusManagers and * focus implementations. It is not for general client use. * * @param newFocusCycleRoot the new focus cycle root * @see #getCurrentFocusCycleRoot * @see #getGlobalCurrentFocusCycleRoot * @throws SecurityException if the calling thread does not have * "replaceKeyboardFocusManager" permission */ public void setGlobalCurrentFocusCycleRoot(Container newFocusCycleRoot) throws SecurityException { checkReplaceKFMPermission(); Container oldFocusCycleRoot; synchronized (KeyboardFocusManager.class) { oldFocusCycleRoot = getCurrentFocusCycleRoot(); currentFocusCycleRoot = newFocusCycleRoot; } firePropertyChange("currentFocusCycleRoot", oldFocusCycleRoot, newFocusCycleRoot); } void setGlobalCurrentFocusCycleRootPriv(final Container newFocusCycleRoot) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { setGlobalCurrentFocusCycleRoot(newFocusCycleRoot); return null; } }); } /** * Adds a PropertyChangeListener to the listener list. The listener is * registered for all bound properties of this class, including the * following: * <ul> * <li>whether the KeyboardFocusManager is currently managing focus * for this application or applet's browser context * ("managingFocus")</li> * <li>the focus owner ("focusOwner")</li> * <li>the permanent focus owner ("permanentFocusOwner")</li> * <li>the focused Window ("focusedWindow")</li> * <li>the active Window ("activeWindow")</li> * <li>the default focus traversal policy * ("defaultFocusTraversalPolicy")</li> * <li>the Set of default FORWARD_TRAVERSAL_KEYS * ("forwardDefaultFocusTraversalKeys")</li> * <li>the Set of default BACKWARD_TRAVERSAL_KEYS * ("backwardDefaultFocusTraversalKeys")</li> * <li>the Set of default UP_CYCLE_TRAVERSAL_KEYS * ("upCycleDefaultFocusTraversalKeys")</li> * <li>the Set of default DOWN_CYCLE_TRAVERSAL_KEYS * ("downCycleDefaultFocusTraversalKeys")</li> * <li>the current focus cycle root ("currentFocusCycleRoot")</li> * </ul> * If listener is null, no exception is thrown and no action is performed. * * @param listener the PropertyChangeListener to be added * @see #removePropertyChangeListener * @see #getPropertyChangeListeners * @see #addPropertyChangeListener(java.lang.String,java.beans.PropertyChangeListener) */ public void addPropertyChangeListener(PropertyChangeListener listener) { if (listener != null) { synchronized (this) { if (changeSupport == null) { changeSupport = new PropertyChangeSupport(this); } changeSupport.addPropertyChangeListener(listener); } } } /** * Removes a PropertyChangeListener from the listener list. This method * should be used to remove the PropertyChangeListeners that were * registered for all bound properties of this class. * <p> * If listener is null, no exception is thrown and no action is performed. * * @param listener the PropertyChangeListener to be removed * @see #addPropertyChangeListener * @see #getPropertyChangeListeners * @see #removePropertyChangeListener(java.lang.String,java.beans.PropertyChangeListener) */ public void removePropertyChangeListener(PropertyChangeListener listener) { if (listener != null) { synchronized (this) { if (changeSupport != null) { changeSupport.removePropertyChangeListener(listener); } } } } /** * Returns an array of all the property change listeners * registered on this keyboard focus manager. * * @return all of this keyboard focus manager's * {@code PropertyChangeListener}s * or an empty array if no property change * listeners are currently registered * * @see #addPropertyChangeListener * @see #removePropertyChangeListener * @see #getPropertyChangeListeners(java.lang.String) * @since 1.4 */ public synchronized PropertyChangeListener[] getPropertyChangeListeners() { if (changeSupport == null) { changeSupport = new PropertyChangeSupport(this); } return changeSupport.getPropertyChangeListeners(); } /** * Adds a PropertyChangeListener to the listener list for a specific * property. The specified property may be user-defined, or one of the * following: * <ul> * <li>whether the KeyboardFocusManager is currently managing focus * for this application or applet's browser context * ("managingFocus")</li> * <li>the focus owner ("focusOwner")</li> * <li>the permanent focus owner ("permanentFocusOwner")</li> * <li>the focused Window ("focusedWindow")</li> * <li>the active Window ("activeWindow")</li> * <li>the default focus traversal policy * ("defaultFocusTraversalPolicy")</li> * <li>the Set of default FORWARD_TRAVERSAL_KEYS * ("forwardDefaultFocusTraversalKeys")</li> * <li>the Set of default BACKWARD_TRAVERSAL_KEYS * ("backwardDefaultFocusTraversalKeys")</li> * <li>the Set of default UP_CYCLE_TRAVERSAL_KEYS * ("upCycleDefaultFocusTraversalKeys")</li> * <li>the Set of default DOWN_CYCLE_TRAVERSAL_KEYS * ("downCycleDefaultFocusTraversalKeys")</li> * <li>the current focus cycle root ("currentFocusCycleRoot")</li> * </ul> * If listener is null, no exception is thrown and no action is performed. * * @param propertyName one of the property names listed above * @param listener the PropertyChangeListener to be added * @see #addPropertyChangeListener(java.beans.PropertyChangeListener) * @see #removePropertyChangeListener(java.lang.String,java.beans.PropertyChangeListener) * @see #getPropertyChangeListeners(java.lang.String) */ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { if (listener != null) { synchronized (this) { if (changeSupport == null) { changeSupport = new PropertyChangeSupport(this); } changeSupport.addPropertyChangeListener(propertyName, listener); } } } /** * Removes a PropertyChangeListener from the listener list for a specific * property. This method should be used to remove PropertyChangeListeners * that were registered for a specific bound property. * <p> * If listener is null, no exception is thrown and no action is performed. * * @param propertyName a valid property name * @param listener the PropertyChangeListener to be removed * @see #addPropertyChangeListener(java.lang.String,java.beans.PropertyChangeListener) * @see #getPropertyChangeListeners(java.lang.String) * @see #removePropertyChangeListener(java.beans.PropertyChangeListener) */ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { if (listener != null) { synchronized (this) { if (changeSupport != null) { changeSupport.removePropertyChangeListener(propertyName, listener); } } } } /** * Returns an array of all the {@code PropertyChangeListener}s * associated with the named property. * * @param propertyName the property name * @return all of the {@code PropertyChangeListener}s associated with * the named property or an empty array if no such listeners have * been added. * * @see #addPropertyChangeListener(java.lang.String,java.beans.PropertyChangeListener) * @see #removePropertyChangeListener(java.lang.String,java.beans.PropertyChangeListener) * @since 1.4 */ public synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { if (changeSupport == null) { changeSupport = new PropertyChangeSupport(this); } return changeSupport.getPropertyChangeListeners(propertyName); } /** * Fires a PropertyChangeEvent in response to a change in a bound property. * The event will be delivered to all registered PropertyChangeListeners. * No event will be delivered if oldValue and newValue are the same. * * @param propertyName the name of the property that has changed * @param oldValue the property's previous value * @param newValue the property's new value */ protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { if (oldValue == newValue) { return; } PropertyChangeSupport changeSupport = this.changeSupport; if (changeSupport != null) { changeSupport.firePropertyChange(propertyName, oldValue, newValue); } } /** * Adds a VetoableChangeListener to the listener list. The listener is * registered for all vetoable properties of this class, including the * following: * <ul> * <li>the focus owner ("focusOwner")</li> * <li>the permanent focus owner ("permanentFocusOwner")</li> * <li>the focused Window ("focusedWindow")</li> * <li>the active Window ("activeWindow")</li> * </ul> * If listener is null, no exception is thrown and no action is performed. * * @param listener the VetoableChangeListener to be added * @see #removeVetoableChangeListener * @see #getVetoableChangeListeners * @see #addVetoableChangeListener(java.lang.String,java.beans.VetoableChangeListener) */ public void addVetoableChangeListener(VetoableChangeListener listener) { if (listener != null) { synchronized (this) { if (vetoableSupport == null) { vetoableSupport = new VetoableChangeSupport(this); } vetoableSupport.addVetoableChangeListener(listener); } } } /** * Removes a VetoableChangeListener from the listener list. This method * should be used to remove the VetoableChangeListeners that were * registered for all vetoable properties of this class. * <p> * If listener is null, no exception is thrown and no action is performed. * * @param listener the VetoableChangeListener to be removed * @see #addVetoableChangeListener * @see #getVetoableChangeListeners * @see #removeVetoableChangeListener(java.lang.String,java.beans.VetoableChangeListener) */ public void removeVetoableChangeListener(VetoableChangeListener listener) { if (listener != null) { synchronized (this) { if (vetoableSupport != null) { vetoableSupport.removeVetoableChangeListener(listener); } } } } /** * Returns an array of all the vetoable change listeners * registered on this keyboard focus manager. * * @return all of this keyboard focus manager's * {@code VetoableChangeListener}s * or an empty array if no vetoable change * listeners are currently registered * * @see #addVetoableChangeListener * @see #removeVetoableChangeListener * @see #getVetoableChangeListeners(java.lang.String) * @since 1.4 */ public synchronized VetoableChangeListener[] getVetoableChangeListeners() { if (vetoableSupport == null) { vetoableSupport = new VetoableChangeSupport(this); } return vetoableSupport.getVetoableChangeListeners(); } /** * Adds a VetoableChangeListener to the listener list for a specific * property. The specified property may be user-defined, or one of the * following: * <ul> * <li>the focus owner ("focusOwner")</li> * <li>the permanent focus owner ("permanentFocusOwner")</li> * <li>the focused Window ("focusedWindow")</li> * <li>the active Window ("activeWindow")</li> * </ul> * If listener is null, no exception is thrown and no action is performed. * * @param propertyName one of the property names listed above * @param listener the VetoableChangeListener to be added * @see #addVetoableChangeListener(java.beans.VetoableChangeListener) * @see #removeVetoableChangeListener * @see #getVetoableChangeListeners */ public void addVetoableChangeListener(String propertyName, VetoableChangeListener listener) { if (listener != null) { synchronized (this) { if (vetoableSupport == null) { vetoableSupport = new VetoableChangeSupport(this); } vetoableSupport.addVetoableChangeListener(propertyName, listener); } } } /** * Removes a VetoableChangeListener from the listener list for a specific * property. This method should be used to remove VetoableChangeListeners * that were registered for a specific bound property. * <p> * If listener is null, no exception is thrown and no action is performed. * * @param propertyName a valid property name * @param listener the VetoableChangeListener to be removed * @see #addVetoableChangeListener * @see #getVetoableChangeListeners * @see #removeVetoableChangeListener(java.beans.VetoableChangeListener) */ public void removeVetoableChangeListener(String propertyName, VetoableChangeListener listener) { if (listener != null) { synchronized (this) { if (vetoableSupport != null) { vetoableSupport.removeVetoableChangeListener(propertyName, listener); } } } } /** * Returns an array of all the {@code VetoableChangeListener}s * associated with the named property. * * @param propertyName the property name * @return all of the {@code VetoableChangeListener}s associated with * the named property or an empty array if no such listeners have * been added. * * @see #addVetoableChangeListener(java.lang.String,java.beans.VetoableChangeListener) * @see #removeVetoableChangeListener(java.lang.String,java.beans.VetoableChangeListener) * @see #getVetoableChangeListeners * @since 1.4 */ public synchronized VetoableChangeListener[] getVetoableChangeListeners(String propertyName) { if (vetoableSupport == null) { vetoableSupport = new VetoableChangeSupport(this); } return vetoableSupport.getVetoableChangeListeners(propertyName); } /** * Fires a PropertyChangeEvent in response to a change in a vetoable * property. The event will be delivered to all registered * VetoableChangeListeners. If a VetoableChangeListener throws a * PropertyVetoException, a new event is fired reverting all * VetoableChangeListeners to the old value and the exception is then * rethrown. No event will be delivered if oldValue and newValue are the * same. * * @param propertyName the name of the property that has changed * @param oldValue the property's previous value * @param newValue the property's new value * @throws java.beans.PropertyVetoException if a * {@code VetoableChangeListener} threw * {@code PropertyVetoException} */ protected void fireVetoableChange(String propertyName, Object oldValue, Object newValue) throws PropertyVetoException { if (oldValue == newValue) { return; } VetoableChangeSupport vetoableSupport = this.vetoableSupport; if (vetoableSupport != null) { vetoableSupport.fireVetoableChange(propertyName, oldValue, newValue); } } /** * Adds a KeyEventDispatcher to this KeyboardFocusManager's dispatcher * chain. This KeyboardFocusManager will request that each * KeyEventDispatcher dispatch KeyEvents generated by the user before * finally dispatching the KeyEvent itself. KeyEventDispatchers will be * notified in the order in which they were added. Notifications will halt * as soon as one KeyEventDispatcher returns {@code true} from its * {@code dispatchKeyEvent} method. There is no limit to the total * number of KeyEventDispatchers which can be added, nor to the number of * times which a particular KeyEventDispatcher instance can be added. * <p> * If a null dispatcher is specified, no action is taken and no exception * is thrown. * <p> * In a multithreaded application, {@link KeyEventDispatcher} behaves * the same as other AWT listeners. See * <a href="doc-files/AWTThreadIssues.html#ListenersThreads" * >AWT Threading Issues</a> for more details. * * @param dispatcher the KeyEventDispatcher to add to the dispatcher chain * @see #removeKeyEventDispatcher */ public void addKeyEventDispatcher(KeyEventDispatcher dispatcher) { if (dispatcher != null) { synchronized (this) { if (keyEventDispatchers == null) { keyEventDispatchers = new java.util.LinkedList<>(); } keyEventDispatchers.add(dispatcher); } } } /** * Removes a KeyEventDispatcher which was previously added to this * KeyboardFocusManager's dispatcher chain. This KeyboardFocusManager * cannot itself be removed, unless it was explicitly re-registered via a * call to {@code addKeyEventDispatcher}. * <p> * If a null dispatcher is specified, if the specified dispatcher is not * in the dispatcher chain, or if this KeyboardFocusManager is specified * without having been explicitly re-registered, no action is taken and no * exception is thrown. * <p> * In a multithreaded application, {@link KeyEventDispatcher} behaves * the same as other AWT listeners. See * <a href="doc-files/AWTThreadIssues.html#ListenersThreads" * >AWT Threading Issues</a> for more details. * * @param dispatcher the KeyEventDispatcher to remove from the dispatcher * chain * @see #addKeyEventDispatcher */ public void removeKeyEventDispatcher(KeyEventDispatcher dispatcher) { if (dispatcher != null) { synchronized (this) { if (keyEventDispatchers != null) { keyEventDispatchers.remove(dispatcher); } } } } /** * Returns this KeyboardFocusManager's KeyEventDispatcher chain as a List. * The List will not include this KeyboardFocusManager unless it was * explicitly re-registered via a call to * {@code addKeyEventDispatcher}. If no other KeyEventDispatchers are * registered, implementations are free to return null or a List of length * 0. Client code should not assume one behavior over another, nor should * it assume that the behavior, once established, will not change. * * @return a possibly null or empty List of KeyEventDispatchers * @see #addKeyEventDispatcher * @see #removeKeyEventDispatcher */ @SuppressWarnings("unchecked") // Cast of result of clone protected synchronized java.util.List<KeyEventDispatcher> getKeyEventDispatchers() { return (keyEventDispatchers != null) ? (java.util.List<KeyEventDispatcher>) keyEventDispatchers.clone() : null; } /** * Adds a KeyEventPostProcessor to this KeyboardFocusManager's post- * processor chain. After a KeyEvent has been dispatched to and handled by * its target, KeyboardFocusManager will request that each * KeyEventPostProcessor perform any necessary post-processing as part * of the KeyEvent's final resolution. KeyEventPostProcessors * will be notified in the order in which they were added; the current * KeyboardFocusManager will be notified last. Notifications will halt * as soon as one KeyEventPostProcessor returns {@code true} from its * {@code postProcessKeyEvent} method. There is no limit to the * total number of KeyEventPostProcessors that can be added, nor to the * number of times that a particular KeyEventPostProcessor instance can be * added. * <p> * If a null post-processor is specified, no action is taken and no * exception is thrown. * <p> * In a multithreaded application, {@link KeyEventPostProcessor} behaves * the same as other AWT listeners. See * <a href="doc-files/AWTThreadIssues.html#ListenersThreads" * >AWT Threading Issues</a> for more details. * * @param processor the KeyEventPostProcessor to add to the post-processor * chain * @see #removeKeyEventPostProcessor */ public void addKeyEventPostProcessor(KeyEventPostProcessor processor) { if (processor != null) { synchronized (this) { if (keyEventPostProcessors == null) { keyEventPostProcessors = new java.util.LinkedList<>(); } keyEventPostProcessors.add(processor); } } } /** * Removes a previously added KeyEventPostProcessor from this * KeyboardFocusManager's post-processor chain. This KeyboardFocusManager * cannot itself be entirely removed from the chain. Only additional * references added via {@code addKeyEventPostProcessor} can be * removed. * <p> * If a null post-processor is specified, if the specified post-processor * is not in the post-processor chain, or if this KeyboardFocusManager is * specified without having been explicitly added, no action is taken and * no exception is thrown. * <p> * In a multithreaded application, {@link KeyEventPostProcessor} behaves * the same as other AWT listeners. See * <a href="doc-files/AWTThreadIssues.html#ListenersThreads" * >AWT Threading Issues</a> for more details. * * @param processor the KeyEventPostProcessor to remove from the post- * processor chain * @see #addKeyEventPostProcessor */ public void removeKeyEventPostProcessor(KeyEventPostProcessor processor) { if (processor != null) { synchronized (this) { if (keyEventPostProcessors != null) { keyEventPostProcessors.remove(processor); } } } } /** * Returns this KeyboardFocusManager's KeyEventPostProcessor chain as a * List. The List will not include this KeyboardFocusManager unless it was * explicitly added via a call to {@code addKeyEventPostProcessor}. If * no KeyEventPostProcessors are registered, implementations are free to * return null or a List of length 0. Client code should not assume one * behavior over another, nor should it assume that the behavior, once * established, will not change. * * @return a possibly null or empty List of KeyEventPostProcessors * @see #addKeyEventPostProcessor * @see #removeKeyEventPostProcessor */ @SuppressWarnings("unchecked") // Cast of result of clone protected java.util.List<KeyEventPostProcessor> getKeyEventPostProcessors() { return (keyEventPostProcessors != null) ? (java.util.List<KeyEventPostProcessor>) keyEventPostProcessors.clone() : null; } static void setMostRecentFocusOwner(Component component) { Component window = component; while (window != null && !(window instanceof Window)) { window = window.parent; } if (window != null) { setMostRecentFocusOwner((Window) window, component); } } static synchronized void setMostRecentFocusOwner(Window window, Component component) { // ATTN: component has a strong reference to window via chain // of Component.parent fields. Since WeakHasMap refers to its // values strongly, we need to break the strong link from the // value (component) back to its key (window). WeakReference<Component> weakValue = null; if (component != null) { weakValue = new WeakReference<>(component); } mostRecentFocusOwners.put(window, weakValue); } static void clearMostRecentFocusOwner(Component comp) { Container window; if (comp == null) { return; } synchronized (comp.getTreeLock()) { window = comp.getParent(); while (window != null && !(window instanceof Window)) { window = window.getParent(); } } synchronized (KeyboardFocusManager.class) { if ((window != null) && (getMostRecentFocusOwner((Window) window) == comp)) { setMostRecentFocusOwner((Window) window, null); } // Also clear temporary lost component stored in Window if (window != null) { Window realWindow = (Window) window; if (realWindow.getTemporaryLostComponent() == comp) { realWindow.setTemporaryLostComponent(null); } } } } /* * Please be careful changing this method! It is called from * javax.swing.JComponent.runInputVerifier() using reflection. */ static synchronized Component getMostRecentFocusOwner(Window window) { WeakReference<Component> weakValue = mostRecentFocusOwners.get(window); return weakValue == null ? null : weakValue.get(); } /** * This method is called by the AWT event dispatcher requesting that the * current KeyboardFocusManager dispatch the specified event on its behalf. * It is expected that all KeyboardFocusManagers will dispatch all * FocusEvents, all WindowEvents related to focus, and all KeyEvents. * These events should be dispatched based on the KeyboardFocusManager's * notion of the focus owner and the focused and active Windows, sometimes * overriding the source of the specified AWTEvent. Dispatching must be * done using {@code redispatchEvent} to prevent the AWT event * dispatcher from recursively requesting that the KeyboardFocusManager * dispatch the event again. If this method returns {@code false}, * then the AWT event dispatcher will attempt to dispatch the event itself. * * @param e the AWTEvent to be dispatched * @return {@code true} if this method dispatched the event; * {@code false} otherwise * @see #redispatchEvent * @see #dispatchKeyEvent */ public abstract boolean dispatchEvent(AWTEvent e); /** * Redispatches an AWTEvent in such a way that the AWT event dispatcher * will not recursively request that the KeyboardFocusManager, or any * installed KeyEventDispatchers, dispatch the event again. Client * implementations of {@code dispatchEvent} and client-defined * KeyEventDispatchers must call {@code redispatchEvent(target, e)} * instead of {@code target.dispatchEvent(e)} to dispatch an event. * <p> * This method is intended to be used only by KeyboardFocusManagers and * KeyEventDispatchers. It is not for general client use. * * @param target the Component to which the event should be dispatched * @param e the event to dispatch * @see #dispatchEvent * @see KeyEventDispatcher */ public final void redispatchEvent(Component target, AWTEvent e) { e.focusManagerIsDispatching = true; target.dispatchEvent(e); e.focusManagerIsDispatching = false; } /** * Typically this method will be called by {@code dispatchEvent} if no * other KeyEventDispatcher in the dispatcher chain dispatched the * KeyEvent, or if no other KeyEventDispatchers are registered. If an * implementation of this method returns {@code false}, * {@code dispatchEvent} may try to dispatch the KeyEvent itself, or * may simply return {@code false}. If {@code true} is returned, * {@code dispatchEvent} should return {@code true} as well. * * @param e the KeyEvent which the current KeyboardFocusManager has * requested that this KeyEventDispatcher dispatch * @return {@code true} if the KeyEvent was dispatched; * {@code false} otherwise * @see #dispatchEvent */ public abstract boolean dispatchKeyEvent(KeyEvent e); /** * This method will be called by {@code dispatchKeyEvent}. * By default, this method will handle any unconsumed KeyEvents that * map to an AWT {@code MenuShortcut} by consuming the event * and activating the shortcut. * * @param e the KeyEvent to post-process * @return {@code true} to indicate that no other * KeyEventPostProcessor will be notified of the KeyEvent. * @see #dispatchKeyEvent * @see MenuShortcut */ public abstract boolean postProcessKeyEvent(KeyEvent e); /** * This method initiates a focus traversal operation if and only if the * KeyEvent represents a focus traversal key for the specified * focusedComponent. It is expected that focusedComponent is the current * focus owner, although this need not be the case. If it is not, * focus traversal will nevertheless proceed as if focusedComponent * were the current focus owner. * * @param focusedComponent the Component that will be the basis for a focus * traversal operation if the specified event represents a focus * traversal key for the Component * @param e the event that may represent a focus traversal key */ public abstract void processKeyEvent(Component focusedComponent, KeyEvent e); /** * Called by the AWT to notify the KeyboardFocusManager that it should * delay dispatching of KeyEvents until the specified Component becomes * the focus owner. If client code requests a focus change, and the AWT * determines that this request might be granted by the native windowing * system, then the AWT will call this method. It is the responsibility of * the KeyboardFocusManager to delay dispatching of KeyEvents with * timestamps later than the specified time stamp until the specified * Component receives a FOCUS_GAINED event, or the AWT cancels the delay * request by invoking {@code dequeueKeyEvents} or * {@code discardKeyEvents}. * * @param after timestamp of current event, or the current, system time if * the current event has no timestamp, or the AWT cannot determine * which event is currently being handled * @param untilFocused Component which should receive a FOCUS_GAINED event * before any pending KeyEvents * @see #dequeueKeyEvents * @see #discardKeyEvents */ protected abstract void enqueueKeyEvents(long after, Component untilFocused); /** * Called by the AWT to notify the KeyboardFocusManager that it should * cancel delayed dispatching of KeyEvents. All KeyEvents which were * enqueued because of a call to {@code enqueueKeyEvents} with the * same timestamp and Component should be released for normal dispatching * to the current focus owner. If the given timestamp is less than zero, * the outstanding enqueue request for the given Component with the <b> * oldest</b> timestamp (if any) should be cancelled. * * @param after the timestamp specified in the call to * {@code enqueueKeyEvents}, or any value < 0 * @param untilFocused the Component specified in the call to * {@code enqueueKeyEvents} * @see #enqueueKeyEvents * @see #discardKeyEvents */ protected abstract void dequeueKeyEvents(long after, Component untilFocused); /** * Called by the AWT to notify the KeyboardFocusManager that it should * cancel delayed dispatching of KeyEvents. All KeyEvents which were * enqueued because of one or more calls to {@code enqueueKeyEvents} * with the same Component should be discarded. * * @param comp the Component specified in one or more calls to * {@code enqueueKeyEvents} * @see #enqueueKeyEvents * @see #dequeueKeyEvents */ protected abstract void discardKeyEvents(Component comp); /** * Focuses the Component after aComponent, typically based on a * FocusTraversalPolicy. * * @param aComponent the Component that is the basis for the focus * traversal operation * @see FocusTraversalPolicy */ public abstract void focusNextComponent(Component aComponent); /** * Focuses the Component before aComponent, typically based on a * FocusTraversalPolicy. * * @param aComponent the Component that is the basis for the focus * traversal operation * @see FocusTraversalPolicy */ public abstract void focusPreviousComponent(Component aComponent); /** * Moves the focus up one focus traversal cycle. Typically, the focus owner * is set to aComponent's focus cycle root, and the current focus cycle * root is set to the new focus owner's focus cycle root. If, however, * aComponent's focus cycle root is a Window, then typically the focus * owner is set to the Window's default Component to focus, and the current * focus cycle root is unchanged. * * @param aComponent the Component that is the basis for the focus * traversal operation */ public abstract void upFocusCycle(Component aComponent); /** * Moves the focus down one focus traversal cycle. Typically, if * aContainer is a focus cycle root, then the focus owner is set to * aContainer's default Component to focus, and the current focus cycle * root is set to aContainer. If aContainer is not a focus cycle root, then * no focus traversal operation occurs. * * @param aContainer the Container that is the basis for the focus * traversal operation */ public abstract void downFocusCycle(Container aContainer); /** * Focuses the Component after the current focus owner. */ public final void focusNextComponent() { Component focusOwner = getFocusOwner(); if (focusOwner != null) { focusNextComponent(focusOwner); } } /** * Focuses the Component before the current focus owner. */ public final void focusPreviousComponent() { Component focusOwner = getFocusOwner(); if (focusOwner != null) { focusPreviousComponent(focusOwner); } } /** * Moves the focus up one focus traversal cycle from the current focus * owner. Typically, the new focus owner is set to the current focus * owner's focus cycle root, and the current focus cycle root is set to the * new focus owner's focus cycle root. If, however, the current focus * owner's focus cycle root is a Window, then typically the focus owner is * set to the focus cycle root's default Component to focus, and the * current focus cycle root is unchanged. */ public final void upFocusCycle() { Component focusOwner = getFocusOwner(); if (focusOwner != null) { upFocusCycle(focusOwner); } } /** * Moves the focus down one focus traversal cycle from the current focus * owner, if and only if the current focus owner is a Container that is a * focus cycle root. Typically, the focus owner is set to the current focus * owner's default Component to focus, and the current focus cycle root is * set to the current focus owner. If the current focus owner is not a * Container that is a focus cycle root, then no focus traversal operation * occurs. */ public final void downFocusCycle() { Component focusOwner = getFocusOwner(); if (focusOwner instanceof Container) { downFocusCycle((Container) focusOwner); } } /** * Dumps the list of focus requests to stderr */ void dumpRequests() { System.err.println(">>> Requests dump, time: " + System.currentTimeMillis()); synchronized (heavyweightRequests) { for (HeavyweightFocusRequest req : heavyweightRequests) { System.err.println(">>> Req: " + req); } } System.err.println(""); } private static final class LightweightFocusRequest { final Component component; final boolean temporary; final FocusEvent.Cause cause; LightweightFocusRequest(Component component, boolean temporary, FocusEvent.Cause cause) { this.component = component; this.temporary = temporary; this.cause = cause; } public String toString() { return "LightweightFocusRequest[component=" + component + ",temporary=" + temporary + ", cause=" + cause + "]"; } } private static final class HeavyweightFocusRequest { final Component heavyweight; final LinkedList<LightweightFocusRequest> lightweightRequests; static final HeavyweightFocusRequest CLEAR_GLOBAL_FOCUS_OWNER = new HeavyweightFocusRequest(); private HeavyweightFocusRequest() { heavyweight = null; lightweightRequests = null; } HeavyweightFocusRequest(Component heavyweight, Component descendant, boolean temporary, FocusEvent.Cause cause) { if (log.isLoggable(PlatformLogger.Level.FINE)) { if (heavyweight == null) { log.fine("Assertion (heavyweight != null) failed"); } } this.heavyweight = heavyweight; this.lightweightRequests = new LinkedList<LightweightFocusRequest>(); addLightweightRequest(descendant, temporary, cause); } boolean addLightweightRequest(Component descendant, boolean temporary, FocusEvent.Cause cause) { if (log.isLoggable(PlatformLogger.Level.FINE)) { if (this == HeavyweightFocusRequest.CLEAR_GLOBAL_FOCUS_OWNER) { log.fine("Assertion (this != HeavyweightFocusRequest.CLEAR_GLOBAL_FOCUS_OWNER) failed"); } if (descendant == null) { log.fine("Assertion (descendant != null) failed"); } } Component lastDescendant = ((lightweightRequests.size() > 0) ? lightweightRequests.getLast().component : null); if (descendant != lastDescendant) { // Not a duplicate request lightweightRequests.add(new LightweightFocusRequest(descendant, temporary, cause)); return true; } else { return false; } } LightweightFocusRequest getFirstLightweightRequest() { if (this == CLEAR_GLOBAL_FOCUS_OWNER) { return null; } return lightweightRequests.getFirst(); } public String toString() { boolean first = true; String str = "HeavyweightFocusRequest[heavyweight=" + heavyweight + ",lightweightRequests="; if (lightweightRequests == null) { str += null; } else { str += "["; for (LightweightFocusRequest lwRequest : lightweightRequests) { if (first) { first = false; } else { str += ","; } str += lwRequest; } str += "]"; } str += "]"; return str; } } /* * heavyweightRequests is used as a monitor for synchronized changes of * currentLightweightRequests, clearingCurrentLightweightRequests and * newFocusOwner. */ private static LinkedList<HeavyweightFocusRequest> heavyweightRequests = new LinkedList<HeavyweightFocusRequest>(); private static LinkedList<LightweightFocusRequest> currentLightweightRequests; private static boolean clearingCurrentLightweightRequests; private static boolean allowSyncFocusRequests = true; private static Component newFocusOwner = null; private static volatile boolean disableRestoreFocus; static final int SNFH_FAILURE = 0; static final int SNFH_SUCCESS_HANDLED = 1; static final int SNFH_SUCCESS_PROCEED = 2; static boolean processSynchronousLightweightTransfer(Component heavyweight, Component descendant, boolean temporary, boolean focusedWindowChangeAllowed, long time) { Window parentWindow = SunToolkit.getContainingWindow(heavyweight); if (parentWindow == null || !parentWindow.syncLWRequests) { return false; } if (descendant == null) { // Focus transfers from a lightweight child back to the // heavyweight Container should be treated like lightweight // focus transfers. descendant = heavyweight; } KeyboardFocusManager manager = getCurrentKeyboardFocusManager(SunToolkit.targetToAppContext(descendant)); FocusEvent currentFocusOwnerEvent = null; FocusEvent newFocusOwnerEvent = null; Component currentFocusOwner = manager.getGlobalFocusOwner(); synchronized (heavyweightRequests) { HeavyweightFocusRequest hwFocusRequest = getLastHWRequest(); if (hwFocusRequest == null && heavyweight == manager.getNativeFocusOwner() && allowSyncFocusRequests) { if (descendant == currentFocusOwner) { // Redundant request. return true; } // 'heavyweight' owns the native focus and there are no pending // requests. 'heavyweight' must be a Container and // 'descendant' must not be the focus owner. Otherwise, // we would never have gotten this far. manager.enqueueKeyEvents(time, descendant); hwFocusRequest = new HeavyweightFocusRequest(heavyweight, descendant, temporary, FocusEvent.Cause.UNKNOWN); heavyweightRequests.add(hwFocusRequest); if (currentFocusOwner != null) { currentFocusOwnerEvent = new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, temporary, descendant); } newFocusOwnerEvent = new FocusEvent(descendant, FocusEvent.FOCUS_GAINED, temporary, currentFocusOwner); } } boolean result = false; final boolean clearing = clearingCurrentLightweightRequests; Throwable caughtEx = null; try { clearingCurrentLightweightRequests = false; synchronized (Component.LOCK) { if (currentFocusOwnerEvent != null && currentFocusOwner != null) { ((AWTEvent) currentFocusOwnerEvent).isPosted = true; caughtEx = dispatchAndCatchException(caughtEx, currentFocusOwner, currentFocusOwnerEvent); result = true; } if (newFocusOwnerEvent != null && descendant != null) { ((AWTEvent) newFocusOwnerEvent).isPosted = true; caughtEx = dispatchAndCatchException(caughtEx, descendant, newFocusOwnerEvent); result = true; } } } finally { clearingCurrentLightweightRequests = clearing; } if (caughtEx instanceof RuntimeException) { throw (RuntimeException) caughtEx; } else if (caughtEx instanceof Error) { throw (Error) caughtEx; } return result; } /** * Indicates whether the native implementation should proceed with a * pending, native focus request. Before changing the focus at the native * level, the AWT implementation should always call this function for * permission. This function will reject the request if a duplicate request * preceded it, or if the specified heavyweight Component already owns the * focus and no native focus changes are pending. Otherwise, the request * will be approved and the focus request list will be updated so that, * if necessary, the proper descendant will be focused when the * corresponding FOCUS_GAINED event on the heavyweight is received. * * An implementation must ensure that calls to this method and native * focus changes are atomic. If this is not guaranteed, then the ordering * of the focus request list may be incorrect, leading to errors in the * type-ahead mechanism. Typically this is accomplished by only calling * this function from the native event pumping thread, or by holding a * global, native lock during invocation. */ static int shouldNativelyFocusHeavyweight(Component heavyweight, Component descendant, boolean temporary, boolean focusedWindowChangeAllowed, long time, FocusEvent.Cause cause) { if (log.isLoggable(PlatformLogger.Level.FINE)) { if (heavyweight == null) { log.fine("Assertion (heavyweight != null) failed"); } if (time == 0) { log.fine("Assertion (time != 0) failed"); } } if (descendant == null) { // Focus transfers from a lightweight child back to the // heavyweight Container should be treated like lightweight // focus transfers. descendant = heavyweight; } KeyboardFocusManager manager = getCurrentKeyboardFocusManager(SunToolkit.targetToAppContext(descendant)); KeyboardFocusManager thisManager = getCurrentKeyboardFocusManager(); Component currentFocusOwner = thisManager.getGlobalFocusOwner(); Component nativeFocusOwner = thisManager.getNativeFocusOwner(); Window nativeFocusedWindow = thisManager.getNativeFocusedWindow(); if (focusLog.isLoggable(PlatformLogger.Level.FINER)) { focusLog.finer("SNFH for {0} in {1}", String.valueOf(descendant), String.valueOf(heavyweight)); } if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) { focusLog.finest("0. Current focus owner {0}", String.valueOf(currentFocusOwner)); focusLog.finest("0. Native focus owner {0}", String.valueOf(nativeFocusOwner)); focusLog.finest("0. Native focused window {0}", String.valueOf(nativeFocusedWindow)); } synchronized (heavyweightRequests) { HeavyweightFocusRequest hwFocusRequest = getLastHWRequest(); if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) { focusLog.finest("Request {0}", String.valueOf(hwFocusRequest)); } if (hwFocusRequest == null && heavyweight == nativeFocusOwner && heavyweight.getContainingWindow() == nativeFocusedWindow) { if (descendant == currentFocusOwner) { // Redundant request. if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) focusLog.finest("1. SNFH_FAILURE for {0}", String.valueOf(descendant)); return SNFH_FAILURE; } // 'heavyweight' owns the native focus and there are no pending // requests. 'heavyweight' must be a Container and // 'descendant' must not be the focus owner. Otherwise, // we would never have gotten this far. manager.enqueueKeyEvents(time, descendant); hwFocusRequest = new HeavyweightFocusRequest(heavyweight, descendant, temporary, cause); heavyweightRequests.add(hwFocusRequest); if (currentFocusOwner != null) { FocusEvent currentFocusOwnerEvent = new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, temporary, descendant, cause); // Fix 5028014. Rolled out. // SunToolkit.postPriorityEvent(currentFocusOwnerEvent); SunToolkit.postEvent(currentFocusOwner.appContext, currentFocusOwnerEvent); } FocusEvent newFocusOwnerEvent = new FocusEvent(descendant, FocusEvent.FOCUS_GAINED, temporary, currentFocusOwner, cause); // Fix 5028014. Rolled out. // SunToolkit.postPriorityEvent(newFocusOwnerEvent); SunToolkit.postEvent(descendant.appContext, newFocusOwnerEvent); if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) focusLog.finest("2. SNFH_HANDLED for {0}", String.valueOf(descendant)); return SNFH_SUCCESS_HANDLED; } else if (hwFocusRequest != null && hwFocusRequest.heavyweight == heavyweight) { // 'heavyweight' doesn't have the native focus right now, but // if all pending requests were completed, it would. Add // descendant to the heavyweight's list of pending // lightweight focus transfers. if (hwFocusRequest.addLightweightRequest(descendant, temporary, cause)) { manager.enqueueKeyEvents(time, descendant); } if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) { focusLog.finest("3. SNFH_HANDLED for lightweight" + descendant + " in " + heavyweight); } return SNFH_SUCCESS_HANDLED; } else { if (!focusedWindowChangeAllowed) { // For purposes of computing oldFocusedWindow, we should look at // the second to last HeavyweightFocusRequest on the queue iff the // last HeavyweightFocusRequest is CLEAR_GLOBAL_FOCUS_OWNER. If // there is no second to last HeavyweightFocusRequest, null is an // acceptable value. if (hwFocusRequest == HeavyweightFocusRequest.CLEAR_GLOBAL_FOCUS_OWNER) { int size = heavyweightRequests.size(); hwFocusRequest = (size >= 2) ? heavyweightRequests.get(size - 2) : null; } if (focusedWindowChanged(heavyweight, (hwFocusRequest != null) ? hwFocusRequest.heavyweight : nativeFocusedWindow)) { if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) { focusLog.finest("4. SNFH_FAILURE for " + descendant); } return SNFH_FAILURE; } } manager.enqueueKeyEvents(time, descendant); heavyweightRequests.add(new HeavyweightFocusRequest(heavyweight, descendant, temporary, cause)); if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) { focusLog.finest("5. SNFH_PROCEED for " + descendant); } return SNFH_SUCCESS_PROCEED; } } } /** * Returns the Window which will be active after processing this request, * or null if this is a duplicate request. The active Window is useful * because some native platforms do not support setting the native focus * owner to null. On these platforms, the obvious choice is to set the * focus owner to the focus proxy of the active Window. */ static Window markClearGlobalFocusOwner() { // need to call this out of synchronized block to avoid possible deadlock // see 6454631. final Component nativeFocusedWindow = getCurrentKeyboardFocusManager().getNativeFocusedWindow(); synchronized (heavyweightRequests) { HeavyweightFocusRequest hwFocusRequest = getLastHWRequest(); if (hwFocusRequest == HeavyweightFocusRequest.CLEAR_GLOBAL_FOCUS_OWNER) { // duplicate request return null; } heavyweightRequests.add(HeavyweightFocusRequest.CLEAR_GLOBAL_FOCUS_OWNER); Component activeWindow = ((hwFocusRequest != null) ? SunToolkit.getContainingWindow(hwFocusRequest.heavyweight) : nativeFocusedWindow); while (activeWindow != null && !((activeWindow instanceof Frame) || (activeWindow instanceof Dialog))) { activeWindow = activeWindow.getParent_NoClientCode(); } return (Window) activeWindow; } } Component getCurrentWaitingRequest(Component parent) { synchronized (heavyweightRequests) { HeavyweightFocusRequest hwFocusRequest = getFirstHWRequest(); if (hwFocusRequest != null) { if (hwFocusRequest.heavyweight == parent) { LightweightFocusRequest lwFocusRequest = hwFocusRequest.lightweightRequests.getFirst(); if (lwFocusRequest != null) { return lwFocusRequest.component; } } } } return null; } static boolean isAutoFocusTransferEnabled() { synchronized (heavyweightRequests) { return (heavyweightRequests.size() == 0) && !disableRestoreFocus && (null == currentLightweightRequests); } } static boolean isAutoFocusTransferEnabledFor(Component comp) { return isAutoFocusTransferEnabled() && comp.isAutoFocusTransferOnDisposal(); } /* * Used to process exceptions in dispatching focus event (in focusLost/focusGained callbacks). * @param ex previously caught exception that may be processed right here, or null * @param comp the component to dispatch the event to * @param event the event to dispatch to the component */ private static Throwable dispatchAndCatchException(Throwable ex, Component comp, FocusEvent event) { Throwable retEx = null; try { comp.dispatchEvent(event); } catch (RuntimeException re) { retEx = re; } catch (Error er) { retEx = er; } if (retEx != null) { if (ex != null) { handleException(ex); } return retEx; } return ex; } private static void handleException(Throwable ex) { ex.printStackTrace(); } static void processCurrentLightweightRequests() { KeyboardFocusManager manager = getCurrentKeyboardFocusManager(); LinkedList<LightweightFocusRequest> localLightweightRequests = null; Component globalFocusOwner = manager.getGlobalFocusOwner(); if ((globalFocusOwner != null) && (globalFocusOwner.appContext != AppContext.getAppContext())) { // The current app context differs from the app context of a focus // owner (and all pending lightweight requests), so we do nothing // now and wait for a next event. return; } synchronized (heavyweightRequests) { if (currentLightweightRequests != null) { clearingCurrentLightweightRequests = true; disableRestoreFocus = true; localLightweightRequests = currentLightweightRequests; allowSyncFocusRequests = (localLightweightRequests.size() < 2); currentLightweightRequests = null; } else { // do nothing return; } } Throwable caughtEx = null; try { if (localLightweightRequests != null) { Component lastFocusOwner = null; Component currentFocusOwner = null; for (Iterator<KeyboardFocusManager.LightweightFocusRequest> iter = localLightweightRequests .iterator(); iter.hasNext();) { currentFocusOwner = manager.getGlobalFocusOwner(); LightweightFocusRequest lwFocusRequest = iter.next(); /* * WARNING: This is based on DKFM's logic solely! * * We allow to trigger restoreFocus() in the dispatching process * only if we have the last request to dispatch. If the last request * fails, focus will be restored to either the component of the last * previously succeeded request, or to the focus owner that was * before this clearing process. */ if (!iter.hasNext()) { disableRestoreFocus = false; } FocusEvent currentFocusOwnerEvent = null; /* * We're not dispatching FOCUS_LOST while the current focus owner is null. * But regardless of whether it's null or not, we're clearing ALL the local * lw requests. */ if (currentFocusOwner != null) { currentFocusOwnerEvent = new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, lwFocusRequest.temporary, lwFocusRequest.component, lwFocusRequest.cause); } FocusEvent newFocusOwnerEvent = new FocusEvent(lwFocusRequest.component, FocusEvent.FOCUS_GAINED, lwFocusRequest.temporary, currentFocusOwner == null ? lastFocusOwner : currentFocusOwner, lwFocusRequest.cause); if (currentFocusOwner != null) { ((AWTEvent) currentFocusOwnerEvent).isPosted = true; caughtEx = dispatchAndCatchException(caughtEx, currentFocusOwner, currentFocusOwnerEvent); } ((AWTEvent) newFocusOwnerEvent).isPosted = true; caughtEx = dispatchAndCatchException(caughtEx, lwFocusRequest.component, newFocusOwnerEvent); if (manager.getGlobalFocusOwner() == lwFocusRequest.component) { lastFocusOwner = lwFocusRequest.component; } } } } finally { clearingCurrentLightweightRequests = false; disableRestoreFocus = false; localLightweightRequests = null; allowSyncFocusRequests = true; } if (caughtEx instanceof RuntimeException) { throw (RuntimeException) caughtEx; } else if (caughtEx instanceof Error) { throw (Error) caughtEx; } } static FocusEvent retargetUnexpectedFocusEvent(FocusEvent fe) { synchronized (heavyweightRequests) { // Any other case represents a failure condition which we did // not expect. We need to clearFocusRequestList() and patch up // the event as best as possible. if (removeFirstRequest()) { return (FocusEvent) retargetFocusEvent(fe); } Component source = fe.getComponent(); Component opposite = fe.getOppositeComponent(); boolean temporary = false; if (fe.getID() == FocusEvent.FOCUS_LOST && (opposite == null || isTemporary(opposite, source))) { temporary = true; } return new FocusEvent(source, fe.getID(), temporary, opposite, FocusEvent.Cause.UNEXPECTED); } } static FocusEvent retargetFocusGained(FocusEvent fe) { assert (fe.getID() == FocusEvent.FOCUS_GAINED); Component currentFocusOwner = getCurrentKeyboardFocusManager().getGlobalFocusOwner(); Component source = fe.getComponent(); Component opposite = fe.getOppositeComponent(); Component nativeSource = getHeavyweight(source); synchronized (heavyweightRequests) { HeavyweightFocusRequest hwFocusRequest = getFirstHWRequest(); if (hwFocusRequest == HeavyweightFocusRequest.CLEAR_GLOBAL_FOCUS_OWNER) { return retargetUnexpectedFocusEvent(fe); } if (source != null && nativeSource == null && hwFocusRequest != null) { // if source w/o peer and // if source is equal to first lightweight // then we should correct source and nativeSource if (source == hwFocusRequest.getFirstLightweightRequest().component) { source = hwFocusRequest.heavyweight; nativeSource = source; // source is heavyweight itself } } if (hwFocusRequest != null && nativeSource == hwFocusRequest.heavyweight) { // Focus change as a result of a known call to requestFocus(), // or known click on a peer focusable heavyweight Component. heavyweightRequests.removeFirst(); LightweightFocusRequest lwFocusRequest = hwFocusRequest.lightweightRequests.removeFirst(); Component newSource = lwFocusRequest.component; if (currentFocusOwner != null) { /* * Since we receive FOCUS_GAINED when current focus * owner is not null, corresponding FOCUS_LOST is supposed * to be lost. And so, we keep new focus owner * to determine synthetic FOCUS_LOST event which will be * generated by KeyboardFocusManager for this FOCUS_GAINED. * * This code based on knowledge of * DefaultKeyboardFocusManager's implementation and might * be not applicable for another KeyboardFocusManager. */ newFocusOwner = newSource; } boolean temporary = (opposite == null || isTemporary(newSource, opposite)) ? false : lwFocusRequest.temporary; if (hwFocusRequest.lightweightRequests.size() > 0) { currentLightweightRequests = hwFocusRequest.lightweightRequests; EventQueue.invokeLater(new Runnable() { public void run() { processCurrentLightweightRequests(); } }); } // 'opposite' will be fixed by // DefaultKeyboardFocusManager.realOppositeComponent return new FocusEvent(newSource, FocusEvent.FOCUS_GAINED, temporary, opposite, lwFocusRequest.cause); } if (currentFocusOwner != null && currentFocusOwner.getContainingWindow() == source && (hwFocusRequest == null || source != hwFocusRequest.heavyweight)) { // Special case for FOCUS_GAINED in top-levels // If it arrives as the result of activation we should skip it // This event will not have appropriate request record and // on arrival there will be already some focus owner set. return new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_GAINED, false, null, FocusEvent.Cause.ACTIVATION); } return retargetUnexpectedFocusEvent(fe); } // end synchronized(heavyweightRequests) } static FocusEvent retargetFocusLost(FocusEvent fe) { assert (fe.getID() == FocusEvent.FOCUS_LOST); Component currentFocusOwner = getCurrentKeyboardFocusManager().getGlobalFocusOwner(); Component opposite = fe.getOppositeComponent(); Component nativeOpposite = getHeavyweight(opposite); synchronized (heavyweightRequests) { HeavyweightFocusRequest hwFocusRequest = getFirstHWRequest(); if (hwFocusRequest == HeavyweightFocusRequest.CLEAR_GLOBAL_FOCUS_OWNER) { if (currentFocusOwner != null) { // Call to KeyboardFocusManager.clearGlobalFocusOwner() heavyweightRequests.removeFirst(); return new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, false, null, FocusEvent.Cause.CLEAR_GLOBAL_FOCUS_OWNER); } // Otherwise, fall through to failure case below } else if (opposite == null) { // Focus leaving application if (currentFocusOwner != null) { return new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, true, null, FocusEvent.Cause.ACTIVATION); } else { return fe; } } else if (hwFocusRequest != null && (nativeOpposite == hwFocusRequest.heavyweight || nativeOpposite == null && opposite == hwFocusRequest.getFirstLightweightRequest().component)) { if (currentFocusOwner == null) { return fe; } // Focus change as a result of a known call to requestFocus(), // or click on a peer focusable heavyweight Component. // If a focus transfer is made across top-levels, then the // FOCUS_LOST event is always temporary, and the FOCUS_GAINED // event is always permanent. Otherwise, the stored temporary // value is honored. LightweightFocusRequest lwFocusRequest = hwFocusRequest.lightweightRequests.getFirst(); boolean temporary = isTemporary(opposite, currentFocusOwner) ? true : lwFocusRequest.temporary; return new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, temporary, lwFocusRequest.component, lwFocusRequest.cause); } else if (focusedWindowChanged(opposite, currentFocusOwner)) { // If top-level changed there might be no focus request in a list // But we know the opposite, we now it is temporary - dispatch the event. if (!fe.isTemporary() && currentFocusOwner != null) { // Create copy of the event with only difference in temporary parameter. fe = new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, true, opposite, FocusEvent.Cause.ACTIVATION); } return fe; } return retargetUnexpectedFocusEvent(fe); } // end synchronized(heavyweightRequests) } static AWTEvent retargetFocusEvent(AWTEvent event) { if (clearingCurrentLightweightRequests) { return event; } KeyboardFocusManager manager = getCurrentKeyboardFocusManager(); if (focusLog.isLoggable(PlatformLogger.Level.FINER)) { if (event instanceof FocusEvent || event instanceof WindowEvent) { focusLog.finer(">>> {0}", String.valueOf(event)); } if (focusLog.isLoggable(PlatformLogger.Level.FINER) && event instanceof KeyEvent) { focusLog.finer(" focus owner is {0}", String.valueOf(manager.getGlobalFocusOwner())); focusLog.finer(">>> {0}", String.valueOf(event)); } } synchronized (heavyweightRequests) { /* * This code handles FOCUS_LOST event which is generated by * DefaultKeyboardFocusManager for FOCUS_GAINED. * * This code based on knowledge of DefaultKeyboardFocusManager's * implementation and might be not applicable for another * KeyboardFocusManager. * * Fix for 4472032 */ if (newFocusOwner != null && event.getID() == FocusEvent.FOCUS_LOST) { FocusEvent fe = (FocusEvent) event; if (manager.getGlobalFocusOwner() == fe.getComponent() && fe.getOppositeComponent() == newFocusOwner) { newFocusOwner = null; return event; } } } processCurrentLightweightRequests(); switch (event.getID()) { case FocusEvent.FOCUS_GAINED: { event = retargetFocusGained((FocusEvent) event); break; } case FocusEvent.FOCUS_LOST: { event = retargetFocusLost((FocusEvent) event); break; } default: /* do nothing */ } return event; } /** * Clears markers queue * This method is not intended to be overridden by KFM's. * Only DefaultKeyboardFocusManager can implement it. * @since 1.5 */ void clearMarkers() { } static boolean removeFirstRequest() { KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); synchronized (heavyweightRequests) { HeavyweightFocusRequest hwFocusRequest = getFirstHWRequest(); if (hwFocusRequest != null) { heavyweightRequests.removeFirst(); if (hwFocusRequest.lightweightRequests != null) { for (Iterator<KeyboardFocusManager.LightweightFocusRequest> lwIter = hwFocusRequest.lightweightRequests .iterator(); lwIter.hasNext();) { manager.dequeueKeyEvents(-1, lwIter.next().component); } } } // Fix for 4799136 - clear type-ahead markers if requests queue is empty // We do it here because this method is called only when problems happen if (heavyweightRequests.size() == 0) { manager.clearMarkers(); } return (heavyweightRequests.size() > 0); } } static void removeLastFocusRequest(Component heavyweight) { if (log.isLoggable(PlatformLogger.Level.FINE)) { if (heavyweight == null) { log.fine("Assertion (heavyweight != null) failed"); } } KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); synchronized (heavyweightRequests) { HeavyweightFocusRequest hwFocusRequest = getLastHWRequest(); if (hwFocusRequest != null && hwFocusRequest.heavyweight == heavyweight) { heavyweightRequests.removeLast(); } // Fix for 4799136 - clear type-ahead markers if requests queue is empty // We do it here because this method is called only when problems happen if (heavyweightRequests.size() == 0) { manager.clearMarkers(); } } } private static boolean focusedWindowChanged(Component to, Component from) { Window wto = SunToolkit.getContainingWindow(to); Window wfrom = SunToolkit.getContainingWindow(from); if (wto == null && wfrom == null) { return true; } if (wto == null) { return true; } if (wfrom == null) { return true; } return (wto != wfrom); } private static boolean isTemporary(Component to, Component from) { Window wto = SunToolkit.getContainingWindow(to); Window wfrom = SunToolkit.getContainingWindow(from); if (wto == null && wfrom == null) { return false; } if (wto == null) { return true; } if (wfrom == null) { return false; } return (wto != wfrom); } static Component getHeavyweight(Component comp) { if (comp == null || comp.peer == null) { return null; } else if (comp.peer instanceof LightweightPeer) { return comp.getNativeContainer(); } else { return comp; } } // Accessor to private field isProxyActive of KeyEvent private static boolean isProxyActiveImpl(KeyEvent e) { return AWTAccessor.getKeyEventAccessor().isProxyActive(e); } // Returns the value of this KeyEvent's field isProxyActive static boolean isProxyActive(KeyEvent e) { if (!GraphicsEnvironment.isHeadless()) { return isProxyActiveImpl(e); } else { return false; } } private static HeavyweightFocusRequest getLastHWRequest() { synchronized (heavyweightRequests) { return (heavyweightRequests.size() > 0) ? heavyweightRequests.getLast() : null; } } private static HeavyweightFocusRequest getFirstHWRequest() { synchronized (heavyweightRequests) { return (heavyweightRequests.size() > 0) ? heavyweightRequests.getFirst() : null; } } private static void checkReplaceKFMPermission() throws SecurityException { SecurityManager security = System.getSecurityManager(); if (security != null) { if (replaceKeyboardFocusManagerPermission == null) { replaceKeyboardFocusManagerPermission = new AWTPermission("replaceKeyboardFocusManager"); } security.checkPermission(replaceKeyboardFocusManagerPermission); } } // Checks if this KeyboardFocusManager instance is the current KFM, // or otherwise checks if the calling thread has "replaceKeyboardFocusManager" // permission. Here's the reasoning to do so: // // A system KFM instance (which is the current KFM by default) may have no // "replaceKFM" permission when a client code is on the call stack beneath, // but still it should be able to execute the methods protected by this check // due to the system KFM is trusted (and so it does like "privileged"). // // If this KFM instance is not the current KFM but the client code has all // permissions we can't throw SecurityException because it would contradict // the security concepts. In this case the trusted client code is responsible // for calling the secured methods from KFM instance which is not current. private void checkKFMSecurity() throws SecurityException { if (this != getCurrentKeyboardFocusManager()) { checkReplaceKFMPermission(); } } }