KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > KeyboardManager


1 /*
2  * @(#)KeyboardManager.java 1.16 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing;
8
9
10 import java.util.*;
11 import java.awt.*;
12 import java.awt.event.*;
13 import java.applet.*;
14 import java.beans.*;
15 import javax.swing.event.*;
16
17 /**
18   * The KeyboardManager class is used to help dispatch keyboard actions for the
19   * WHEN_IN_FOCUSED_WINDOW style actions. Actions with other conditions are handled
20   * directly in JComponent.
21   *
22   * Here's a description of the symantics of how keyboard dispatching should work
23   * atleast as I understand it.
24   *
25   * KeyEvents are dispatched to the focused component. The focus manager gets first
26   * crack at processing this event. If the focus manager doesn't want it, then
27   * the JComponent calls super.processKeyEvent() this allows listeners a chance
28   * to process the event.
29   *
30   * If none of the listeners "consumes" the event then the keybindings get a shot.
31   * This is where things start to get interesting. First, KeyStokes defined with the
32   * WHEN_FOCUSED condition get a chance. If none of these want the event, then the component
33   * walks though it's parents looked for actions of type WHEN_ANCESTOR_OF_FOCUSED_COMPONENT.
34   *
35   * If no one has taken it yet, then it winds up here. We then look for components registered
36   * for WHEN_IN_FOCUSED_WINDOW events and fire to them. Note that if none of those are found
37   * then we pass the event to the menubars and let them have a crack at it. They're handled differently.
38   *
39   * Lastly, we check if we're looking at an internal frame. If we are and no one wanted the event
40   * then we move up to the InternalFrame's creator and see if anyone wants the event (and so on and so on).
41   *
42   *
43   * @see InputMap
44   */

