PageRenderTime 42ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 414 lines | 272 code | 33 blank | 109 comment | 66 complexity | 1a1f3d771f86efd62bd89e354d385c8a MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  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 4012 2002-02-05 06:28:10Z 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(keyCode == KeyEvent.VK_SPACE)
  176. {
  177. return;
  178. }
  179. else if(readNextChar != null)
  180. {
  181. if(keyCode == KeyEvent.VK_ESCAPE)
  182. {
  183. readNextChar = null;
  184. view.getStatus().setMessage(null);
  185. }
  186. else if(!evt.isActionKey()
  187. && keyCode != KeyEvent.VK_TAB
  188. && keyCode != KeyEvent.VK_ENTER)
  189. {
  190. return;
  191. }
  192. }
  193. else
  194. {
  195. // ok even with no modifiers
  196. }
  197. }
  198. KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
  199. modifiers);
  200. Object o = currentBindings.get(keyStroke);
  201. if(o == null)
  202. {
  203. // Don't beep if the user presses some
  204. // key we don't know about unless a
  205. // prefix is active. Otherwise it will
  206. // beep when caps lock is pressed, etc.
  207. if(currentBindings != bindings)
  208. {
  209. Toolkit.getDefaultToolkit().beep();
  210. // F10 should be passed on, but C+e F10
  211. // shouldn't
  212. repeatCount = 0;
  213. repeat = false;
  214. evt.consume();
  215. currentBindings = bindings;
  216. }
  217. else if(modifiers == 0 && (keyCode == KeyEvent.VK_ENTER
  218. || keyCode == KeyEvent.VK_TAB))
  219. {
  220. userInput((char)keyCode);
  221. evt.consume();
  222. }
  223. }
  224. if(readNextChar != null)
  225. {
  226. readNextChar = null;
  227. view.getStatus().setMessage(null);
  228. }
  229. if(o instanceof EditAction)
  230. {
  231. currentBindings = bindings;
  232. invokeAction((EditAction)o);
  233. evt.consume();
  234. }
  235. else if(o instanceof Hashtable)
  236. {
  237. currentBindings = (Hashtable)o;
  238. evt.consume();
  239. }
  240. } //}}}
  241. //{{{ keyTyped() method
  242. /**
  243. * Handle a key typed event. This inserts the key into the text area.
  244. */
  245. public void keyTyped(KeyEvent evt)
  246. {
  247. char c = evt.getKeyChar();
  248. // ignore
  249. if(c == '\b')
  250. return;
  251. KeyStroke keyStroke;
  252. // this is a hack. a literal space is impossible to
  253. // insert in a key binding string, but you can write
  254. // SPACE.
  255. switch(c)
  256. {
  257. case ' ':
  258. keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
  259. evt.getModifiers());
  260. break;
  261. case '\t':
  262. keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
  263. evt.getModifiers());
  264. break;
  265. case '\n':
  266. keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
  267. evt.getModifiers());
  268. break;
  269. default:
  270. keyStroke = KeyStroke.getKeyStroke(c);
  271. break;
  272. }
  273. Object o = currentBindings.get(keyStroke);
  274. if(o instanceof Hashtable)
  275. {
  276. currentBindings = (Hashtable)o;
  277. }
  278. else if(o instanceof EditAction)
  279. {
  280. currentBindings = bindings;
  281. invokeAction((EditAction)o);
  282. }
  283. else
  284. {
  285. // otherwise, reset to default map and do user input
  286. currentBindings = bindings;
  287. if(repeat && Character.isDigit(c))
  288. {
  289. repeatCount *= 10;
  290. repeatCount += (c - '0');
  291. view.getStatus().setMessage(null);
  292. }
  293. else
  294. userInput(c);
  295. }
  296. } //}}}
  297. //{{{ parseKeyStroke() method
  298. /**
  299. * Converts a string to a keystroke. The string should be of the
  300. * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
  301. * is any combination of A for Alt, C for Control, S for Shift
  302. * or M for Meta, and <i>shortcut</i> is either a single character,
  303. * or a keycode name from the <code>KeyEvent</code> class, without
  304. * the <code>VK_</code> prefix.
  305. * @param keyStroke A string description of the key stroke
  306. */
  307. public static KeyStroke parseKeyStroke(String keyStroke)
  308. {
  309. if(keyStroke == null)
  310. return null;
  311. int modifiers = 0;
  312. int index = keyStroke.indexOf('+');
  313. if(index != -1)
  314. {
  315. for(int i = 0; i < index; i++)
  316. {
  317. switch(Character.toUpperCase(keyStroke
  318. .charAt(i)))
  319. {
  320. case 'A':
  321. modifiers |= InputEvent.ALT_MASK;
  322. break;
  323. case 'C':
  324. if(OperatingSystem.isMacOS())
  325. modifiers |= InputEvent.META_MASK;
  326. else
  327. modifiers |= InputEvent.CTRL_MASK;
  328. break;
  329. case 'M':
  330. if(OperatingSystem.isMacOS())
  331. modifiers |= InputEvent.CTRL_MASK;
  332. else
  333. modifiers |= InputEvent.META_MASK;
  334. break;
  335. case 'S':
  336. modifiers |= InputEvent.SHIFT_MASK;
  337. break;
  338. }
  339. }
  340. }
  341. String key = keyStroke.substring(index + 1);
  342. if(key.length() == 1)
  343. {
  344. char ch = key.charAt(0);
  345. if(modifiers == 0)
  346. return KeyStroke.getKeyStroke(ch);
  347. else
  348. {
  349. return KeyStroke.getKeyStroke(Character.toUpperCase(ch),
  350. modifiers);
  351. }
  352. }
  353. else if(key.length() == 0)
  354. {
  355. Log.log(Log.ERROR,DefaultInputHandler.class,
  356. "Invalid key stroke: " + keyStroke);
  357. return null;
  358. }
  359. else
  360. {
  361. int ch;
  362. try
  363. {
  364. ch = KeyEvent.class.getField("VK_".concat(key))
  365. .getInt(null);
  366. }
  367. catch(Exception e)
  368. {
  369. Log.log(Log.ERROR,DefaultInputHandler.class,
  370. "Invalid key stroke: "
  371. + keyStroke);
  372. return null;
  373. }
  374. return KeyStroke.getKeyStroke(ch,modifiers);
  375. }
  376. } //}}}
  377. //{{{ Private members
  378. private Hashtable bindings;
  379. private Hashtable currentBindings;
  380. //}}}
  381. }