7bde45b2eb
From-SVN: r56147
557 lines
17 KiB
Java
557 lines
17 KiB
Java
/* KeyboardFocusManager.java -- manage component focusing via the keyboard
|
||
Copyright (C) 2002 Free Software Foundation
|
||
|
||
This file is part of GNU Classpath.
|
||
|
||
GNU Classpath is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 2, or (at your option)
|
||
any later version.
|
||
|
||
GNU Classpath 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 for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GNU Classpath; see the file COPYING. If not, write to the
|
||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||
02111-1307 USA.
|
||
|
||
Linking this library statically or dynamically with other modules is
|
||
making a combined work based on this library. Thus, the terms and
|
||
conditions of the GNU General Public License cover the whole
|
||
combination.
|
||
|
||
As a special exception, the copyright holders of this library give you
|
||
permission to link this library with independent modules to produce an
|
||
executable, regardless of the license terms of these independent
|
||
modules, and to copy and distribute the resulting executable under
|
||
terms of your choice, provided that you also meet, for each linked
|
||
independent module, the terms and conditions of the license of that
|
||
module. An independent module is a module which is not derived from
|
||
or based on this library. If you modify this library, you may extend
|
||
this exception to your version of the library, but you are not
|
||
obligated to do so. If you do not wish to do so, delete this
|
||
exception statement from your version. */
|
||
|
||
|
||
package java.awt;
|
||
|
||
import java.awt.event.KeyEvent;
|
||
import java.beans.PropertyChangeListener;
|
||
import java.beans.PropertyChangeSupport;
|
||
import java.beans.PropertyVetoException;
|
||
import java.beans.VetoableChangeListener;
|
||
import java.beans.VetoableChangeSupport;
|
||
import java.util.ArrayList;
|
||
import java.util.Collections;
|
||
import java.util.HashSet;
|
||
import java.util.Iterator;
|
||
import java.util.List;
|
||
import java.util.Set;
|
||
|
||
/**
|
||
*
|
||
* @author Eric Blake <ebb9@email.byu.edu>
|
||
* @since 1.4
|
||
* @status partially updated to 1.4, needs documentation.
|
||
*/
|
||
public abstract class KeyboardFocusManager
|
||
implements KeyEventDispatcher, KeyEventPostProcessor
|
||
{
|
||
public static final int FORWARD_TRAVERSAL_KEYS = 0;
|
||
public static final int BACKWARD_TRAVERSAL_KEYS = 1;
|
||
public static final int UP_CYCLE_TRAVERSAL_KEYS = 2;
|
||
public static final int DOWN_CYCLE_TRAVERSAL_KEYS = 3;
|
||
|
||
private static final Set DEFAULT_FORWARD_KEYS;
|
||
private static final Set DEFAULT_BACKWARD_KEYS;
|
||
static
|
||
{
|
||
Set s = new HashSet();
|
||
s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0));
|
||
s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB,
|
||
KeyEvent.CTRL_DOWN_MASK));
|
||
DEFAULT_FORWARD_KEYS = Collections.unmodifiableSet(s);
|
||
s = new HashSet();
|
||
s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB,
|
||
KeyEvent.SHIFT_DOWN_MASK));
|
||
s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB,
|
||
KeyEvent.SHIFT_DOWN_MASK
|
||
| KeyEvent.CTRL_DOWN_MASK));
|
||
DEFAULT_BACKWARD_KEYS = Collections.unmodifiableSet(s);
|
||
}
|
||
|
||
private static KeyboardFocusManager current
|
||
= new DefaultKeyboardFocusManager();
|
||
|
||
// XXX Not implemented correctly. I think a good implementation here may
|
||
// be to have permanentFocusOwner be null, and fall back to focusOwner,
|
||
// unless a temporary focus change is in effect.
|
||
private static Component focusOwner;
|
||
private static Component permanentFocusOwner;
|
||
|
||
private static Window focusedWindow;
|
||
private static Window activeWindow;
|
||
private static Container focusCycleRoot;
|
||
|
||
private FocusTraversalPolicy defaultPolicy;
|
||
private Set[] defaultFocusKeys = new Set[] {
|
||
DEFAULT_FORWARD_KEYS, DEFAULT_BACKWARD_KEYS,
|
||
Collections.EMPTY_SET, Collections.EMPTY_SET
|
||
};
|
||
|
||
private final PropertyChangeSupport propertyChangeSupport
|
||
= new PropertyChangeSupport(this);
|
||
private final VetoableChangeSupport vetoableChangeSupport
|
||
= new VetoableChangeSupport(this);
|
||
private final ArrayList keyEventDispatchers = new ArrayList();
|
||
private final ArrayList keyEventPostProcessors = new ArrayList();
|
||
|
||
|
||
public KeyboardFocusManager()
|
||
{
|
||
}
|
||
|
||
public static KeyboardFocusManager getCurrentKeyboardFocusManager()
|
||
{
|
||
// XXX Need a way to divide this into contexts.
|
||
return current;
|
||
}
|
||
|
||
public static void setCurrentKeyboardFocusManager(KeyboardFocusManager m)
|
||
{
|
||
SecurityManager sm = System.getSecurityManager();
|
||
if (sm != null)
|
||
sm.checkPermission(new AWTPermission("replaceKeyboardFocusManager"));
|
||
// XXX Need a way to divide this into contexts.
|
||
current = m == null ? new DefaultKeyboardFocusManager() : m;
|
||
}
|
||
|
||
public Component getFocusOwner()
|
||
{
|
||
// XXX Need an easy way to test if this thread is in the context of the
|
||
// global focus owner, to avoid creating the exception in the first place.
|
||
try
|
||
{
|
||
return getGlobalFocusOwner();
|
||
}
|
||
catch (SecurityException e)
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
protected Component getGlobalFocusOwner()
|
||
{
|
||
// XXX Need a way to test if this thread is in the context of the focus
|
||
// owner, and throw a SecurityException if that is the case.
|
||
// XXX Implement.
|
||
return focusOwner;
|
||
}
|
||
|
||
protected void setGlobalFocusOwner(Component owner)
|
||
{
|
||
// XXX Should this send focus events to the components involved?
|
||
if (owner == null || owner.focusable)
|
||
{
|
||
firePropertyChange("focusOwner", focusOwner, owner);
|
||
try
|
||
{
|
||
fireVetoableChange("focusOwner", focusOwner, owner);
|
||
focusOwner = owner;
|
||
}
|
||
catch (PropertyVetoException e)
|
||
{
|
||
}
|
||
}
|
||
}
|
||
|
||
public void clearGlobalFocusOwner()
|
||
{
|
||
// XXX Is this enough?
|
||
setGlobalFocusOwner(null);
|
||
}
|
||
|
||
public Component getPermanentFocusOwner()
|
||
{
|
||
// XXX Need an easy way to test if this thread is in the context of the
|
||
// global focus owner, to avoid creating the exception in the first place.
|
||
try
|
||
{
|
||
return getGlobalPermanentFocusOwner();
|
||
}
|
||
catch (SecurityException e)
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
protected Component getGlobalPermanentFocusOwner()
|
||
{
|
||
// XXX Need a way to test if this thread is in the context of the focus
|
||
// owner, and throw a SecurityException if that is the case.
|
||
// XXX Implement.
|
||
return permanentFocusOwner == null ? focusOwner : permanentFocusOwner;
|
||
}
|
||
|
||
protected void setGlobalPermanentFocusOwner(Component focusOwner)
|
||
{
|
||
// XXX Should this send focus events to the components involved?
|
||
if (focusOwner == null || focusOwner.focusable)
|
||
{
|
||
firePropertyChange("permanentFocusOwner", permanentFocusOwner,
|
||
focusOwner);
|
||
try
|
||
{
|
||
fireVetoableChange("permanentFocusOwner", permanentFocusOwner,
|
||
focusOwner);
|
||
permanentFocusOwner = focusOwner;
|
||
}
|
||
catch (PropertyVetoException e)
|
||
{
|
||
}
|
||
}
|
||
}
|
||
|
||
public Window getFocusedWindow()
|
||
{
|
||
// XXX Need an easy way to test if this thread is in the context of the
|
||
// global focus owner, to avoid creating the exception in the first place.
|
||
try
|
||
{
|
||
return getGlobalFocusedWindow();
|
||
}
|
||
catch (SecurityException e)
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
protected Window getGlobalFocusedWindow()
|
||
{
|
||
// XXX Need a way to test if this thread is in the context of the focus
|
||
// owner, and throw a SecurityException if that is the case.
|
||
// XXX Implement.
|
||
return focusedWindow;
|
||
}
|
||
|
||
protected void setGlobalFocusedWindow(Window window)
|
||
{
|
||
// XXX Should this send focus events to the windows involved?
|
||
if (window == null || window.focusable)
|
||
{
|
||
firePropertyChange("focusedWindow", focusedWindow, window);
|
||
try
|
||
{
|
||
fireVetoableChange("focusedWindow", focusedWindow, window);
|
||
focusedWindow = window;
|
||
}
|
||
catch (PropertyVetoException e)
|
||
{
|
||
}
|
||
}
|
||
}
|
||
|
||
public Window getActiveWindow()
|
||
{
|
||
// XXX Need an easy way to test if this thread is in the context of the
|
||
// global focus owner, to avoid creating the exception in the first place.
|
||
try
|
||
{
|
||
return getGlobalActiveWindow();
|
||
}
|
||
catch (SecurityException e)
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
protected Window getGlobalActiveWindow()
|
||
{
|
||
// XXX Need a way to test if this thread is in the context of the focus
|
||
// owner, and throw a SecurityException if that is the case.
|
||
// XXX Implement.
|
||
return activeWindow;
|
||
}
|
||
|
||
protected void setGlobalActiveWindow(Window window)
|
||
{
|
||
// XXX Should this send focus events to the windows involved?
|
||
firePropertyChange("activeWindow", activeWindow, window);
|
||
try
|
||
{
|
||
fireVetoableChange("activeWindow", activeWindow, window);
|
||
activeWindow = window;
|
||
}
|
||
catch (PropertyVetoException e)
|
||
{
|
||
}
|
||
}
|
||
|
||
public FocusTraversalPolicy getDefaultFocusTraversalPolicy()
|
||
{
|
||
if (defaultPolicy == null)
|
||
defaultPolicy = new DefaultFocusTraversalPolicy();
|
||
return defaultPolicy;
|
||
}
|
||
|
||
public void setDefaultFocusTraversalPolicy(FocusTraversalPolicy policy)
|
||
{
|
||
if (policy == null)
|
||
throw new IllegalArgumentException();
|
||
firePropertyChange("defaultFocusTraversalPolicy", defaultPolicy, policy);
|
||
defaultPolicy = policy;
|
||
}
|
||
|
||
public void setDefaultFocusTraversalKeys(int id, Set keystrokes)
|
||
{
|
||
if (keystrokes == null)
|
||
throw new IllegalArgumentException();
|
||
Set sa;
|
||
Set sb;
|
||
Set sc;
|
||
String type;
|
||
switch (id)
|
||
{
|
||
case FORWARD_TRAVERSAL_KEYS:
|
||
sa = defaultFocusKeys[BACKWARD_TRAVERSAL_KEYS];
|
||
sb = defaultFocusKeys[UP_CYCLE_TRAVERSAL_KEYS];
|
||
sc = defaultFocusKeys[DOWN_CYCLE_TRAVERSAL_KEYS];
|
||
type = "forwardDefaultFocusTraversalKeys";
|
||
break;
|
||
case BACKWARD_TRAVERSAL_KEYS:
|
||
sa = defaultFocusKeys[FORWARD_TRAVERSAL_KEYS];
|
||
sb = defaultFocusKeys[UP_CYCLE_TRAVERSAL_KEYS];
|
||
sc = defaultFocusKeys[DOWN_CYCLE_TRAVERSAL_KEYS];
|
||
type = "backwardDefaultFocusTraversalKeys";
|
||
break;
|
||
case UP_CYCLE_TRAVERSAL_KEYS:
|
||
sa = defaultFocusKeys[FORWARD_TRAVERSAL_KEYS];
|
||
sb = defaultFocusKeys[BACKWARD_TRAVERSAL_KEYS];
|
||
sc = defaultFocusKeys[DOWN_CYCLE_TRAVERSAL_KEYS];
|
||
type = "upCycleDefaultFocusTraversalKeys";
|
||
break;
|
||
case DOWN_CYCLE_TRAVERSAL_KEYS:
|
||
sa = defaultFocusKeys[FORWARD_TRAVERSAL_KEYS];
|
||
sb = defaultFocusKeys[BACKWARD_TRAVERSAL_KEYS];
|
||
sc = defaultFocusKeys[UP_CYCLE_TRAVERSAL_KEYS];
|
||
type = "downCycleDefaultFocusTraversalKeys";
|
||
break;
|
||
default:
|
||
throw new IllegalArgumentException();
|
||
}
|
||
int i = keystrokes.size();
|
||
Iterator iter = keystrokes.iterator();
|
||
while (--i >= 0)
|
||
{
|
||
Object o = iter.next();
|
||
if (! (o instanceof AWTKeyStroke)
|
||
|| sa.contains(o) || sb.contains(o) || sc.contains(o)
|
||
|| ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED)
|
||
throw new IllegalArgumentException();
|
||
}
|
||
keystrokes = Collections.unmodifiableSet(new HashSet(keystrokes));
|
||
firePropertyChange(type, defaultFocusKeys[id], keystrokes);
|
||
defaultFocusKeys[id] = keystrokes;
|
||
}
|
||
|
||
public Set getDefaultFocusTraversalKeys(int id)
|
||
{
|
||
if (id < FORWARD_TRAVERSAL_KEYS || id > DOWN_CYCLE_TRAVERSAL_KEYS)
|
||
throw new IllegalArgumentException();
|
||
return defaultFocusKeys[id];
|
||
}
|
||
|
||
public Container getCurrentFocusCycleRoot()
|
||
{
|
||
// XXX Need an easy way to test if this thread is in the context of the
|
||
// global focus owner, to avoid creating the exception in the first place.
|
||
try
|
||
{
|
||
return getGlobalCurrentFocusCycleRoot();
|
||
}
|
||
catch (SecurityException e)
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
protected Container getGlobalCurrentFocusCycleRoot()
|
||
{
|
||
// XXX Need a way to test if this thread is in the context of the focus
|
||
// owner, and throw a SecurityException if that is the case.
|
||
// XXX Implement.
|
||
return focusCycleRoot;
|
||
}
|
||
|
||
protected void setGlobalCurrentFocusCycleRoot(Container cycleRoot)
|
||
{
|
||
firePropertyChange("currentFocusCycleRoot", focusCycleRoot, cycleRoot);
|
||
focusCycleRoot = cycleRoot;
|
||
}
|
||
|
||
public void addPropertyChangeListener(PropertyChangeListener l)
|
||
{
|
||
if (l != null)
|
||
propertyChangeSupport.addPropertyChangeListener(l);
|
||
}
|
||
|
||
public void removePropertyChangeListener(PropertyChangeListener l)
|
||
{
|
||
if (l != null)
|
||
propertyChangeSupport.removePropertyChangeListener(l);
|
||
}
|
||
|
||
public PropertyChangeListener[] getPropertyChangeListeners()
|
||
{
|
||
return propertyChangeSupport.getPropertyChangeListeners();
|
||
}
|
||
|
||
public void addPropertyChangeListener(String name, PropertyChangeListener l)
|
||
{
|
||
if (l != null)
|
||
propertyChangeSupport.addPropertyChangeListener(name, l);
|
||
}
|
||
|
||
public void removePropertyChangeListener(String name,
|
||
PropertyChangeListener l)
|
||
{
|
||
if (l != null)
|
||
propertyChangeSupport.removePropertyChangeListener(name, l);
|
||
}
|
||
|
||
public PropertyChangeListener[] getPropertyChangeListeners(String name)
|
||
{
|
||
return propertyChangeSupport.getPropertyChangeListeners(name);
|
||
}
|
||
|
||
protected void firePropertyChange(String name, Object o, Object n)
|
||
{
|
||
propertyChangeSupport.firePropertyChange(name, o, n);
|
||
}
|
||
|
||
public void addVetoableChangeListener(VetoableChangeListener l)
|
||
{
|
||
if (l != null)
|
||
vetoableChangeSupport.addVetoableChangeListener(l);
|
||
}
|
||
|
||
public void removeVetoableChangeListener(VetoableChangeListener l)
|
||
{
|
||
if (l != null)
|
||
vetoableChangeSupport.removeVetoableChangeListener(l);
|
||
}
|
||
|
||
public VetoableChangeListener[] getVetoableChangeListeners()
|
||
{
|
||
return vetoableChangeSupport.getVetoableChangeListeners();
|
||
}
|
||
|
||
public void addVetoableChangeListener(String name, VetoableChangeListener l)
|
||
{
|
||
if (l != null)
|
||
vetoableChangeSupport.addVetoableChangeListener(name, l);
|
||
}
|
||
|
||
public void removeVetoableChangeListener(String name,
|
||
VetoableChangeListener l)
|
||
{
|
||
if (l != null)
|
||
vetoableChangeSupport.removeVetoableChangeListener(name, l);
|
||
}
|
||
|
||
public VetoableChangeListener[] getVetoableChangeListeners(String name)
|
||
{
|
||
return vetoableChangeSupport.getVetoableChangeListeners(name);
|
||
}
|
||
|
||
protected void fireVetoableChange(String name, Object o, Object n)
|
||
throws PropertyVetoException
|
||
{
|
||
vetoableChangeSupport.fireVetoableChange(name, o, n);
|
||
}
|
||
|
||
public void addKeyEventDispatcher(KeyEventDispatcher dispatcher)
|
||
{
|
||
if (dispatcher != null)
|
||
keyEventDispatchers.add(dispatcher);
|
||
}
|
||
|
||
public void removeKeyEventDispatcher(KeyEventDispatcher dispatcher)
|
||
{
|
||
keyEventDispatchers.remove(dispatcher);
|
||
}
|
||
|
||
protected List getKeyEventDispatchers()
|
||
{
|
||
return (List) keyEventDispatchers.clone();
|
||
}
|
||
|
||
public void addKeyEventPostProcessor(KeyEventPostProcessor postProcessor)
|
||
{
|
||
if (postProcessor != null)
|
||
keyEventPostProcessors.add(postProcessor);
|
||
}
|
||
|
||
public void removeKeyEventPostProcessor(KeyEventPostProcessor postProcessor)
|
||
{
|
||
keyEventPostProcessors.remove(postProcessor);
|
||
}
|
||
|
||
protected List getKeyEventPostProcessors()
|
||
{
|
||
return (List) keyEventPostProcessors.clone();
|
||
}
|
||
|
||
public abstract boolean dispatchEvent(AWTEvent e);
|
||
|
||
public final void redispatchEvent(Component target, AWTEvent e)
|
||
{
|
||
throw new Error("not implemented");
|
||
}
|
||
|
||
public abstract boolean dispatchKeyEvent(KeyEvent e);
|
||
|
||
public abstract boolean postProcessKeyEvent(KeyEvent e);
|
||
|
||
public abstract void processKeyEvent(Component focused, KeyEvent e);
|
||
|
||
protected abstract void enqueueKeyEvents(long after, Component untilFocused);
|
||
|
||
protected abstract void dequeueKeyEvents(long after, Component untilFocused);
|
||
|
||
protected abstract void discardKeyEvents(Component comp);
|
||
|
||
public abstract void focusNextComponent(Component comp);
|
||
|
||
public abstract void focusPreviousComponent(Component comp);
|
||
|
||
public abstract void upFocusCycle(Component comp);
|
||
|
||
public abstract void downFocusCycle(Container cont);
|
||
|
||
public final void focusNextComponent()
|
||
{
|
||
focusNextComponent(focusOwner);
|
||
}
|
||
|
||
public final void focusPreviousComponent()
|
||
{
|
||
focusPreviousComponent(focusOwner);
|
||
}
|
||
|
||
public final void upFocusCycle()
|
||
{
|
||
upFocusCycle(focusOwner);
|
||
}
|
||
|
||
public final void downFocusCycle()
|
||
{
|
||
if (focusOwner instanceof Container
|
||
&& ((Container) focusOwner).isFocusCycleRoot())
|
||
downFocusCycle((Container) focusOwner);
|
||
}
|
||
} // class KeyboardFocusManager
|