45 class KeyboardManager {
46
47     static KeyboardManager JavaDoc currentManager = new KeyboardManager JavaDoc();
48
49     /**
50       * maps top-level containers to a sub-hashtable full of keystrokes
51       */

52     Hashtable containerMap = new Hashtable();
53
54     /**
55       * Maps component/keystroke pairs to a topLevel container
56       * This is mainly used for fast unregister operations
57       */

58     Hashtable componentKeyStrokeMap = new Hashtable();
59
60     public static KeyboardManager JavaDoc getCurrentManager() {
61         return currentManager;
62     }
63
64     public static void setCurrentManager(KeyboardManager JavaDoc km) {
65         currentManager = km;
66     }
67
68     /**
69       * register keystrokes here which are for the WHEN_IN_FOCUSED_WINDOW
70       * case.
71       * Other types of keystrokes will be handled by walking the hierarchy
72       * That simplifies some potentially hairy stuff.
73       */

74      public void registerKeyStroke(KeyStroke JavaDoc k, JComponent JavaDoc c) {
75          Container topContainer = getTopAncestor(c);
76      if (topContainer == null) {
77          return;
78      }
79      Hashtable keyMap = (Hashtable)containerMap.get(topContainer);
80
81      if (keyMap == null) { // lazy evaluate one
82
keyMap = registerNewTopContainer(topContainer);
83      }
84
85      Object JavaDoc tmp = keyMap.get(k);
86      if (tmp == null) {
87          keyMap.put(k,c);
88      } else if (tmp instanceof Vector) { // if there's a Vector there then add to it.
89
Vector v = (Vector)tmp;
90          if (!v.contains(c)) { // only add if this keystroke isn't registered for this component
91
v.addElement(c);
92          }
93      } else if (tmp instanceof JComponent JavaDoc) {
94        // if a JComponent is there then remove it and replace it with a vector
95
// Then add the old compoennt and the new compoent to the vector
96
// then insert the vector in the table
97
if (tmp != c) { // this means this is already registered for this component, no need to dup
98
Vector v = new Vector();
99            v.addElement(tmp);
100            v.addElement(c);
101            keyMap.put(k, v);
102        }
103      } else {
104          System.out.println("Unexpected condition in registerKeyStroke");
105          Thread.dumpStack();
106      }
107      
108      componentKeyStrokeMap.put(new ComponentKeyStrokePair(c,k), topContainer);
109
110      }
111
112      /**
113        * find the top Window or Applet
114        */

115      private static Container getTopAncestor(JComponent JavaDoc c) {
116         for(Container p = c.getParent(); p != null; p = p.getParent()) {
117             if (p instanceof Window && ((Window)p).isFocusableWindow() ||
118                 p instanceof Applet || p instanceof JInternalFrame JavaDoc) {
119
120                 return p;
121         }
122         }
123         return null;
124      }
125
126      public void unregisterKeyStroke(KeyStroke JavaDoc ks, JComponent JavaDoc c) {
127
128        // component may have already been removed from the hierarchy, we
129
// need to look up the container using the componentKeyStrokeMap.
130

131          ComponentKeyStrokePair ckp = new ComponentKeyStrokePair(c,ks);
132      
133      Object JavaDoc topContainer = componentKeyStrokeMap.get(ckp);
134
135      if (topContainer == null) { // never heard of this pairing, so bail
136
return;
137      }
138
139      Hashtable keyMap = (Hashtable)containerMap.get(topContainer);
140      if (keyMap == null) { // this should never happen, but I'm being safe
141
Thread.dumpStack();
142          return;
143      }
144
145      Object JavaDoc tmp = keyMap.get(ks);
146      if (tmp == null) { // this should never happen, but I'm being safe
147
Thread.dumpStack();
148          return;
149      }
150
151      if (tmp instanceof JComponent JavaDoc && tmp == c) {
152          keyMap.remove(ks); // remove the KeyStroke from the Map
153
//System.out.println("removed a stroke" + ks);
154
} else if (tmp instanceof Vector ) { // this means there is more than one component reg for this key
155
Vector v = (Vector)tmp;
156          v.removeElement(c);
157          if ( v.isEmpty() ) {
158              keyMap.remove(ks); // remove the KeyStroke from the Map
159
//System.out.println("removed a ks vector");
160
}
161      }
162      
163      if ( keyMap.isEmpty() ) { // if no more bindings in this table
164
containerMap.remove(topContainer); // remove table to enable GC
165
//System.out.println("removed a container");
166
}
167
168      componentKeyStrokeMap.remove(ckp);
169      }
170
171     /**
172       * This method is called when the focused component (and none of
173       * its ancestors) want the key event. This will look up the keystroke
174       * to see if any chidren (or subchildren) of the specified container
175       * want a crack at the event.
176       * If one of them wants it, then it will "DO-THE-RIGHT-THING"
177       */

178     public boolean fireKeyboardAction(KeyEvent e, boolean pressed, Container topAncestor) {
179
180          if (e.isConsumed()) {
181           System.out.println("Aquired pre-used event!");
182           Thread.dumpStack();
183          }
184
185          KeyStroke JavaDoc ks;
186
187
188      if(e.getID() == KeyEvent.KEY_TYPED) {
189                ks=KeyStroke.getKeyStroke(e.getKeyChar());
190          } else {
191                ks=KeyStroke.getKeyStroke(e.getKeyCode(), e.getModifiers(), !pressed);
192      }
193
194      Hashtable keyMap = (Hashtable)containerMap.get(topAncestor);
195      if (keyMap != null) { // this container isn't registered, so bail
196

197          Object JavaDoc tmp = keyMap.get(ks);
198
199          if (tmp == null) {
200            // don't do anything
201
} else if ( tmp instanceof JComponent JavaDoc) {
202              JComponent JavaDoc c = (JComponent JavaDoc)tmp;
203          if ( c.isShowing() && c.isEnabled() ) { // only give it out if enabled and visible
204
fireBinding(c, ks, e, pressed);
205          }
206          } else if ( tmp instanceof Vector) { //more than one comp registered for this
207
Vector v = (Vector)tmp;
208                  // There is no well defined order for WHEN_IN_FOCUSED_WINDOW
209
// bindings, but we give precedence to those bindings just
210
// added. This is done so that JMenus WHEN_IN_FOCUSED_WINDOW
211
// bindings are accessed before those of the JRootPane (they
212
// both have a WHEN_IN_FOCUSED_WINDOW binding for enter).
213
for (int counter = v.size() - 1; counter >= 0; counter--) {
214              JComponent JavaDoc c = (JComponent JavaDoc)v.elementAt(counter);
215              //System.out.println("Trying collision: " + c + " vector = "+ v.size());
216
if ( c.isShowing() && c.isEnabled() ) { // don't want to give these out
217
fireBinding(c, ks, e, pressed);
218              if (e.isConsumed())
219                  return true;
220              }
221          }
222          } else {
223              System.out.println( "Unexpected condition in fireKeyboardAction " + tmp);
224          // This means that tmp wasn't null, a JComponent, or a Vector. What is it?
225
Thread.dumpStack();
226          }
227      }
228
229      if (e.isConsumed()) {
230          return true;
231      }
232      // if no one else handled it, then give the menus a crack
233
// The're handled differently. The key is to let any JMenuBars
234
// process the event
235
if ( keyMap != null) {
236          Vector v = (Vector)keyMap.get(JMenuBar JavaDoc.class);
237          if (v != null) {
238              Enumeration iter = v.elements();
239          while (iter.hasMoreElements()) {
240              JMenuBar JavaDoc mb = (JMenuBar JavaDoc)iter.nextElement();
241              if ( mb.isShowing() && mb.isEnabled() ) { // don't want to give these out
242
fireBinding(mb, ks, e, pressed);
243              if (e.isConsumed()) {
244                  return true;
245              }
246              }
247          }
248          }
249      }
250
251      return e.isConsumed();
252     }
253
254     void fireBinding(JComponent JavaDoc c, KeyStroke JavaDoc ks, KeyEvent e, boolean pressed) {
255     if (c.processKeyBinding(ks, e, JComponent.WHEN_IN_FOCUSED_WINDOW,
256                 pressed)) {
257         e.consume();
258     }
259     }
260
261     public void registerMenuBar(JMenuBar JavaDoc mb) {
262         Container top = getTopAncestor(mb);
263     Hashtable keyMap = (Hashtable)containerMap.get(top);
264
265     if (keyMap == null) { // lazy evaluate one
266
keyMap = registerNewTopContainer(top);
267     }
268     // use the menubar class as the key
269
Vector menuBars = (Vector)keyMap.get(JMenuBar JavaDoc.class);
270
271     if (menuBars == null) { // if we don't have a list of menubars,
272
// then make one.
273
menuBars = new Vector();
274         keyMap.put(JMenuBar JavaDoc.class, menuBars);
275     }
276
277     if (!menuBars.contains(mb)) {
278         menuBars.addElement(mb);
279     }
280     }
281
282
283     public void unregisterMenuBar(JMenuBar JavaDoc mb) {
284     Object JavaDoc topContainer = getTopAncestor(mb);
285     Hashtable keyMap = (Hashtable)containerMap.get(topContainer);
286     if (keyMap!=null) {
287         Vector v = (Vector)keyMap.get(JMenuBar JavaDoc.class);
288         if (v != null) {
289         v.removeElement(mb);
290         if (v.isEmpty()) {
291             keyMap.remove(JMenuBar JavaDoc.class);
292             if (keyMap.isEmpty()) {
293             // remove table to enable GC
294
containerMap.remove(topContainer);
295             }
296         }
297         }
298     }
299     }
300     protected Hashtable registerNewTopContainer(Container topContainer) {
301          Hashtable keyMap = new Hashtable();
302          containerMap.put(topContainer, keyMap);
303          return keyMap;
304     }
305
306     /**
307       * This class is used to create keys for a hashtable
308       * which looks up topContainers based on component, keystroke pairs
309       * This is used to make unregistering KeyStrokes fast
310       */

311     class ComponentKeyStrokePair {
312         Object JavaDoc component;
313         Object JavaDoc keyStroke;
314         
315         public ComponentKeyStrokePair(Object JavaDoc comp, Object JavaDoc key) {
316         component = comp;
317         keyStroke = key;
318     }
319
320         public boolean equals(Object JavaDoc o) {
321         if ( !(o instanceof ComponentKeyStrokePair)) {
322             return false;
323         }
324         ComponentKeyStrokePair ckp = (ComponentKeyStrokePair)o;
325         return ((component.equals(ckp.component)) && (keyStroke.equals(ckp.keyStroke)));
326     }
327
328         public int hashCode() {
329         return component.hashCode() * keyStroke.hashCode();
330     }
331
332     }
333
334 } // end KeyboardManager
335
Popular Tags