KeyboardManager.java 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /* KeyboardManager.java --
  2. Copyright (C) 2005 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package javax.swing;
  32. import java.applet.Applet;
  33. import java.awt.Component;
  34. import java.awt.Container;
  35. import java.awt.Window;
  36. import java.awt.event.KeyEvent;
  37. import java.util.Enumeration;
  38. import java.util.Hashtable;
  39. import java.util.Vector;
  40. import java.util.WeakHashMap;
  41. /**
  42. * This class maintains a mapping from top-level containers to a
  43. * Hashtable. The Hashtable maps KeyStrokes to Components to be used when
  44. * Components register keyboard actions with the condition
  45. * JComponent.WHEN_IN_FOCUSED_WINDOW.
  46. *
  47. * @author Anthony Balkissoon abalkiss at redhat dot com
  48. *
  49. */
  50. class KeyboardManager
  51. {
  52. /** Shared instance of KeyboardManager **/
  53. static KeyboardManager manager = new KeyboardManager();
  54. /**
  55. * A mapping between top level containers and Hashtables that
  56. * map KeyStrokes to Components.
  57. */
  58. WeakHashMap topLevelLookup = new WeakHashMap();
  59. /**
  60. * A mapping between top level containers and Vectors of JMenuBars
  61. * used to allow all the JMenuBars within a top level container
  62. * a chance to consume key events.
  63. */
  64. Hashtable menuBarLookup = new Hashtable();
  65. /**
  66. * Returns the shared instance of KeyboardManager.
  67. * @return the shared instance of KeybaordManager.
  68. */
  69. public static KeyboardManager getManager()
  70. {
  71. return manager;
  72. }
  73. /**
  74. * Returns the top-level ancestor for the given JComponent.
  75. * @param c the JComponent whose top-level ancestor we want
  76. * @return the top-level ancestor for the given JComponent.
  77. */
  78. static Container findTopLevel (Component c)
  79. {
  80. Container topLevel = (c instanceof Container) ? (Container) c
  81. : c.getParent();
  82. while (topLevel != null &&
  83. !(topLevel instanceof Window) &&
  84. !(topLevel instanceof Applet) &&
  85. !(topLevel instanceof JInternalFrame))
  86. topLevel = topLevel.getParent();
  87. return topLevel;
  88. }
  89. /**
  90. * Returns the Hashtable that maps KeyStrokes to Components, for
  91. * the specified top-level container c. If no Hashtable exists
  92. * we create and register it here and return the newly created
  93. * Hashtable.
  94. *
  95. * @param c the top-level container whose Hashtable we want
  96. * @return the Hashtable mapping KeyStrokes to Components for the
  97. * specified top-level container
  98. */
  99. Hashtable getHashtableForTopLevel (Container c)
  100. {
  101. Hashtable keyToComponent = (Hashtable)topLevelLookup.get(c);
  102. if (keyToComponent == null)
  103. {
  104. keyToComponent = new Hashtable();
  105. topLevelLookup.put(c, keyToComponent);
  106. }
  107. return keyToComponent;
  108. }
  109. /**
  110. * Registers a KeyStroke with a Component. This does not register
  111. * the KeyStroke to a specific Action. When searching for a
  112. * WHEN_IN_FOCUSED_WINDOW binding we will first go up to the focused
  113. * top-level Container, then get the Hashtable that maps KeyStrokes
  114. * to components for that particular top-level Container, then
  115. * call processKeyBindings on that component with the condition
  116. * JComponent.WHEN_IN_FOCUSED_WINDOW.
  117. * @param comp the JComponent associated with the KeyStroke
  118. * @param key the KeyStroke
  119. */
  120. public void registerBinding(JComponent comp, KeyStroke key)
  121. {
  122. // This method associates a KeyStroke with a particular JComponent
  123. // When the KeyStroke occurs, if this component's top-level ancestor
  124. // has focus (one of its children is the focused Component) then
  125. // comp.processKeyBindings will be called with condition
  126. // JComponent.WHEN_IN_FOCUSED_WINDOW.
  127. // Look for the JComponent's top-level parent and return if it is null
  128. Container topLevel = findTopLevel(comp);
  129. if (topLevel == null)
  130. return;
  131. // Now get the Hashtable for this top-level container
  132. Hashtable keyToComponent = getHashtableForTopLevel(topLevel);
  133. // And add the new binding to this Hashtable
  134. // FIXME: should allow more than one JComponent to be associated
  135. // with a KeyStroke, in case one of them is disabled
  136. keyToComponent.put(key, comp);
  137. }
  138. public void clearBindingsForComp(JComponent comp)
  139. {
  140. // This method clears all the WHEN_IN_FOCUSED_WINDOW bindings associated
  141. // with <code>comp</code>. This is used for a terribly ineffcient
  142. // strategy in which JComponent.updateComponentInputMap simply clears
  143. // all bindings associated with its component and then reloads all the
  144. // bindings from the updated ComponentInputMap. This is only a preliminary
  145. // strategy and should be improved upon once the WHEN_IN_FOCUSED_WINDOW
  146. // bindings work.
  147. // Find the top-level ancestor
  148. Container topLevel = findTopLevel(comp);
  149. if (topLevel == null)
  150. return;
  151. // And now get its Hashtable
  152. Hashtable keyToComponent = getHashtableForTopLevel(topLevel);
  153. Enumeration keys = keyToComponent.keys();
  154. Object temp;
  155. // Iterate through the keys and remove any key whose value is comp
  156. while (keys.hasMoreElements())
  157. {
  158. temp = keys.nextElement();
  159. if (comp == (JComponent)keyToComponent.get(temp))
  160. keyToComponent.remove(temp);
  161. }
  162. }
  163. /**
  164. * This method registers all the bindings in the given ComponentInputMap.
  165. * Rather than call registerBinding on all the keys, we do the work here
  166. * so that we don't duplicate finding the top-level container and
  167. * getting its Hashtable.
  168. *
  169. * @param map the ComponentInputMap whose bindings we want to register
  170. */
  171. public void registerEntireMap (ComponentInputMap map)
  172. {
  173. if (map == null)
  174. return;
  175. JComponent comp = map.getComponent();
  176. KeyStroke[] keys = map.allKeys();
  177. if (keys == null)
  178. return;
  179. // Find the top-level container associated with this ComponentInputMap
  180. Container topLevel = findTopLevel(comp);
  181. if (topLevel == null)
  182. return;
  183. // Register the KeyStrokes in the top-level container's Hashtable
  184. Hashtable keyToComponent = getHashtableForTopLevel(topLevel);
  185. for (int i = 0; i < keys.length; i++)
  186. keyToComponent.put(keys[i], comp);
  187. }
  188. public boolean processKeyStroke (Component comp, KeyStroke key, KeyEvent e)
  189. {
  190. boolean pressed = e.getID() == KeyEvent.KEY_PRESSED;
  191. // Look for the top-level ancestor
  192. Container topLevel = findTopLevel(comp);
  193. if (topLevel == null)
  194. return false;
  195. // Now get the Hashtable for that top-level container
  196. Hashtable keyToComponent = getHashtableForTopLevel(topLevel);
  197. Enumeration keys = keyToComponent.keys();
  198. JComponent target = (JComponent)keyToComponent.get(key);
  199. if (target != null && target.processKeyBinding
  200. (key, e, JComponent.WHEN_IN_FOCUSED_WINDOW, pressed))
  201. return true;
  202. // Have to give all the JMenuBars a chance to consume the event
  203. Vector menuBars = getVectorForTopLevel(topLevel);
  204. for (int i = 0; i < menuBars.size(); i++)
  205. if (((JMenuBar)menuBars.elementAt(i)).processKeyBinding(key, e, JComponent.WHEN_IN_FOCUSED_WINDOW, pressed))
  206. return true;
  207. return false;
  208. }
  209. /**
  210. * Returns the Vector of JMenuBars associated with the top-level
  211. * @param c the top-level container whose JMenuBar Vector we want
  212. * @return the Vector of JMenuBars for this top level container
  213. */
  214. Vector getVectorForTopLevel(Container c)
  215. {
  216. Vector result = (Vector) menuBarLookup.get(c);
  217. if (result == null)
  218. {
  219. result = new Vector();
  220. menuBarLookup.put (c, result);
  221. }
  222. return result;
  223. }
  224. /**
  225. * In processKeyStroke, KeyManager must give all JMenuBars in the
  226. * focused top-level container a chance to process the event. So,
  227. * JMenuBars must be registered in KeyManager and associated with a
  228. * top-level container. That's what this method is for.
  229. * @param menuBar the JMenuBar to register
  230. */
  231. public void registerJMenuBar (JMenuBar menuBar)
  232. {
  233. Container topLevel = findTopLevel(menuBar);
  234. Vector menuBars = getVectorForTopLevel(topLevel);
  235. if (!menuBars.contains(menuBar))
  236. menuBars.add(menuBar);
  237. }
  238. /**
  239. * Unregisters a JMenuBar from its top-level container. This is
  240. * called before the JMenuBar is actually removed from the container
  241. * so findTopLevel will still find us the correct top-level container.
  242. * @param menuBar the JMenuBar to unregister.
  243. */
  244. public void unregisterJMenuBar (JMenuBar menuBar)
  245. {
  246. Container topLevel = findTopLevel(menuBar);
  247. Vector menuBars = getVectorForTopLevel(topLevel);
  248. if (menuBars.contains(menuBar))
  249. menuBars.remove(menuBar);
  250. }
  251. }