PageRenderTime 52ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/gui/KeyEventTranslator.java

#
Java | 511 lines | 355 code | 39 blank | 117 comment | 85 complexity | 1ee0ca211812e7758851f9d35f24dcf4 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. * KeyEventTranslator.java - Hides some warts of AWT event API
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2003, 2005 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 java.awt.event.*;
  25. import java.util.HashMap;
  26. import java.util.Map;
  27. import org.gjt.sp.jedit.Debug;
  28. import org.gjt.sp.jedit.OperatingSystem;
  29. import org.gjt.sp.util.Log;
  30. import org.gjt.sp.util.StandardUtilities;
  31. //}}}
  32. /**
  33. * In conjunction with the <code>KeyEventWorkaround</code>, hides some
  34. * warts in the AWT key event API.
  35. *
  36. * @author Slava Pestov
  37. * @version $Id: KeyEventTranslator.java 20108 2011-10-18 12:16:38Z evanpw $
  38. */
  39. public class KeyEventTranslator
  40. {
  41. //{{{ addTranslation() method
  42. /**
  43. * Adds a keyboard translation.
  44. * @param key1 Translate this key
  45. * @param key2 Into this key
  46. * @since jEdit 4.2pre3
  47. */
  48. public static void addTranslation(Key key1, Key key2)
  49. {
  50. transMap.put(key1,key2);
  51. } //}}}
  52. //{{{ translateKeyEvent() method
  53. protected static KeyEvent lastKeyPressEvent;
  54. protected static boolean lastKeyPressAccepted;
  55. /**
  56. * Pass this an event from {@link
  57. * KeyEventWorkaround#processKeyEvent(java.awt.event.KeyEvent)}.
  58. * @param evt the KeyEvent to translate
  59. * @since jEdit 4.2pre3
  60. */
  61. public static Key translateKeyEvent(KeyEvent evt)
  62. {
  63. int modifiers = evt.getModifiers();
  64. Key returnValue;
  65. switch(evt.getID())
  66. {
  67. case KeyEvent.KEY_PRESSED:
  68. int keyCode = evt.getKeyCode();
  69. if((keyCode >= KeyEvent.VK_0
  70. && keyCode <= KeyEvent.VK_9)
  71. || (keyCode >= KeyEvent.VK_A
  72. && keyCode <= KeyEvent.VK_Z))
  73. {
  74. if(Debug.ALTERNATIVE_DISPATCHER)
  75. return null;
  76. else
  77. {
  78. returnValue = new Key(
  79. modifiersToString(modifiers),
  80. '\0',Character.toLowerCase(
  81. (char)keyCode));
  82. }
  83. }
  84. else
  85. {
  86. if(keyCode == KeyEvent.VK_TAB)
  87. {
  88. evt.consume();
  89. returnValue = new Key(
  90. modifiersToString(modifiers),
  91. keyCode,'\0');
  92. }
  93. else if(keyCode == KeyEvent.VK_SPACE)
  94. {
  95. // for SPACE or S+SPACE we pass the
  96. // key typed since international
  97. // keyboards sometimes produce a
  98. // KEY_PRESSED SPACE but not a
  99. // KEY_TYPED SPACE, eg if you have to
  100. // do a "<space> to insert ".
  101. if((modifiers & ~InputEvent.SHIFT_MASK) == 0)
  102. returnValue = null;
  103. else if (Debug.ALTERNATIVE_DISPATCHER && (modifiers & ~InputEvent.META_MASK) == 0)
  104. returnValue = null;
  105. else
  106. {
  107. returnValue = new Key(
  108. modifiersToString(modifiers),
  109. 0,' ');
  110. }
  111. }
  112. else
  113. {
  114. returnValue = new Key(
  115. modifiersToString(modifiers),
  116. keyCode,'\0');
  117. }
  118. }
  119. break;
  120. case KeyEvent.KEY_TYPED:
  121. char ch = evt.getKeyChar();
  122. if(KeyEventWorkaround.isMacControl(evt))
  123. ch |= 0x60;
  124. switch(ch)
  125. {
  126. case '\n':
  127. case '\t':
  128. case '\b':
  129. return null;
  130. case ' ':
  131. if (Debug.ALTERNATIVE_DISPATCHER && (modifiers & ~InputEvent.META_MASK) == 0)
  132. returnValue = new Key(
  133. modifiersToString(modifiers),
  134. 0,' ');
  135. else if((modifiers & ~InputEvent.SHIFT_MASK) != 0)
  136. return null;
  137. }
  138. int ignoreMods;
  139. if(Debug.ALT_KEY_PRESSED_DISABLED)
  140. {
  141. /* on MacOS, A+ can be user input */
  142. ignoreMods = InputEvent.SHIFT_MASK
  143. | InputEvent.ALT_GRAPH_MASK
  144. | InputEvent.ALT_MASK;
  145. }
  146. else
  147. {
  148. /* on MacOS, A+ can be user input */
  149. ignoreMods = InputEvent.SHIFT_MASK
  150. | InputEvent.ALT_GRAPH_MASK;
  151. }
  152. if((modifiers & InputEvent.ALT_GRAPH_MASK) == 0
  153. && (modifiers & ~ignoreMods) != 0)
  154. {
  155. if(Debug.ALTERNATIVE_DISPATCHER)
  156. {
  157. returnValue = new Key(
  158. modifiersToString(modifiers),
  159. 0,ch);
  160. }
  161. else
  162. return null;
  163. }
  164. else
  165. {
  166. if(ch == ' ')
  167. {
  168. returnValue = new Key(
  169. modifiersToString(modifiers),
  170. 0,ch);
  171. }
  172. else
  173. returnValue = new Key(null,0,ch);
  174. }
  175. break;
  176. default:
  177. return null;
  178. }
  179. /* I guess translated events do not have the 'evt' field set
  180. so consuming won't work. I don't think this is a problem as
  181. nothing uses translation anyway */
  182. Key trans = transMap.get(returnValue);
  183. if(trans == null)
  184. return returnValue;
  185. else
  186. return trans;
  187. } //}}}
  188. //{{{ parseKey() method
  189. /**
  190. * Converts a string to a keystroke. The string should be of the
  191. * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
  192. * is any combination of A for Alt, C for Control, S for Shift
  193. * or M for Meta, and <i>shortcut</i> is either a single character,
  194. * or a keycode name from the <code>KeyEvent</code> class, without
  195. * the <code>VK_</code> prefix.
  196. * @param keyStroke A string description of the key stroke
  197. * @since jEdit 4.2pre3
  198. */
  199. public static Key parseKey(String keyStroke)
  200. {
  201. if(keyStroke == null)
  202. return null;
  203. int modifiers = 0;
  204. String key;
  205. int endOfModifiers = keyStroke.indexOf('+');
  206. if(endOfModifiers <= 0) // not found or found at first
  207. {
  208. key = keyStroke;
  209. }
  210. else
  211. {
  212. for(int i = 0; i < endOfModifiers; i++)
  213. {
  214. switch(Character.toUpperCase(keyStroke
  215. .charAt(i)))
  216. {
  217. case 'A':
  218. modifiers |= a;
  219. break;
  220. case 'C':
  221. modifiers |= c;
  222. break;
  223. case 'M':
  224. modifiers |= m;
  225. break;
  226. case 'S':
  227. modifiers |= s;
  228. break;
  229. }
  230. }
  231. key = keyStroke.substring(endOfModifiers + 1);
  232. }
  233. if(key.length() == 1)
  234. {
  235. return new Key(modifiersToString(modifiers),0,key.charAt(0));
  236. }
  237. else if(key.length() == 0)
  238. {
  239. Log.log(Log.ERROR,KeyEventTranslator.class,
  240. "Invalid key stroke: " + keyStroke);
  241. return null;
  242. }
  243. else if(key.equals("SPACE"))
  244. {
  245. return new Key(modifiersToString(modifiers),0,' ');
  246. }
  247. else
  248. {
  249. int ch;
  250. try
  251. {
  252. ch = KeyEvent.class.getField("VK_".concat(key))
  253. .getInt(null);
  254. }
  255. catch(Exception e)
  256. {
  257. Log.log(Log.ERROR,KeyEventTranslator.class,
  258. "Invalid key stroke: "
  259. + keyStroke);
  260. return null;
  261. }
  262. return new Key(modifiersToString(modifiers),ch,'\0');
  263. }
  264. } //}}}
  265. //{{{ setModifierMapping() method
  266. /**
  267. * Changes the mapping between symbolic modifier key names
  268. * (<code>C</code>, <code>A</code>, <code>M</code>, <code>S</code>) and
  269. * Java modifier flags.
  270. *
  271. * You can map more than one Java modifier to a symobolic modifier, for
  272. * example :
  273. * <p><code><pre>
  274. * setModifierMapping(
  275. * InputEvent.CTRL_MASK,
  276. * InputEvent.ALT_MASK | InputEvent.META_MASK,
  277. * 0,
  278. * InputEvent.SHIFT_MASK);
  279. *<pre></code></p>
  280. *
  281. * You cannot map a Java modifer to more than one symbolic modifier.
  282. *
  283. * @param c The modifier(s) to map the <code>C</code> modifier to
  284. * @param a The modifier(s) to map the <code>A</code> modifier to
  285. * @param m The modifier(s) to map the <code>M</code> modifier to
  286. * @param s The modifier(s) to map the <code>S</code> modifier to
  287. *
  288. * @since jEdit 4.2pre3
  289. */
  290. public static void setModifierMapping(int c, int a, int m, int s)
  291. {
  292. int duplicateMapping =
  293. (c & a) | (c & m) | (c & s) | (a & m) | (a & s) | (m & s);
  294. if((duplicateMapping & InputEvent.CTRL_MASK) != 0)
  295. {
  296. throw new IllegalArgumentException(
  297. "CTRL is mapped to more than one modifier");
  298. }
  299. if((duplicateMapping & InputEvent.ALT_MASK) != 0)
  300. {
  301. throw new IllegalArgumentException(
  302. "ALT is mapped to more than one modifier");
  303. }
  304. if((duplicateMapping & InputEvent.META_MASK) != 0)
  305. {
  306. throw new IllegalArgumentException(
  307. "META is mapped to more than one modifier");
  308. }
  309. if((duplicateMapping & InputEvent.SHIFT_MASK) != 0)
  310. {
  311. throw new IllegalArgumentException(
  312. "SHIFT is mapped to more than one modifier");
  313. }
  314. KeyEventTranslator.c = c;
  315. KeyEventTranslator.a = a;
  316. KeyEventTranslator.m = m;
  317. KeyEventTranslator.s = s;
  318. } //}}}
  319. //{{{ getSymbolicModifierName() method
  320. /**
  321. * Returns a the symbolic modifier name for the specified Java modifier
  322. * flag.
  323. *
  324. * @param mod A modifier constant from <code>InputEvent</code>
  325. *
  326. * @since jEdit 4.2pre3
  327. */
  328. public static char getSymbolicModifierName(int mod)
  329. {
  330. if((mod & c) != 0)
  331. return 'C';
  332. else if((mod & a) != 0)
  333. return 'A';
  334. else if((mod & m) != 0)
  335. return 'M';
  336. else if((mod & s) != 0)
  337. return 'S';
  338. else
  339. return '\0';
  340. } //}}}
  341. //{{{ modifiersToString() method
  342. private static final int[] MODS = {
  343. InputEvent.CTRL_MASK,
  344. InputEvent.ALT_MASK,
  345. InputEvent.META_MASK,
  346. InputEvent.SHIFT_MASK
  347. };
  348. public static String modifiersToString(int mods)
  349. {
  350. StringBuilder buf = null;
  351. for(int i = 0; i < MODS.length; i++)
  352. {
  353. if((mods & MODS[i]) != 0)
  354. buf = lazyAppend(buf,getSymbolicModifierName(MODS[i]));
  355. }
  356. if(buf == null)
  357. return null;
  358. else
  359. return buf.toString();
  360. } //}}}
  361. //{{{ getModifierString() method
  362. /**
  363. * Returns a string containing symbolic modifier names set in the
  364. * specified event.
  365. *
  366. * @param evt The event
  367. *
  368. * @since jEdit 4.2pre3
  369. */
  370. public static String getModifierString(InputEvent evt)
  371. {
  372. StringBuilder buf = new StringBuilder();
  373. if(evt.isControlDown())
  374. buf.append(getSymbolicModifierName(InputEvent.CTRL_MASK));
  375. if(evt.isAltDown())
  376. buf.append(getSymbolicModifierName(InputEvent.ALT_MASK));
  377. if(evt.isMetaDown())
  378. buf.append(getSymbolicModifierName(InputEvent.META_MASK));
  379. if(evt.isShiftDown())
  380. buf.append(getSymbolicModifierName(InputEvent.SHIFT_MASK));
  381. return buf.length() == 0 ? null : buf.toString();
  382. } //}}}
  383. static int c, a, m, s;
  384. //{{{ Private members
  385. /** This map is a pool of Key. */
  386. private static final Map<Key, Key> transMap = new HashMap<Key, Key>();
  387. private static StringBuilder lazyAppend(StringBuilder buf, char ch)
  388. {
  389. if(buf == null)
  390. buf = new StringBuilder();
  391. if(buf.indexOf(String.valueOf(ch)) == -1)
  392. buf.append(ch);
  393. return buf;
  394. }
  395. static
  396. {
  397. if(OperatingSystem.isMacOS())
  398. {
  399. setModifierMapping(
  400. InputEvent.META_MASK, /* == C+ */
  401. InputEvent.CTRL_MASK, /* == A+ */
  402. /* M+ discarded by key event workaround! */
  403. InputEvent.ALT_MASK, /* == M+ */
  404. InputEvent.SHIFT_MASK /* == S+ */);
  405. }
  406. else
  407. {
  408. setModifierMapping(
  409. InputEvent.CTRL_MASK,
  410. InputEvent.ALT_MASK,
  411. InputEvent.META_MASK,
  412. InputEvent.SHIFT_MASK);
  413. }
  414. } //}}}
  415. //{{{ Key class
  416. public static class Key
  417. {
  418. public final String modifiers;
  419. public final int key;
  420. public final char input;
  421. private final int hashCode;
  422. /**
  423. Wether this Key event applies to all jEdit windows (and not only a specific jEdit GUI component).
  424. */
  425. protected boolean isFromGlobalContext;
  426. public Key(String modifiers, int key, char input)
  427. {
  428. this.modifiers = modifiers;
  429. this.key = key;
  430. this.input = input;
  431. hashCode = key + input;
  432. }
  433. @Override
  434. public int hashCode()
  435. {
  436. return hashCode;
  437. }
  438. @Override
  439. public boolean equals(Object o)
  440. {
  441. if(o instanceof Key)
  442. {
  443. Key k = (Key)o;
  444. if(StandardUtilities.objectsEqual(modifiers,
  445. k.modifiers) && key == k.key
  446. && input == k.input)
  447. {
  448. return true;
  449. }
  450. }
  451. return false;
  452. }
  453. @Override
  454. public String toString()
  455. {
  456. return (modifiers == null ? "" : modifiers)
  457. + '<'
  458. + Integer.toString(key,16)
  459. + ','
  460. + Integer.toString(input,16)
  461. + '>';
  462. }
  463. public void setIsFromGlobalContext(boolean to)
  464. {
  465. isFromGlobalContext = to;
  466. }
  467. public boolean isFromGlobalContext()
  468. {
  469. return isFromGlobalContext;
  470. }
  471. } //}}}
  472. }