| /* | |
| * Copyright (c) 2000, 2024, 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.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="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 { | |
| 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; | |
| 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. | |
| * | |
| * @param newManager the new KeyboardFocusManager for this thread's context | |
| * @see #getCurrentKeyboardFocusManager | |
| * @see DefaultKeyboardFocusManager | |
| */ | |
| public static void setCurrentKeyboardFocusManager(KeyboardFocusManager newManager) { | |
| 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<>(); | |
| /* | |
| * 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. | |
| * | |
| * @return the focus owner | |
| * @see #getFocusOwner | |
| * @see #setGlobalFocusOwner | |
| */ | |
| protected Component getGlobalFocusOwner() { | |
| synchronized (KeyboardFocusManager.class) { | |
| 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 | |
| */ | |
| protected void setGlobalFocusOwner(Component focusOwner) { | |
| Component oldFocusOwner = null; | |
| boolean shouldFire = false; | |
| if (focusOwner == null || focusOwner.isFocusable()) { | |
| synchronized (KeyboardFocusManager.class) { | |
| 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> | |
| * 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 | |
| */ | |
| public void clearGlobalFocusOwner() { | |
| 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() { | |
| clearGlobalFocusOwner(); | |
| } | |
| 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 | |
| */ | |
| protected Component getGlobalPermanentFocusOwner() { | |
| synchronized (KeyboardFocusManager.class) { | |
| 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 | |
| */ | |
| protected void setGlobalPermanentFocusOwner(Component permanentFocusOwner) { | |
| Component oldPermanentFocusOwner = null; | |
| boolean shouldFire = false; | |
| if (permanentFocusOwner == null || permanentFocusOwner.isFocusable()) { | |
| synchronized (KeyboardFocusManager.class) { | |
| 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 | |
| */ | |
| protected Window getGlobalFocusedWindow() { | |
| synchronized (KeyboardFocusManager.class) { | |
| 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 | |
| */ | |
| protected void setGlobalFocusedWindow(Window focusedWindow) { | |
| Window oldFocusedWindow = null; | |
| boolean shouldFire = false; | |
| if (focusedWindow == null || focusedWindow.isFocusableWindow()) { | |
| synchronized (KeyboardFocusManager.class) { | |
| 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 | |
| */ | |
| protected Window getGlobalActiveWindow() { | |
| synchronized (KeyboardFocusManager.class) { | |
| 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() | |
| */ | |
| protected void setGlobalActiveWindow(Window activeWindow) { | |
| Window oldActiveWindow; | |
| synchronized (KeyboardFocusManager.class) { | |
| 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 | |
| */ | |
| protected Container getGlobalCurrentFocusCycleRoot() { | |
| synchronized (KeyboardFocusManager.class) { | |
| 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> | |
| * 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 | |
| */ | |
| public void setGlobalCurrentFocusCycleRoot(Container newFocusCycleRoot) { | |
| Container oldFocusCycleRoot; | |
| synchronized (KeyboardFocusManager.class) { | |
| oldFocusCycleRoot = getCurrentFocusCycleRoot(); | |
| currentFocusCycleRoot = newFocusCycleRoot; | |
| } | |
| firePropertyChange("currentFocusCycleRoot", oldFocusCycleRoot, | |
| newFocusCycleRoot); | |
| } | |
| void setGlobalCurrentFocusCycleRootPriv(final Container newFocusCycleRoot) { | |
| setGlobalCurrentFocusCycleRoot(newFocusCycleRoot); | |
| } | |
| /** | |
| * 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 | Error e) { | |
| retEx = e; | |
| } | |
| 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 (LightweightFocusRequest lwFocusRequest : hwFocusRequest.lightweightRequests) { | |
| manager.dequeueKeyEvents | |
| (-1, lwFocusRequest.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; | |
| } | |
| } | |
| } |