/jEdit/tags/jedit-4-2-pre4/org/gjt/sp/jedit/gui/DefaultInputHandler.java

# · Java · 485 lines · 297 code · 38 blank · 150 comment · 53 complexity · 1d4315a1e6f2e4f98c7393a04efdb4f8 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, 2003 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 4813 2003-06-27 02:24:05Z 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. * @since jEdit 4.2pre1
  74. */
  75. public void addKeyBinding(String keyBinding, String action)
  76. {
  77. _addKeyBinding(keyBinding,(Object)action);
  78. } //}}}
  79. //{{{ addKeyBinding() method
  80. /**
  81. * Adds a key binding to this input handler. The key binding is
  82. * a list of white space separated key strokes of the form
  83. * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
  84. * or S for Shift, and key is either a character (a-z) or a field
  85. * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
  86. * @param keyBinding The key binding
  87. * @param action The action
  88. */
  89. public void addKeyBinding(String keyBinding, EditAction action)
  90. {
  91. _addKeyBinding(keyBinding,(Object)action);
  92. } //}}}
  93. //{{{ removeKeyBinding() method
  94. /**
  95. * Removes a key binding from this input handler. This is not yet
  96. * implemented.
  97. * @param keyBinding The key binding
  98. */
  99. public void removeKeyBinding(String keyBinding)
  100. {
  101. Hashtable current = bindings;
  102. StringTokenizer st = new StringTokenizer(keyBinding);
  103. while(st.hasMoreTokens())
  104. {
  105. String keyCodeStr = st.nextToken();
  106. KeyEventTranslator.Key keyStroke = KeyEventTranslator.parseKey(keyCodeStr);
  107. if(keyStroke == null)
  108. return;
  109. if(st.hasMoreTokens())
  110. {
  111. Object o = current.get(keyStroke);
  112. if(o instanceof Hashtable)
  113. current = ((Hashtable)o);
  114. else if(o != null)
  115. {
  116. // we have binding foo
  117. // but user asks to remove foo bar?
  118. current.remove(keyStroke);
  119. return;
  120. }
  121. else
  122. {
  123. // user asks to remove non-existent
  124. return;
  125. }
  126. }
  127. else
  128. current.remove(keyStroke);
  129. }
  130. } //}}}
  131. //{{{ removeAllKeyBindings() method
  132. /**
  133. * Removes all key bindings from this input handler.
  134. */
  135. public void removeAllKeyBindings()
  136. {
  137. bindings.clear();
  138. } //}}}
  139. //{{{ getKeyBinding() method
  140. /**
  141. * Returns either an edit action, or a hashtable if the specified key
  142. * is a prefix.
  143. * @param keyBinding The key binding
  144. * @since jEdit 3.2pre5
  145. */
  146. public Object getKeyBinding(String keyBinding)
  147. {
  148. Hashtable current = bindings;
  149. StringTokenizer st = new StringTokenizer(keyBinding);
  150. while(st.hasMoreTokens())
  151. {
  152. KeyEventTranslator.Key keyStroke = KeyEventTranslator.parseKey(keyBinding);
  153. if(keyStroke == null)
  154. return null;
  155. if(st.hasMoreTokens())
  156. {
  157. Object o = current.get(keyStroke);
  158. if(o instanceof Hashtable)
  159. current = (Hashtable)o;
  160. else
  161. return o;
  162. }
  163. else
  164. {
  165. return current.get(keyStroke);
  166. }
  167. }
  168. return null;
  169. } //}}}
  170. //{{{ isPrefixActive() method
  171. /**
  172. * Returns if a prefix key has been pressed.
  173. */
  174. public boolean isPrefixActive()
  175. {
  176. return bindings != currentBindings;
  177. } //}}}
  178. //{{{ keyPressed() method
  179. /**
  180. * Handle a key pressed event. This will look up the binding for
  181. * the key stroke and execute it.
  182. */
  183. public void keyPressed(KeyEvent evt)
  184. {
  185. KeyEventTranslator.Key keyStroke = KeyEventTranslator.translateKeyEvent(evt);
  186. if(keyStroke != null)
  187. handleKey(keyStroke);
  188. } //}}}
  189. //{{{ keyTyped() method
  190. /**
  191. * Handle a key typed event. This will look up the binding for
  192. * the key stroke and execute it.
  193. */
  194. public void keyTyped(KeyEvent evt)
  195. {
  196. KeyEventTranslator.Key keyStroke = KeyEventTranslator.translateKeyEvent(evt);
  197. if(keyStroke != null)
  198. handleKey(keyStroke);
  199. } //}}}
  200. //{{{ getSymbolicModifierName() method
  201. /**
  202. * Returns a the symbolic modifier name for the specified Java modifier
  203. * flag.
  204. *
  205. * @param mod A modifier constant from <code>InputEvent</code>
  206. *
  207. * @since jEdit 4.1pre3
  208. */
  209. public static char getSymbolicModifierName(int mod)
  210. {
  211. return KeyEventTranslator.getSymbolicModifierName(mod);
  212. } //}}}
  213. //{{{ getModifierString() method
  214. /**
  215. * Returns a string containing symbolic modifier names set in the
  216. * specified event.
  217. *
  218. * @param evt The event
  219. *
  220. * @since jEdit 4.1pre3
  221. */
  222. public static String getModifierString(InputEvent evt)
  223. {
  224. return KeyEventTranslator.getModifierString(evt);
  225. } //}}}
  226. //{{{ parseKeyStroke() method
  227. /**
  228. * Converts a string to a keystroke. The string should be of the
  229. * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
  230. * is any combination of A for Alt, C for Control, S for Shift
  231. * or M for Meta, and <i>shortcut</i> is either a single character,
  232. * or a keycode name from the <code>KeyEvent</code> class, without
  233. * the <code>VK_</code> prefix.
  234. * @param keyStroke A string description of the key stroke
  235. */
  236. public static KeyStroke parseKeyStroke(String keyStroke)
  237. {
  238. if(keyStroke == null)
  239. return null;
  240. int modifiers = 0;
  241. int index = keyStroke.indexOf('+');
  242. if(index != -1)
  243. {
  244. for(int i = 0; i < index; i++)
  245. {
  246. switch(Character.toUpperCase(keyStroke
  247. .charAt(i)))
  248. {
  249. case 'A':
  250. modifiers |= KeyEventTranslator.a;
  251. break;
  252. case 'C':
  253. modifiers |= KeyEventTranslator.c;
  254. break;
  255. case 'M':
  256. modifiers |= KeyEventTranslator.m;
  257. break;
  258. case 'S':
  259. modifiers |= KeyEventTranslator.s;
  260. break;
  261. }
  262. }
  263. }
  264. String key = keyStroke.substring(index + 1);
  265. if(key.length() == 1)
  266. {
  267. char ch = key.charAt(0);
  268. if(modifiers == 0)
  269. return KeyStroke.getKeyStroke(ch);
  270. else
  271. {
  272. return KeyStroke.getKeyStroke(Character.toUpperCase(ch),
  273. modifiers);
  274. }
  275. }
  276. else if(key.length() == 0)
  277. {
  278. Log.log(Log.ERROR,DefaultInputHandler.class,
  279. "Invalid key stroke: " + keyStroke);
  280. return null;
  281. }
  282. else
  283. {
  284. int ch;
  285. try
  286. {
  287. ch = KeyEvent.class.getField("VK_".concat(key))
  288. .getInt(null);
  289. }
  290. catch(Exception e)
  291. {
  292. Log.log(Log.ERROR,DefaultInputHandler.class,
  293. "Invalid key stroke: "
  294. + keyStroke);
  295. return null;
  296. }
  297. return KeyStroke.getKeyStroke(ch,modifiers);
  298. }
  299. } //}}}
  300. //{{{ Private members
  301. // Stores prefix name in bindings hashtable
  302. private static Object PREFIX_STR = "PREFIX_STR";
  303. private Hashtable bindings;
  304. private Hashtable currentBindings;
  305. //{{{ handleKey() method
  306. private void handleKey(KeyEventTranslator.Key keyStroke)
  307. {
  308. char input = '\0';
  309. if(keyStroke.modifiers == null
  310. || keyStroke.modifiers.equals("S"))
  311. {
  312. switch(keyStroke.key)
  313. {
  314. case '\n':
  315. case '\t':
  316. case ' ':
  317. input = (char)keyStroke.key;
  318. break;
  319. default:
  320. input = keyStroke.input;
  321. }
  322. }
  323. if(readNextChar != null)
  324. {
  325. if(input != '\0')
  326. {
  327. setCurrentBindings(bindings);
  328. invokeReadNextChar(input);
  329. repeatCount = 1;
  330. return;
  331. }
  332. else
  333. {
  334. readNextChar = null;
  335. view.getStatus().setMessage(null);
  336. }
  337. }
  338. Object o = currentBindings.get(keyStroke);
  339. if(o == null)
  340. {
  341. // Don't beep if the user presses some
  342. // key we don't know about unless a
  343. // prefix is active. Otherwise it will
  344. // beep when caps lock is pressed, etc.
  345. if(currentBindings != bindings)
  346. {
  347. Toolkit.getDefaultToolkit().beep();
  348. // F10 should be passed on, but C+e F10
  349. // shouldn't
  350. repeatCount = 1;
  351. setCurrentBindings(bindings);
  352. }
  353. if(input != '\0')
  354. userInput(input);
  355. else
  356. {
  357. // this is retarded. excuse me while I drool
  358. // and make stupid noises
  359. switch(keyStroke.key)
  360. {
  361. case KeyEvent.VK_NUMPAD0:
  362. case KeyEvent.VK_NUMPAD1:
  363. case KeyEvent.VK_NUMPAD2:
  364. case KeyEvent.VK_NUMPAD3:
  365. case KeyEvent.VK_NUMPAD4:
  366. case KeyEvent.VK_NUMPAD5:
  367. case KeyEvent.VK_NUMPAD6:
  368. case KeyEvent.VK_NUMPAD7:
  369. case KeyEvent.VK_NUMPAD8:
  370. case KeyEvent.VK_NUMPAD9:
  371. case KeyEvent.VK_MULTIPLY:
  372. case KeyEvent.VK_ADD:
  373. /* case KeyEvent.VK_SEPARATOR: */
  374. case KeyEvent.VK_SUBTRACT:
  375. case KeyEvent.VK_DECIMAL:
  376. case KeyEvent.VK_DIVIDE:
  377. KeyEventWorkaround.numericKeypadKey();
  378. break;
  379. }
  380. }
  381. }
  382. else if(o instanceof Hashtable)
  383. {
  384. setCurrentBindings((Hashtable)o);
  385. }
  386. else if(o instanceof String)
  387. {
  388. setCurrentBindings(bindings);
  389. invokeAction((String)o);
  390. }
  391. else if(o instanceof EditAction)
  392. {
  393. setCurrentBindings(bindings);
  394. invokeAction((EditAction)o);
  395. }
  396. } //}}}
  397. //{{{ setCurrentBindings() method
  398. private void setCurrentBindings(Hashtable bindings)
  399. {
  400. view.getStatus().setMessage((String)bindings.get(PREFIX_STR));
  401. currentBindings = bindings;
  402. } //}}}
  403. //{{{ _addKeyBinding() method
  404. /**
  405. * Adds a key binding to this input handler. The key binding is
  406. * a list of white space separated key strokes of the form
  407. * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
  408. * or S for Shift, and key is either a character (a-z) or a field
  409. * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
  410. * @param keyBinding The key binding
  411. * @param action The action
  412. */
  413. public void _addKeyBinding(String keyBinding, Object action)
  414. {
  415. Hashtable current = bindings;
  416. String prefixStr = null;
  417. StringTokenizer st = new StringTokenizer(keyBinding);
  418. while(st.hasMoreTokens())
  419. {
  420. String keyCodeStr = st.nextToken();
  421. if(prefixStr == null)
  422. prefixStr = keyCodeStr;
  423. else
  424. prefixStr = prefixStr + " " + keyCodeStr;
  425. KeyEventTranslator.Key keyStroke = KeyEventTranslator.parseKey(keyCodeStr);
  426. if(keyStroke == null)
  427. return;
  428. if(st.hasMoreTokens())
  429. {
  430. Object o = current.get(keyStroke);
  431. if(o instanceof Hashtable)
  432. current = (Hashtable)o;
  433. else
  434. {
  435. Hashtable hash = new Hashtable();
  436. hash.put(PREFIX_STR,prefixStr);
  437. o = hash;
  438. current.put(keyStroke,o);
  439. current = (Hashtable)o;
  440. }
  441. }
  442. else
  443. current.put(keyStroke,action);
  444. }
  445. } //}}}
  446. //}}}
  447. }