/* * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package java.awt; import java.awt.event.FocusEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.awt.peer.ComponentPeer; import java.awt.peer.LightweightPeer; import java.lang.ref.WeakReference; import java.util.LinkedList; import java.util.Iterator; import java.util.ListIterator; import java.util.Set; import sun.util.logging.PlatformLogger; import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.awt.AWTAccessor; import sun.awt.CausedFocusEvent; import sun.awt.TimedWindowEvent; /** * The default KeyboardFocusManager for AWT applications. Focus traversal is * done in response to a Component's focus traversal keys, and using a * Container's FocusTraversalPolicy. *
* Please see
*
* How to Use the Focus Subsystem,
* a section in The Java Tutorial, and the
* Focus Specification
* for more information.
*
* @author David Mendenhall
*
* @see FocusTraversalPolicy
* @see Component#setFocusTraversalKeys
* @see Component#getFocusTraversalKeys
* @since 1.4
*/
public class DefaultKeyboardFocusManager extends KeyboardFocusManager {
private static final PlatformLogger focusLog = PlatformLogger.getLogger("java.awt.focus.DefaultKeyboardFocusManager");
// null weak references to not create too many objects
private static final WeakReference
* In all cases, this method returns false
, then the AWT event
* dispatcher will attempt to dispatch the event itself.
*
* @param e the AWTEvent to be dispatched
* @return true
if this method dispatched the event;
* false
otherwise
*/
public boolean dispatchEvent(AWTEvent e) {
if (focusLog.isLoggable(PlatformLogger.Level.FINE) && (e instanceof WindowEvent || e instanceof FocusEvent)) {
focusLog.fine("" + e);
}
switch (e.getID()) {
case WindowEvent.WINDOW_GAINED_FOCUS: {
if (repostIfFollowsKeyEvents((WindowEvent)e)) {
break;
}
WindowEvent we = (WindowEvent)e;
Window oldFocusedWindow = getGlobalFocusedWindow();
Window newFocusedWindow = we.getWindow();
if (newFocusedWindow == oldFocusedWindow) {
break;
}
if (!(newFocusedWindow.isFocusableWindow()
&& newFocusedWindow.isVisible()
&& newFocusedWindow.isDisplayable()))
{
// we can not accept focus on such window, so reject it.
restoreFocus(we);
break;
}
// If there exists a current focused window, then notify it
// that it has lost focus.
if (oldFocusedWindow != null) {
boolean isEventDispatched =
sendMessage(oldFocusedWindow,
new WindowEvent(oldFocusedWindow,
WindowEvent.WINDOW_LOST_FOCUS,
newFocusedWindow));
// Failed to dispatch, clear by ourselfves
if (!isEventDispatched) {
setGlobalFocusOwner(null);
setGlobalFocusedWindow(null);
}
}
// Because the native libraries do not post WINDOW_ACTIVATED
// events, we need to synthesize one if the active Window
// changed.
Window newActiveWindow =
getOwningFrameDialog(newFocusedWindow);
Window currentActiveWindow = getGlobalActiveWindow();
if (newActiveWindow != currentActiveWindow) {
sendMessage(newActiveWindow,
new WindowEvent(newActiveWindow,
WindowEvent.WINDOW_ACTIVATED,
currentActiveWindow));
if (newActiveWindow != getGlobalActiveWindow()) {
// Activation change was rejected. Unlikely, but
// possible.
restoreFocus(we);
break;
}
}
setGlobalFocusedWindow(newFocusedWindow);
if (newFocusedWindow != getGlobalFocusedWindow()) {
// Focus change was rejected. Will happen if
// newFocusedWindow is not a focusable Window.
restoreFocus(we);
break;
}
// Restore focus to the Component which last held it. We do
// this here so that client code can override our choice in
// a WINDOW_GAINED_FOCUS handler.
//
// Make sure that the focus change request doesn't change the
// focused Window in case we are no longer the focused Window
// when the request is handled.
if (inSendMessage == 0) {
// Identify which Component should initially gain focus
// in the Window.
//
// * If we're in SendMessage, then this is a synthetic
// WINDOW_GAINED_FOCUS message which was generated by a
// the FOCUS_GAINED handler. Allow the Component to
// which the FOCUS_GAINED message was targeted to
// receive the focus.
// * Otherwise, look up the correct Component here.
// We don't use Window.getMostRecentFocusOwner because
// window is focused now and 'null' will be returned
// Calculating of most recent focus owner and focus
// request should be synchronized on KeyboardFocusManager.class
// to prevent from thread race when user will request
// focus between calculation and our request.
// But if focus transfer is synchronous, this synchronization
// may cause deadlock, thus we don't synchronize this block.
Component toFocus = KeyboardFocusManager.
getMostRecentFocusOwner(newFocusedWindow);
boolean isFocusRestore = restoreFocusTo != null &&
toFocus == restoreFocusTo;
if ((toFocus == null) &&
newFocusedWindow.isFocusableWindow())
{
toFocus = newFocusedWindow.getFocusTraversalPolicy().
getInitialComponent(newFocusedWindow);
}
Component tempLost = null;
synchronized(KeyboardFocusManager.class) {
tempLost = newFocusedWindow.setTemporaryLostComponent(null);
}
// The component which last has the focus when this window was focused
// should receive focus first
if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
focusLog.finer("tempLost {0}, toFocus {1}",
tempLost, toFocus);
}
if (tempLost != null) {
tempLost.requestFocusInWindow(
isFocusRestore && tempLost == toFocus ?
CausedFocusEvent.Cause.ROLLBACK :
CausedFocusEvent.Cause.ACTIVATION);
}
if (toFocus != null && toFocus != tempLost) {
// If there is a component which requested focus when this window
// was inactive it expects to receive focus after activation.
toFocus.requestFocusInWindow(CausedFocusEvent.Cause.ACTIVATION);
}
}
restoreFocusTo = null;
Window realOppositeWindow = this.realOppositeWindowWR.get();
if (realOppositeWindow != we.getOppositeWindow()) {
we = new WindowEvent(newFocusedWindow,
WindowEvent.WINDOW_GAINED_FOCUS,
realOppositeWindow);
}
return typeAheadAssertions(newFocusedWindow, we);
}
case WindowEvent.WINDOW_ACTIVATED: {
WindowEvent we = (WindowEvent)e;
Window oldActiveWindow = getGlobalActiveWindow();
Window newActiveWindow = we.getWindow();
if (oldActiveWindow == newActiveWindow) {
break;
}
// If there exists a current active window, then notify it that
// it has lost activation.
if (oldActiveWindow != null) {
boolean isEventDispatched =
sendMessage(oldActiveWindow,
new WindowEvent(oldActiveWindow,
WindowEvent.WINDOW_DEACTIVATED,
newActiveWindow));
// Failed to dispatch, clear by ourselfves
if (!isEventDispatched) {
setGlobalActiveWindow(null);
}
if (getGlobalActiveWindow() != null) {
// Activation change was rejected. Unlikely, but
// possible.
break;
}
}
setGlobalActiveWindow(newActiveWindow);
if (newActiveWindow != getGlobalActiveWindow()) {
// Activation change was rejected. Unlikely, but
// possible.
break;
}
return typeAheadAssertions(newActiveWindow, we);
}
case FocusEvent.FOCUS_GAINED: {
restoreFocusTo = null;
FocusEvent fe = (FocusEvent)e;
CausedFocusEvent.Cause cause = (fe instanceof CausedFocusEvent) ?
((CausedFocusEvent)fe).getCause() : CausedFocusEvent.Cause.UNKNOWN;
Component oldFocusOwner = getGlobalFocusOwner();
Component newFocusOwner = fe.getComponent();
if (oldFocusOwner == newFocusOwner) {
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
focusLog.fine("Skipping {0} because focus owner is the same", e);
}
// We can't just drop the event - there could be
// type-ahead markers associated with it.
dequeueKeyEvents(-1, newFocusOwner);
break;
}
// If there exists a current focus owner, then notify it that
// it has lost focus.
if (oldFocusOwner != null) {
boolean isEventDispatched =
sendMessage(oldFocusOwner,
new CausedFocusEvent(oldFocusOwner,
FocusEvent.FOCUS_LOST,
fe.isTemporary(),
newFocusOwner, cause));
// Failed to dispatch, clear by ourselfves
if (!isEventDispatched) {
setGlobalFocusOwner(null);
if (!fe.isTemporary()) {
setGlobalPermanentFocusOwner(null);
}
}
}
// Because the native windowing system has a different notion
// of the current focus and activation states, it is possible
// that a Component outside of the focused Window receives a
// FOCUS_GAINED event. We synthesize a WINDOW_GAINED_FOCUS
// event in that case.
final Window newFocusedWindow = SunToolkit.getContainingWindow(newFocusOwner);
final Window currentFocusedWindow = getGlobalFocusedWindow();
if (newFocusedWindow != null &&
newFocusedWindow != currentFocusedWindow)
{
sendMessage(newFocusedWindow,
new WindowEvent(newFocusedWindow,
WindowEvent.WINDOW_GAINED_FOCUS,
currentFocusedWindow));
if (newFocusedWindow != getGlobalFocusedWindow()) {
// Focus change was rejected. Will happen if
// newFocusedWindow is not a focusable Window.
// Need to recover type-ahead, but don't bother
// restoring focus. That was done by the
// WINDOW_GAINED_FOCUS handler
dequeueKeyEvents(-1, newFocusOwner);
break;
}
}
if (!(newFocusOwner.isFocusable() && newFocusOwner.isShowing() &&
// Refuse focus on a disabled component if the focus event
// isn't of UNKNOWN reason (i.e. not a result of a direct request
// but traversal, activation or system generated).
(newFocusOwner.isEnabled() || cause.equals(CausedFocusEvent.Cause.UNKNOWN))))
{
// we should not accept focus on such component, so reject it.
dequeueKeyEvents(-1, newFocusOwner);
if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
// If FOCUS_GAINED is for a disposed component (however
// it shouldn't happen) its toplevel parent is null. In this
// case we have to try to restore focus in the current focused
// window (for the details: 6607170).
if (newFocusedWindow == null) {
restoreFocus(fe, currentFocusedWindow);
} else {
restoreFocus(fe, newFocusedWindow);
}
setMostRecentFocusOwner(newFocusedWindow, null); // see: 8013773
}
break;
}
setGlobalFocusOwner(newFocusOwner);
if (newFocusOwner != getGlobalFocusOwner()) {
// Focus change was rejected. Will happen if
// newFocusOwner is not focus traversable.
dequeueKeyEvents(-1, newFocusOwner);
if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
restoreFocus(fe, (Window)newFocusedWindow);
}
break;
}
if (!fe.isTemporary()) {
setGlobalPermanentFocusOwner(newFocusOwner);
if (newFocusOwner != getGlobalPermanentFocusOwner()) {
// Focus change was rejected. Unlikely, but possible.
dequeueKeyEvents(-1, newFocusOwner);
if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
restoreFocus(fe, (Window)newFocusedWindow);
}
break;
}
}
setNativeFocusOwner(getHeavyweight(newFocusOwner));
Component realOppositeComponent = this.realOppositeComponentWR.get();
if (realOppositeComponent != null &&
realOppositeComponent != fe.getOppositeComponent()) {
fe = new CausedFocusEvent(newFocusOwner,
FocusEvent.FOCUS_GAINED,
fe.isTemporary(),
realOppositeComponent, cause);
((AWTEvent) fe).isPosted = true;
}
return typeAheadAssertions(newFocusOwner, fe);
}
case FocusEvent.FOCUS_LOST: {
FocusEvent fe = (FocusEvent)e;
Component currentFocusOwner = getGlobalFocusOwner();
if (currentFocusOwner == null) {
if (focusLog.isLoggable(PlatformLogger.Level.FINE))
focusLog.fine("Skipping {0} because focus owner is null", e);
break;
}
// Ignore cases where a Component loses focus to itself.
// If we make a mistake because of retargeting, then the
// FOCUS_GAINED handler will correct it.
if (currentFocusOwner == fe.getOppositeComponent()) {
if (focusLog.isLoggable(PlatformLogger.Level.FINE))
focusLog.fine("Skipping {0} because current focus owner is equal to opposite", e);
break;
}
setGlobalFocusOwner(null);
if (getGlobalFocusOwner() != null) {
// Focus change was rejected. Unlikely, but possible.
restoreFocus(currentFocusOwner, true);
break;
}
if (!fe.isTemporary()) {
setGlobalPermanentFocusOwner(null);
if (getGlobalPermanentFocusOwner() != null) {
// Focus change was rejected. Unlikely, but possible.
restoreFocus(currentFocusOwner, true);
break;
}
} else {
Window owningWindow = currentFocusOwner.getContainingWindow();
if (owningWindow != null) {
owningWindow.setTemporaryLostComponent(currentFocusOwner);
}
}
setNativeFocusOwner(null);
fe.setSource(currentFocusOwner);
realOppositeComponentWR = (fe.getOppositeComponent() != null)
? new WeakReferencedispatchEvent
if no other
* KeyEventDispatcher in the dispatcher chain dispatched the KeyEvent, or
* if no other KeyEventDispatchers are registered. If the event has not
* been consumed, its target is enabled, and the focus owner is not null,
* this method dispatches the event to its target. This method will also
* subsequently dispatch the event to all registered
* KeyEventPostProcessors. After all this operations are finished,
* the event is passed to peers for processing.
* true
, since
* DefaultKeyboardFocusManager is designed so that neither
* dispatchEvent
, nor the AWT event dispatcher, should take
* further action on the event in any situation.
*
* @param e the KeyEvent to be dispatched
* @return true
* @see Component#dispatchEvent
*/
public boolean dispatchKeyEvent(KeyEvent e) {
Component focusOwner = (((AWTEvent)e).isPosted) ? getFocusOwner() : e.getComponent();
if (focusOwner != null && focusOwner.isShowing() && focusOwner.canBeFocusOwner()) {
if (!e.isConsumed()) {
Component comp = e.getComponent();
if (comp != null && comp.isEnabled()) {
redispatchEvent(comp, e);
}
}
}
boolean stopPostProcessing = false;
java.util.ListdispatchKeyEvent
. It will
* handle any unconsumed KeyEvents that map to an AWT
* MenuShortcut
by consuming the event and activating the
* shortcut.
*
* @param e the KeyEvent to post-process
* @return true
* @see #dispatchKeyEvent
* @see MenuShortcut
*/
public boolean postProcessKeyEvent(KeyEvent e) {
if (!e.isConsumed()) {
Component target = e.getComponent();
Container p = (Container)
(target instanceof Container ? target : target.getParent());
if (p != null) {
p.postProcessKeyEvent(e);
}
}
return true;
}
private void pumpApprovedKeyEvents() {
KeyEvent ke;
do {
ke = null;
synchronized (this) {
if (enqueuedKeyEvents.size() != 0) {
ke = enqueuedKeyEvents.getFirst();
if (typeAheadMarkers.size() != 0) {
TypeAheadMarker marker = typeAheadMarkers.getFirst();
// Fixed 5064013: may appears that the events have the same time
// if (ke.getWhen() >= marker.after) {
// The fix is rolled out.
if (ke.getWhen() > marker.after) {
ke = null;
}
}
if (ke != null) {
if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
focusLog.finer("Pumping approved event {0}", ke);
}
enqueuedKeyEvents.removeFirst();
}
}
}
if (ke != null) {
preDispatchKeyEvent(ke);
}
} while (ke != null);
}
/**
* Dumps the list of type-ahead queue markers to stderr
*/
void dumpMarkers() {
if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {
focusLog.finest(">>> Markers dump, time: {0}", System.currentTimeMillis());
synchronized (this) {
if (typeAheadMarkers.size() != 0) {
Iteratorcomp
* in a markers' queue
* @since 1.5
*/
private boolean hasMarker(Component comp) {
for (IteratordequeueKeyEvents
or 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 will receive a FOCUS_GAINED event
* before any pending KeyEvents
* @see #dequeueKeyEvents
* @see #discardKeyEvents
*/
protected synchronized void enqueueKeyEvents(long after,
Component untilFocused) {
if (untilFocused == null) {
return;
}
if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
focusLog.finer("Enqueue at {0} for {1}",
after, untilFocused);
}
int insertionIndex = 0,
i = typeAheadMarkers.size();
ListIteratorenqueueKeyEvents
with the same timestamp and Component.
* If the given timestamp is less than zero, the outstanding enqueue
* request for the given Component with the oldest timestamp (if
* any) should be cancelled.
*
* @param after the timestamp specified in the call to
* enqueueKeyEvents
, or any value < 0
* @param untilFocused the Component specified in the call to
* enqueueKeyEvents
* @see #enqueueKeyEvents
* @see #discardKeyEvents
*/
protected synchronized void dequeueKeyEvents(long after,
Component untilFocused) {
if (untilFocused == null) {
return;
}
if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
focusLog.finer("Dequeue at {0} for {1}",
after, untilFocused);
}
TypeAheadMarker marker;
ListIteratorenqueueKeyEvents
with the specified Component, or one of
* its descendants.
*
* @param comp the Component specified in one or more calls to
* enqueueKeyEvents
, or a parent of such a Component
* @see #enqueueKeyEvents
* @see #dequeueKeyEvents
*/
protected synchronized void discardKeyEvents(Component comp) {
if (comp == null) {
return;
}
long start = -1;
for (Iterator