1 /* KeyboardFocusManager.java -- manage component focusing via the keyboard
2    Copyright (C) 2002 Free Software Foundation
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package java.awt;
40 
41 import java.awt.event.KeyEvent;
42 import java.beans.PropertyChangeListener;
43 import java.beans.PropertyChangeSupport;
44 import java.beans.PropertyVetoException;
45 import java.beans.VetoableChangeListener;
46 import java.beans.VetoableChangeSupport;
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.List;
52 import java.util.Set;
53 
54 /**
55  *
56  * @author Eric Blake <ebb9@email.byu.edu>
57  * @since 1.4
58  * @status partially updated to 1.4, needs documentation.
59  */
60 public abstract class KeyboardFocusManager
61   implements KeyEventDispatcher, KeyEventPostProcessor
62 {
63   public static final int FORWARD_TRAVERSAL_KEYS = 0;
64   public static final int BACKWARD_TRAVERSAL_KEYS = 1;
65   public static final int UP_CYCLE_TRAVERSAL_KEYS = 2;
66   public static final int DOWN_CYCLE_TRAVERSAL_KEYS = 3;
67 
68   private static final Set DEFAULT_FORWARD_KEYS;
69   private static final Set DEFAULT_BACKWARD_KEYS;
70   static
71   {
72     Set s = new HashSet();
AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0)73     s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0));
AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, KeyEvent.CTRL_DOWN_MASK)74     s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB,
75                                        KeyEvent.CTRL_DOWN_MASK));
76     DEFAULT_FORWARD_KEYS = Collections.unmodifiableSet(s);
77     s = new HashSet();
AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK)78     s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB,
79                                        KeyEvent.SHIFT_DOWN_MASK));
AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK)80     s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB,
81                                        KeyEvent.SHIFT_DOWN_MASK
82                                        | KeyEvent.CTRL_DOWN_MASK));
83     DEFAULT_BACKWARD_KEYS = Collections.unmodifiableSet(s);
84   }
85 
86   private static KeyboardFocusManager current
87     = new DefaultKeyboardFocusManager();
88 
89   // XXX Not implemented correctly. I think a good implementation here may
90   // be to have permanentFocusOwner be null, and fall back to focusOwner,
91   // unless a temporary focus change is in effect.
92   private static Component focusOwner;
93   private static Component permanentFocusOwner;
94 
95   private static Window focusedWindow;
96   private static Window activeWindow;
97   private static Container focusCycleRoot;
98 
99   private FocusTraversalPolicy defaultPolicy;
100   private Set[] defaultFocusKeys = new Set[] {
101     DEFAULT_FORWARD_KEYS, DEFAULT_BACKWARD_KEYS,
102     Collections.EMPTY_SET, Collections.EMPTY_SET
103   };
104 
105   private final PropertyChangeSupport propertyChangeSupport
106     = new PropertyChangeSupport(this);
107   private final VetoableChangeSupport vetoableChangeSupport
108     = new VetoableChangeSupport(this);
109   private final ArrayList keyEventDispatchers = new ArrayList();
110   private final ArrayList keyEventPostProcessors = new ArrayList();
111 
112 
KeyboardFocusManager()113   public KeyboardFocusManager()
114   {
115   }
116 
getCurrentKeyboardFocusManager()117   public static KeyboardFocusManager getCurrentKeyboardFocusManager()
118   {
119     // XXX Need a way to divide this into contexts.
120     return current;
121   }
122 
setCurrentKeyboardFocusManager(KeyboardFocusManager m)123   public static void setCurrentKeyboardFocusManager(KeyboardFocusManager m)
124   {
125     SecurityManager sm = System.getSecurityManager();
126     if (sm != null)
127       sm.checkPermission(new AWTPermission("replaceKeyboardFocusManager"));
128     // XXX Need a way to divide this into contexts.
129     current = m == null ? new DefaultKeyboardFocusManager() : m;
130   }
131 
getFocusOwner()132   public Component getFocusOwner()
133   {
134     // XXX Need an easy way to test if this thread is in the context of the
135     // global focus owner, to avoid creating the exception in the first place.
136     try
137       {
138         return getGlobalFocusOwner();
139       }
140     catch (SecurityException e)
141       {
142         return null;
143       }
144   }
145 
getGlobalFocusOwner()146   protected Component getGlobalFocusOwner()
147   {
148     // XXX Need a way to test if this thread is in the context of the focus
149     // owner, and throw a SecurityException if that is the case.
150     // XXX Implement.
151     return focusOwner;
152   }
153 
setGlobalFocusOwner(Component owner)154   protected void setGlobalFocusOwner(Component owner)
155   {
156     // XXX Should this send focus events to the components involved?
157     if (owner == null || owner.focusable)
158       {
159         firePropertyChange("focusOwner", focusOwner, owner);
160         try
161           {
162             fireVetoableChange("focusOwner", focusOwner, owner);
163             focusOwner = owner;
164           }
165         catch (PropertyVetoException e)
166           {
167           }
168       }
169   }
170 
clearGlobalFocusOwner()171   public void clearGlobalFocusOwner()
172   {
173     // XXX Is this enough?
174     setGlobalFocusOwner(null);
175   }
176 
getPermanentFocusOwner()177   public Component getPermanentFocusOwner()
178   {
179     // XXX Need an easy way to test if this thread is in the context of the
180     // global focus owner, to avoid creating the exception in the first place.
181     try
182       {
183         return getGlobalPermanentFocusOwner();
184       }
185     catch (SecurityException e)
186       {
187         return null;
188       }
189   }
190 
getGlobalPermanentFocusOwner()191   protected Component getGlobalPermanentFocusOwner()
192   {
193     // XXX Need a way to test if this thread is in the context of the focus
194     // owner, and throw a SecurityException if that is the case.
195     // XXX Implement.
196     return permanentFocusOwner == null ? focusOwner : permanentFocusOwner;
197   }
198 
setGlobalPermanentFocusOwner(Component focusOwner)199   protected void setGlobalPermanentFocusOwner(Component focusOwner)
200   {
201     // XXX Should this send focus events to the components involved?
202     if (focusOwner == null || focusOwner.focusable)
203       {
204         firePropertyChange("permanentFocusOwner", permanentFocusOwner,
205                            focusOwner);
206         try
207           {
208             fireVetoableChange("permanentFocusOwner", permanentFocusOwner,
209                                focusOwner);
210             permanentFocusOwner = focusOwner;
211           }
212         catch (PropertyVetoException e)
213           {
214           }
215       }
216   }
217 
getFocusedWindow()218   public Window getFocusedWindow()
219   {
220     // XXX Need an easy way to test if this thread is in the context of the
221     // global focus owner, to avoid creating the exception in the first place.
222     try
223       {
224         return getGlobalFocusedWindow();
225       }
226     catch (SecurityException e)
227       {
228         return null;
229       }
230   }
231 
getGlobalFocusedWindow()232   protected Window getGlobalFocusedWindow()
233   {
234     // XXX Need a way to test if this thread is in the context of the focus
235     // owner, and throw a SecurityException if that is the case.
236     // XXX Implement.
237     return focusedWindow;
238   }
239 
setGlobalFocusedWindow(Window window)240   protected void setGlobalFocusedWindow(Window window)
241   {
242     // XXX Should this send focus events to the windows involved?
243     if (window == null || window.focusable)
244       {
245         firePropertyChange("focusedWindow", focusedWindow, window);
246         try
247           {
248             fireVetoableChange("focusedWindow", focusedWindow, window);
249             focusedWindow = window;
250           }
251         catch (PropertyVetoException e)
252           {
253           }
254       }
255   }
256 
getActiveWindow()257   public Window getActiveWindow()
258   {
259     // XXX Need an easy way to test if this thread is in the context of the
260     // global focus owner, to avoid creating the exception in the first place.
261     try
262       {
263         return getGlobalActiveWindow();
264       }
265     catch (SecurityException e)
266       {
267         return null;
268       }
269   }
270 
getGlobalActiveWindow()271   protected Window getGlobalActiveWindow()
272   {
273     // XXX Need a way to test if this thread is in the context of the focus
274     // owner, and throw a SecurityException if that is the case.
275     // XXX Implement.
276     return activeWindow;
277   }
278 
setGlobalActiveWindow(Window window)279   protected void setGlobalActiveWindow(Window window)
280   {
281     // XXX Should this send focus events to the windows involved?
282     firePropertyChange("activeWindow", activeWindow, window);
283     try
284       {
285         fireVetoableChange("activeWindow", activeWindow, window);
286         activeWindow = window;
287       }
288     catch (PropertyVetoException e)
289       {
290       }
291   }
292 
getDefaultFocusTraversalPolicy()293   public FocusTraversalPolicy getDefaultFocusTraversalPolicy()
294   {
295     if (defaultPolicy == null)
296       defaultPolicy = new DefaultFocusTraversalPolicy();
297     return defaultPolicy;
298   }
299 
setDefaultFocusTraversalPolicy(FocusTraversalPolicy policy)300   public void setDefaultFocusTraversalPolicy(FocusTraversalPolicy policy)
301   {
302     if (policy == null)
303       throw new IllegalArgumentException();
304     firePropertyChange("defaultFocusTraversalPolicy", defaultPolicy, policy);
305     defaultPolicy = policy;
306   }
307 
setDefaultFocusTraversalKeys(int id, Set keystrokes)308   public void setDefaultFocusTraversalKeys(int id, Set keystrokes)
309   {
310     if (keystrokes == null)
311       throw new IllegalArgumentException();
312     Set sa;
313     Set sb;
314     Set sc;
315     String type;
316     switch (id)
317       {
318       case FORWARD_TRAVERSAL_KEYS:
319         sa = defaultFocusKeys[BACKWARD_TRAVERSAL_KEYS];
320         sb = defaultFocusKeys[UP_CYCLE_TRAVERSAL_KEYS];
321         sc = defaultFocusKeys[DOWN_CYCLE_TRAVERSAL_KEYS];
322         type = "forwardDefaultFocusTraversalKeys";
323         break;
324       case BACKWARD_TRAVERSAL_KEYS:
325         sa = defaultFocusKeys[FORWARD_TRAVERSAL_KEYS];
326         sb = defaultFocusKeys[UP_CYCLE_TRAVERSAL_KEYS];
327         sc = defaultFocusKeys[DOWN_CYCLE_TRAVERSAL_KEYS];
328         type = "backwardDefaultFocusTraversalKeys";
329         break;
330       case UP_CYCLE_TRAVERSAL_KEYS:
331         sa = defaultFocusKeys[FORWARD_TRAVERSAL_KEYS];
332         sb = defaultFocusKeys[BACKWARD_TRAVERSAL_KEYS];
333         sc = defaultFocusKeys[DOWN_CYCLE_TRAVERSAL_KEYS];
334         type = "upCycleDefaultFocusTraversalKeys";
335         break;
336       case DOWN_CYCLE_TRAVERSAL_KEYS:
337         sa = defaultFocusKeys[FORWARD_TRAVERSAL_KEYS];
338         sb = defaultFocusKeys[BACKWARD_TRAVERSAL_KEYS];
339         sc = defaultFocusKeys[UP_CYCLE_TRAVERSAL_KEYS];
340         type = "downCycleDefaultFocusTraversalKeys";
341         break;
342       default:
343         throw new IllegalArgumentException();
344       }
345     int i = keystrokes.size();
346     Iterator iter = keystrokes.iterator();
347     while (--i >= 0)
348       {
349         Object o = iter.next();
350         if (! (o instanceof AWTKeyStroke)
351             || sa.contains(o) || sb.contains(o) || sc.contains(o)
352             || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED)
353           throw new IllegalArgumentException();
354       }
355     keystrokes = Collections.unmodifiableSet(new HashSet(keystrokes));
356     firePropertyChange(type, defaultFocusKeys[id], keystrokes);
357     defaultFocusKeys[id] = keystrokes;
358   }
359 
getDefaultFocusTraversalKeys(int id)360   public Set getDefaultFocusTraversalKeys(int id)
361   {
362     if (id < FORWARD_TRAVERSAL_KEYS || id > DOWN_CYCLE_TRAVERSAL_KEYS)
363       throw new IllegalArgumentException();
364     return defaultFocusKeys[id];
365   }
366 
getCurrentFocusCycleRoot()367   public Container getCurrentFocusCycleRoot()
368   {
369     // XXX Need an easy way to test if this thread is in the context of the
370     // global focus owner, to avoid creating the exception in the first place.
371     try
372       {
373         return getGlobalCurrentFocusCycleRoot();
374       }
375     catch (SecurityException e)
376       {
377         return null;
378       }
379   }
380 
getGlobalCurrentFocusCycleRoot()381   protected Container getGlobalCurrentFocusCycleRoot()
382   {
383     // XXX Need a way to test if this thread is in the context of the focus
384     // owner, and throw a SecurityException if that is the case.
385     // XXX Implement.
386     return focusCycleRoot;
387   }
388 
setGlobalCurrentFocusCycleRoot(Container cycleRoot)389   public void setGlobalCurrentFocusCycleRoot(Container cycleRoot)
390   {
391     firePropertyChange("currentFocusCycleRoot", focusCycleRoot, cycleRoot);
392     focusCycleRoot = cycleRoot;
393   }
394 
addPropertyChangeListener(PropertyChangeListener l)395   public void addPropertyChangeListener(PropertyChangeListener l)
396   {
397     if (l != null)
398       propertyChangeSupport.addPropertyChangeListener(l);
399   }
400 
removePropertyChangeListener(PropertyChangeListener l)401   public void removePropertyChangeListener(PropertyChangeListener l)
402   {
403     if (l != null)
404       propertyChangeSupport.removePropertyChangeListener(l);
405   }
406 
getPropertyChangeListeners()407   public PropertyChangeListener[] getPropertyChangeListeners()
408   {
409     return propertyChangeSupport.getPropertyChangeListeners();
410   }
411 
addPropertyChangeListener(String name, PropertyChangeListener l)412   public void addPropertyChangeListener(String name, PropertyChangeListener l)
413   {
414     if (l != null)
415       propertyChangeSupport.addPropertyChangeListener(name, l);
416   }
417 
removePropertyChangeListener(String name, PropertyChangeListener l)418   public void removePropertyChangeListener(String name,
419                                            PropertyChangeListener l)
420   {
421     if (l != null)
422       propertyChangeSupport.removePropertyChangeListener(name, l);
423   }
424 
getPropertyChangeListeners(String name)425   public PropertyChangeListener[] getPropertyChangeListeners(String name)
426   {
427     return propertyChangeSupport.getPropertyChangeListeners(name);
428   }
429 
firePropertyChange(String name, Object o, Object n)430   protected void firePropertyChange(String name, Object o, Object n)
431   {
432     propertyChangeSupport.firePropertyChange(name, o, n);
433   }
434 
addVetoableChangeListener(VetoableChangeListener l)435   public void addVetoableChangeListener(VetoableChangeListener l)
436   {
437     if (l != null)
438       vetoableChangeSupport.addVetoableChangeListener(l);
439   }
440 
removeVetoableChangeListener(VetoableChangeListener l)441   public void removeVetoableChangeListener(VetoableChangeListener l)
442   {
443     if (l != null)
444       vetoableChangeSupport.removeVetoableChangeListener(l);
445   }
446 
getVetoableChangeListeners()447   public VetoableChangeListener[] getVetoableChangeListeners()
448   {
449     return vetoableChangeSupport.getVetoableChangeListeners();
450   }
451 
addVetoableChangeListener(String name, VetoableChangeListener l)452   public void addVetoableChangeListener(String name, VetoableChangeListener l)
453   {
454     if (l != null)
455       vetoableChangeSupport.addVetoableChangeListener(name, l);
456   }
457 
removeVetoableChangeListener(String name, VetoableChangeListener l)458   public void removeVetoableChangeListener(String name,
459                                            VetoableChangeListener l)
460   {
461     if (l != null)
462       vetoableChangeSupport.removeVetoableChangeListener(name, l);
463   }
464 
getVetoableChangeListeners(String name)465   public VetoableChangeListener[] getVetoableChangeListeners(String name)
466   {
467     return vetoableChangeSupport.getVetoableChangeListeners(name);
468   }
469 
fireVetoableChange(String name, Object o, Object n)470   protected void fireVetoableChange(String name, Object o, Object n)
471     throws PropertyVetoException
472   {
473     vetoableChangeSupport.fireVetoableChange(name, o, n);
474   }
475 
addKeyEventDispatcher(KeyEventDispatcher dispatcher)476   public void addKeyEventDispatcher(KeyEventDispatcher dispatcher)
477   {
478     if (dispatcher != null)
479       keyEventDispatchers.add(dispatcher);
480   }
481 
removeKeyEventDispatcher(KeyEventDispatcher dispatcher)482   public void removeKeyEventDispatcher(KeyEventDispatcher dispatcher)
483   {
484     keyEventDispatchers.remove(dispatcher);
485   }
486 
getKeyEventDispatchers()487   protected List getKeyEventDispatchers()
488   {
489     return (List) keyEventDispatchers.clone();
490   }
491 
addKeyEventPostProcessor(KeyEventPostProcessor postProcessor)492   public void addKeyEventPostProcessor(KeyEventPostProcessor postProcessor)
493   {
494     if (postProcessor != null)
495       keyEventPostProcessors.add(postProcessor);
496   }
497 
removeKeyEventPostProcessor(KeyEventPostProcessor postProcessor)498   public void removeKeyEventPostProcessor(KeyEventPostProcessor postProcessor)
499   {
500     keyEventPostProcessors.remove(postProcessor);
501   }
502 
getKeyEventPostProcessors()503   protected List getKeyEventPostProcessors()
504   {
505     return (List) keyEventPostProcessors.clone();
506   }
507 
dispatchEvent(AWTEvent e)508   public abstract boolean dispatchEvent(AWTEvent e);
509 
redispatchEvent(Component target, AWTEvent e)510   public final void redispatchEvent(Component target, AWTEvent e)
511   {
512     throw new Error("not implemented");
513   }
514 
dispatchKeyEvent(KeyEvent e)515   public abstract boolean dispatchKeyEvent(KeyEvent e);
516 
postProcessKeyEvent(KeyEvent e)517   public abstract boolean postProcessKeyEvent(KeyEvent e);
518 
processKeyEvent(Component focused, KeyEvent e)519   public abstract void processKeyEvent(Component focused, KeyEvent e);
520 
enqueueKeyEvents(long after, Component untilFocused)521   protected abstract void enqueueKeyEvents(long after, Component untilFocused);
522 
dequeueKeyEvents(long after, Component untilFocused)523   protected abstract void dequeueKeyEvents(long after, Component untilFocused);
524 
discardKeyEvents(Component comp)525   protected abstract void discardKeyEvents(Component comp);
526 
focusNextComponent(Component comp)527   public abstract void focusNextComponent(Component comp);
528 
focusPreviousComponent(Component comp)529   public abstract void focusPreviousComponent(Component comp);
530 
upFocusCycle(Component comp)531   public abstract void upFocusCycle(Component comp);
532 
downFocusCycle(Container cont)533   public abstract void downFocusCycle(Container cont);
534 
focusNextComponent()535   public final void focusNextComponent()
536   {
537     focusNextComponent(focusOwner);
538   }
539 
focusPreviousComponent()540   public final void focusPreviousComponent()
541   {
542     focusPreviousComponent(focusOwner);
543   }
544 
upFocusCycle()545   public final void upFocusCycle()
546   {
547     upFocusCycle(focusOwner);
548   }
549 
downFocusCycle()550   public final void downFocusCycle()
551   {
552     if (focusOwner instanceof Container
553         && ((Container) focusOwner).isFocusCycleRoot())
554       downFocusCycle((Container) focusOwner);
555   }
556 } // class KeyboardFocusManager
557