PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

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

#
Java | 515 lines | 334 code | 39 blank | 142 comment | 83 complexity | 013cf91481a95d16fd6d27e27e445c50 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 4305 2002-08-13 18:59:48Z 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() || evt.isMetaDown()))
  166. {
  167. // if modifier active, handle all keys, otherwise
  168. // only some
  169. if((keyCode >= KeyEvent.VK_A && keyCode <= KeyEvent.VK_Z)
  170. || (keyCode >= KeyEvent.VK_0 && keyCode <= KeyEvent.VK_9))
  171. {
  172. return;
  173. }
  174. else if(keyCode == KeyEvent.VK_SPACE)
  175. {
  176. return;
  177. }
  178. else if(readNextChar != null)
  179. {
  180. if(keyCode == KeyEvent.VK_ESCAPE)
  181. {
  182. readNextChar = null;
  183. view.getStatus().setMessage(null);
  184. }
  185. else if(!evt.isActionKey()
  186. && keyCode != KeyEvent.VK_TAB
  187. && keyCode != KeyEvent.VK_ENTER)
  188. {
  189. return;
  190. }
  191. }
  192. else
  193. {
  194. // ok even with no modifiers
  195. }
  196. }
  197. KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
  198. modifiers);
  199. Object o = currentBindings.get(keyStroke);
  200. if(o == null)
  201. {
  202. // Don't beep if the user presses some
  203. // key we don't know about unless a
  204. // prefix is active. Otherwise it will
  205. // beep when caps lock is pressed, etc.
  206. if(currentBindings != bindings)
  207. {
  208. Toolkit.getDefaultToolkit().beep();
  209. // F10 should be passed on, but C+e F10
  210. // shouldn't
  211. repeatCount = 0;
  212. repeat = false;
  213. evt.consume();
  214. currentBindings = bindings;
  215. }
  216. else if(modifiers == 0 && (keyCode == KeyEvent.VK_ENTER
  217. || keyCode == KeyEvent.VK_TAB))
  218. {
  219. userInput((char)keyCode);
  220. evt.consume();
  221. }
  222. }
  223. if(readNextChar != null)
  224. {
  225. readNextChar = null;
  226. view.getStatus().setMessage(null);
  227. }
  228. if(o instanceof EditAction)
  229. {
  230. currentBindings = bindings;
  231. invokeAction((EditAction)o);
  232. evt.consume();
  233. }
  234. else if(o instanceof Hashtable)
  235. {
  236. currentBindings = (Hashtable)o;
  237. evt.consume();
  238. }
  239. if(o == null)
  240. {
  241. switch(evt.getKeyCode())
  242. {
  243. case KeyEvent.VK_NUMPAD0: case KeyEvent.VK_NUMPAD1:
  244. case KeyEvent.VK_NUMPAD2: case KeyEvent.VK_NUMPAD3:
  245. case KeyEvent.VK_NUMPAD4: case KeyEvent.VK_NUMPAD5:
  246. case KeyEvent.VK_NUMPAD6: case KeyEvent.VK_NUMPAD7:
  247. case KeyEvent.VK_NUMPAD8: case KeyEvent.VK_NUMPAD9:
  248. case KeyEvent.VK_MULTIPLY: case KeyEvent.VK_ADD:
  249. /* case KeyEvent.VK_SEPARATOR: */ case KeyEvent.VK_SUBTRACT:
  250. case KeyEvent.VK_DECIMAL: case KeyEvent.VK_DIVIDE:
  251. KeyEventWorkaround.numericKeypadKey();
  252. break;
  253. }
  254. }
  255. } //}}}
  256. //{{{ keyTyped() method
  257. /**
  258. * Handle a key typed event. This inserts the key into the text area.
  259. */
  260. public void keyTyped(KeyEvent evt)
  261. {
  262. char c = evt.getKeyChar();
  263. // ignore
  264. if(c == '\b')
  265. return;
  266. KeyStroke keyStroke;
  267. // this is a hack. a literal space is impossible to
  268. // insert in a key binding string, but you can write
  269. // SPACE.
  270. switch(c)
  271. {
  272. case ' ':
  273. keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
  274. evt.getModifiers());
  275. break;
  276. case '\t':
  277. keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
  278. evt.getModifiers());
  279. break;
  280. case '\n':
  281. keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
  282. evt.getModifiers());
  283. break;
  284. default:
  285. keyStroke = KeyStroke.getKeyStroke(c);
  286. break;
  287. }
  288. Object o = currentBindings.get(keyStroke);
  289. if(o instanceof Hashtable)
  290. {
  291. currentBindings = (Hashtable)o;
  292. }
  293. else if(o instanceof EditAction)
  294. {
  295. currentBindings = bindings;
  296. invokeAction((EditAction)o);
  297. }
  298. else
  299. {
  300. // otherwise, reset to default map and do user input
  301. currentBindings = bindings;
  302. if(repeat && Character.isDigit(c))
  303. {
  304. repeatCount *= 10;
  305. repeatCount += (c - '0');
  306. view.getStatus().setMessage(null);
  307. }
  308. else
  309. userInput(c);
  310. }
  311. } //}}}
  312. //{{{ setModifierMapping() method
  313. /**
  314. * Changes the mapping between symbolic modifier key names
  315. * (<code>C</code>, <code>A</code>, <code>M</code>, <code>S</code>) and
  316. * Java modifier flags.
  317. *
  318. * @param c The modifier to map the <code>C</code> modifier to
  319. * @param a The modifier to map the <code>A</code> modifier to
  320. * @param m The modifier to map the <code>M</code> modifier to
  321. * @param s The modifier to map the <code>S</code> modifier to
  322. *
  323. * @since jEdit 4.1pre3
  324. */
  325. public static void setModifierMapping(int c, int a, int m, int s)
  326. {
  327. DefaultInputHandler.c = c;
  328. DefaultInputHandler.a = a;
  329. DefaultInputHandler.m = m;
  330. DefaultInputHandler.s = s;
  331. } //}}}
  332. //{{{ getSymbolicModifierName() method
  333. /**
  334. * Returns a the symbolic modifier name for the specified Java modifier
  335. * flag.
  336. *
  337. * @param mod A modifier constant from <code>InputEvent</code>
  338. *
  339. * @since jEdit 4.1pre3
  340. */
  341. public static char getSymbolicModifierName(int mod)
  342. {
  343. // this relies on the fact that if C is mapped to M, then
  344. // M will be mapped to C.
  345. if(mod == c)
  346. return 'C';
  347. else if(mod == a)
  348. return 'A';
  349. else if(mod == m)
  350. return 'M';
  351. else if(mod == s)
  352. return 'S';
  353. else
  354. return '\0';
  355. } //}}}
  356. //{{{ getModifierString() method
  357. /**
  358. * Returns a string containing symbolic modifier names set in the
  359. * specified event.
  360. *
  361. * @param evt The event
  362. *
  363. * @since jEdit 4.1pre3
  364. */
  365. public static String getModifierString(InputEvent evt)
  366. {
  367. StringBuffer buf = new StringBuffer();
  368. if(evt.isControlDown())
  369. buf.append(getSymbolicModifierName(InputEvent.CTRL_MASK));
  370. if(evt.isAltDown())
  371. buf.append(getSymbolicModifierName(InputEvent.ALT_MASK));
  372. if(evt.isMetaDown())
  373. buf.append(getSymbolicModifierName(InputEvent.META_MASK));
  374. if(evt.isShiftDown())
  375. buf.append(getSymbolicModifierName(InputEvent.SHIFT_MASK));
  376. return buf.toString();
  377. } //}}}
  378. //{{{ parseKeyStroke() method
  379. /**
  380. * Converts a string to a keystroke. The string should be of the
  381. * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
  382. * is any combination of A for Alt, C for Control, S for Shift
  383. * or M for Meta, and <i>shortcut</i> is either a single character,
  384. * or a keycode name from the <code>KeyEvent</code> class, without
  385. * the <code>VK_</code> prefix.
  386. * @param keyStroke A string description of the key stroke
  387. */
  388. public static KeyStroke parseKeyStroke(String keyStroke)
  389. {
  390. if(keyStroke == null)
  391. return null;
  392. int modifiers = 0;
  393. int index = keyStroke.indexOf('+');
  394. if(index != -1)
  395. {
  396. for(int i = 0; i < index; i++)
  397. {
  398. switch(Character.toUpperCase(keyStroke
  399. .charAt(i)))
  400. {
  401. case 'A':
  402. modifiers |= a;
  403. break;
  404. case 'C':
  405. modifiers |= c;
  406. break;
  407. case 'M':
  408. modifiers |= m;
  409. break;
  410. case 'S':
  411. modifiers |= s;
  412. break;
  413. }
  414. }
  415. }
  416. String key = keyStroke.substring(index + 1);
  417. if(key.length() == 1)
  418. {
  419. char ch = key.charAt(0);
  420. if(modifiers == 0)
  421. return KeyStroke.getKeyStroke(ch);
  422. else
  423. {
  424. return KeyStroke.getKeyStroke(Character.toUpperCase(ch),
  425. modifiers);
  426. }
  427. }
  428. else if(key.length() == 0)
  429. {
  430. Log.log(Log.ERROR,DefaultInputHandler.class,
  431. "Invalid key stroke: " + keyStroke);
  432. return null;
  433. }
  434. else
  435. {
  436. int ch;
  437. try
  438. {
  439. ch = KeyEvent.class.getField("VK_".concat(key))
  440. .getInt(null);
  441. }
  442. catch(Exception e)
  443. {
  444. Log.log(Log.ERROR,DefaultInputHandler.class,
  445. "Invalid key stroke: "
  446. + keyStroke);
  447. return null;
  448. }
  449. return KeyStroke.getKeyStroke(ch,modifiers);
  450. }
  451. } //}}}
  452. //{{{ Private members
  453. static
  454. {
  455. if(OperatingSystem.isMacOS())
  456. {
  457. setModifierMapping(
  458. InputEvent.META_MASK,
  459. InputEvent.ALT_MASK,
  460. InputEvent.CTRL_MASK,
  461. InputEvent.SHIFT_MASK);
  462. }
  463. else
  464. {
  465. setModifierMapping(
  466. InputEvent.CTRL_MASK,
  467. InputEvent.ALT_MASK,
  468. InputEvent.META_MASK,
  469. InputEvent.SHIFT_MASK);
  470. }
  471. }
  472. private static int c, a, m, s;
  473. private Hashtable bindings;
  474. private Hashtable currentBindings;
  475. //}}}
  476. }