| /* 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; |
| } |
| |
| public 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 |