/jEdit/tags/jedit-4-0-pre3/org/gjt/sp/jedit/gui/DefaultInputHandler.java

# · Java · 408 lines · 265 code · 35 blank · 108 comment · 61 complexity · e3e518be8440d02eb3673437bb789c22 MD5 · raw file

  1. /*
  2. * DefaultInputHandler.java - Default implementation of an input handler
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999, 2000, 2001 Slava Pestov
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package org.gjt.sp.jedit.gui;
  23. //{{{ Imports
  24. import javax.swing.KeyStroke;
  25. import java.awt.event.*;
  26. import java.awt.Toolkit;
  27. import java.util.Hashtable;
  28. import java.util.StringTokenizer;
  29. import org.gjt.sp.jedit.*;
  30. import org.gjt.sp.util.Log;
  31. //}}}
  32. /**
  33. * The default input handler. It maps sequences of keystrokes into actions
  34. * and inserts key typed events into the text area.
  35. * @author Slava Pestov
  36. * @version $Id: DefaultInputHandler.java 3932 2001-12-03 01:17:46Z spestov $
  37. */
  38. public class DefaultInputHandler extends InputHandler
  39. {
  40. //{{{ DefaultInputHandler constructor
  41. /**
  42. * Creates a new input handler with no key bindings defined.
  43. * @param view The view
  44. */
  45. public DefaultInputHandler(View view)
  46. {
  47. super(view);
  48. bindings = currentBindings = new Hashtable();
  49. } //}}}
  50. //{{{ DefaultInputHandler constructor
  51. /**
  52. * Creates a new input handler with the same set of key bindings
  53. * as the one specified. Note that both input handlers share
  54. * a pointer to exactly the same key binding table; so adding
  55. * a key binding in one will also add it to the other.
  56. * @param copy The input handler to copy key bindings from
  57. * @param view The view
  58. */
  59. public DefaultInputHandler(View view, DefaultInputHandler copy)
  60. {
  61. super(view);
  62. bindings = currentBindings = copy.bindings;
  63. } //}}}
  64. //{{{ addKeyBinding() method
  65. /**
  66. * Adds a key binding to this input handler. The key binding is
  67. * a list of white space separated key strokes of the form
  68. * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
  69. * or S for Shift, and key is either a character (a-z) or a field
  70. * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
  71. * @param keyBinding The key binding
  72. * @param action The action
  73. */
  74. public void addKeyBinding(String keyBinding, EditAction action)
  75. {
  76. Hashtable current = bindings;
  77. StringTokenizer st = new StringTokenizer(keyBinding);
  78. while(st.hasMoreTokens())
  79. {
  80. KeyStroke keyStroke = parseKeyStroke(st.nextToken());
  81. if(keyStroke == null)
  82. return;
  83. if(st.hasMoreTokens())
  84. {
  85. Object o = current.get(keyStroke);
  86. if(o instanceof Hashtable)
  87. current = (Hashtable)o;
  88. else
  89. {
  90. o = new Hashtable();
  91. current.put(keyStroke,o);
  92. current = (Hashtable)o;
  93. }
  94. }
  95. else
  96. current.put(keyStroke,action);
  97. }
  98. } //}}}
  99. //{{{ removeKeyBinding() method
  100. /**
  101. * Removes a key binding from this input handler. This is not yet
  102. * implemented.
  103. * @param keyBinding The key binding
  104. */
  105. public void removeKeyBinding(String keyBinding)
  106. {
  107. throw new InternalError("Not yet implemented");
  108. } //}}}
  109. //{{{ removeAllKeyBindings() method
  110. /**
  111. * Removes all key bindings from this input handler.
  112. */
  113. public void removeAllKeyBindings()
  114. {
  115. bindings.clear();
  116. } //}}}
  117. //{{{ getKeyBinding() method
  118. /**
  119. * Returns either an edit action, or a hashtable if the specified key
  120. * is a prefix.
  121. * @param keyBinding The key binding
  122. * @since jEdit 3.2pre5
  123. */
  124. public Object getKeyBinding(String keyBinding)
  125. {
  126. Hashtable current = bindings;
  127. StringTokenizer st = new StringTokenizer(keyBinding);
  128. while(st.hasMoreTokens())
  129. {
  130. KeyStroke keyStroke = parseKeyStroke(st.nextToken());
  131. if(keyStroke == null)
  132. return null;
  133. if(st.hasMoreTokens())
  134. {
  135. Object o = current.get(keyStroke);
  136. if(o instanceof Hashtable)
  137. current = (Hashtable)o;
  138. else
  139. return o;
  140. }
  141. else
  142. {
  143. return current.get(keyStroke);
  144. }
  145. }
  146. return null;
  147. } //}}}
  148. //{{{ isPrefixActive() method
  149. /**
  150. * Returns if a prefix key has been pressed.
  151. */
  152. public boolean isPrefixActive()
  153. {
  154. return bindings != currentBindings;
  155. } //}}}
  156. //{{{ keyPressed() method
  157. /**
  158. * Handle a key pressed event. This will look up the binding for
  159. * the key stroke and execute it.
  160. */
  161. public void keyPressed(KeyEvent evt)
  162. {
  163. int keyCode = evt.getKeyCode();
  164. int modifiers = evt.getModifiers();
  165. if(!((evt.isControlDown() ^ evt.isAltDown())
  166. || evt.isMetaDown()))
  167. {
  168. // if modifier active, handle all keys, otherwise
  169. // only some
  170. if((keyCode >= KeyEvent.VK_A && keyCode <= KeyEvent.VK_Z)
  171. || (keyCode >= KeyEvent.VK_0 && keyCode <= KeyEvent.VK_9))
  172. {
  173. return;
  174. }
  175. else if(readNextChar != null && !evt.isActionKey()
  176. && keyCode != KeyEvent.VK_TAB
  177. && keyCode != KeyEvent.VK_ENTER)
  178. {
  179. return;
  180. }
  181. else
  182. {
  183. // ok even with no modifiers
  184. }
  185. }
  186. KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
  187. modifiers);
  188. Object o = currentBindings.get(keyStroke);
  189. if(o == null)
  190. {
  191. // Don't beep if the user presses some
  192. // key we don't know about unless a
  193. // prefix is active. Otherwise it will
  194. // beep when caps lock is pressed, etc.
  195. if(currentBindings != bindings)
  196. {
  197. Toolkit.getDefaultToolkit().beep();
  198. // F10 should be passed on, but C+e F10
  199. // shouldn't
  200. repeatCount = 0;
  201. repeat = false;
  202. evt.consume();
  203. currentBindings = bindings;
  204. }
  205. else if(modifiers == 0 && (keyCode == KeyEvent.VK_ENTER
  206. || keyCode == KeyEvent.VK_TAB))
  207. {
  208. userInput((char)keyCode);
  209. evt.consume();
  210. }
  211. }
  212. if(readNextChar != null)
  213. {
  214. readNextChar = null;
  215. view.getStatus().setMessage(null);
  216. }
  217. if(o instanceof EditAction)
  218. {
  219. currentBindings = bindings;
  220. invokeAction((EditAction)o);
  221. evt.consume();
  222. }
  223. else if(o instanceof Hashtable)
  224. {
  225. currentBindings = (Hashtable)o;
  226. evt.consume();
  227. }
  228. } //}}}
  229. //{{{ keyTyped() method
  230. /**
  231. * Handle a key typed event. This inserts the key into the text area.
  232. */
  233. public void keyTyped(KeyEvent evt)
  234. {
  235. char c = evt.getKeyChar();
  236. // ignore
  237. if(c == '\b')
  238. return;
  239. KeyStroke keyStroke;
  240. // this is a hack. a literal space is impossible to
  241. // insert in a key binding string, but you can write
  242. // SPACE.
  243. switch(c)
  244. {
  245. case ' ':
  246. keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
  247. evt.getModifiers());
  248. break;
  249. case '\t':
  250. keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
  251. evt.getModifiers());
  252. break;
  253. case '\n':
  254. keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
  255. evt.getModifiers());
  256. break;
  257. default:
  258. keyStroke = KeyStroke.getKeyStroke(c);
  259. break;
  260. }
  261. Object o = currentBindings.get(keyStroke);
  262. if(o instanceof Hashtable)
  263. {
  264. currentBindings = (Hashtable)o;
  265. }
  266. else if(o instanceof EditAction)
  267. {
  268. currentBindings = bindings;
  269. invokeAction((EditAction)o);
  270. }
  271. else
  272. {
  273. // otherwise, reset to default map and do user input
  274. currentBindings = bindings;
  275. if(repeat && Character.isDigit(c))
  276. {
  277. repeatCount *= 10;
  278. repeatCount += (c - '0');
  279. view.getStatus().setMessage(null);
  280. }
  281. else
  282. userInput(c);
  283. }
  284. } //}}}
  285. //{{{ parseKeyStroke() method
  286. /**
  287. * Converts a string to a keystroke. The string should be of the
  288. * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
  289. * is any combination of A for Alt, C for Control, S for Shift
  290. * or M for Meta, and <i>shortcut</i> is either a single character,
  291. * or a keycode name from the <code>KeyEvent</code> class, without
  292. * the <code>VK_</code> prefix.
  293. * @param keyStroke A string description of the key stroke
  294. */
  295. public static KeyStroke parseKeyStroke(String keyStroke)
  296. {
  297. if(keyStroke == null)
  298. return null;
  299. int modifiers = 0;
  300. int index = keyStroke.indexOf('+');
  301. if(index != -1)
  302. {
  303. for(int i = 0; i < index; i++)
  304. {
  305. switch(Character.toUpperCase(keyStroke
  306. .charAt(i)))
  307. {
  308. case 'A':
  309. modifiers |= InputEvent.ALT_MASK;
  310. break;
  311. case 'C':
  312. if(macOS)
  313. modifiers |= InputEvent.META_MASK;
  314. else
  315. modifiers |= InputEvent.CTRL_MASK;
  316. break;
  317. case 'M':
  318. if(macOS)
  319. modifiers |= InputEvent.CTRL_MASK;
  320. else
  321. modifiers |= InputEvent.META_MASK;
  322. break;
  323. case 'S':
  324. modifiers |= InputEvent.SHIFT_MASK;
  325. break;
  326. }
  327. }
  328. }
  329. String key = keyStroke.substring(index + 1);
  330. if(key.length() == 1)
  331. {
  332. char ch = key.charAt(0);
  333. if(modifiers == 0)
  334. return KeyStroke.getKeyStroke(ch);
  335. else
  336. {
  337. return KeyStroke.getKeyStroke(Character.toUpperCase(ch),
  338. modifiers);
  339. }
  340. }
  341. else if(key.length() == 0)
  342. {
  343. Log.log(Log.ERROR,DefaultInputHandler.class,
  344. "Invalid key stroke: " + keyStroke);
  345. return null;
  346. }
  347. else
  348. {
  349. int ch;
  350. try
  351. {
  352. ch = KeyEvent.class.getField("VK_".concat(key))
  353. .getInt(null);
  354. }
  355. catch(Exception e)
  356. {
  357. Log.log(Log.ERROR,DefaultInputHandler.class,
  358. "Invalid key stroke: "
  359. + keyStroke);
  360. return null;
  361. }
  362. return KeyStroke.getKeyStroke(ch,modifiers);
  363. }
  364. } //}}}
  365. //{{{ Private members
  366. private Hashtable bindings;
  367. private Hashtable currentBindings;
  368. private static boolean macOS;
  369. static
  370. {
  371. macOS = (System.getProperty("os.name").indexOf("Mac") != -1);
  372. } //}}}
  373. }