PageRenderTime 38ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/gui/KeyEventTranslator.java

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