PageRenderTime 55ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

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

#
Java | 395 lines | 285 code | 16 blank | 94 comment | 48 complexity | 95b89beb431df037f17e6f6762b4cbe6 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. * KeyEventWorkaround.java - Works around bugs in Java event handling
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2000, 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 org.gjt.sp.jedit.Debug;
  26. import org.gjt.sp.jedit.OperatingSystem;
  27. import org.gjt.sp.jedit.input.AbstractInputHandler;
  28. import org.gjt.sp.util.Log;
  29. //}}}
  30. /**
  31. * Various hacks to get keyboard event handling to behave in a consistent manner
  32. * across Java implementations. This type of stuff should not be necessary, but
  33. * Java's keyboard handling is crap, to put it mildly.
  34. *
  35. * @author Slava Pestov
  36. * @version $Id: KeyEventWorkaround.java 17503 2010-03-20 09:47:18Z k_satoda $
  37. */
  38. public class KeyEventWorkaround
  39. {
  40. //{{{ isBindable() method
  41. public static boolean isBindable(int keyCode)
  42. {
  43. switch(keyCode)
  44. {
  45. case KeyEvent.VK_ALT:
  46. case KeyEvent.VK_ALT_GRAPH:
  47. case KeyEvent.VK_CONTROL:
  48. case KeyEvent.VK_SHIFT:
  49. case KeyEvent.VK_META:
  50. case KeyEvent.VK_DEAD_GRAVE:
  51. case KeyEvent.VK_DEAD_ACUTE:
  52. case KeyEvent.VK_DEAD_CIRCUMFLEX:
  53. case KeyEvent.VK_DEAD_TILDE:
  54. case KeyEvent.VK_DEAD_MACRON:
  55. case KeyEvent.VK_DEAD_BREVE:
  56. case KeyEvent.VK_DEAD_ABOVEDOT:
  57. case KeyEvent.VK_DEAD_DIAERESIS:
  58. case KeyEvent.VK_DEAD_ABOVERING:
  59. case KeyEvent.VK_DEAD_DOUBLEACUTE:
  60. case KeyEvent.VK_DEAD_CARON:
  61. case KeyEvent.VK_DEAD_CEDILLA:
  62. case KeyEvent.VK_DEAD_OGONEK:
  63. case KeyEvent.VK_DEAD_IOTA:
  64. case KeyEvent.VK_DEAD_VOICED_SOUND:
  65. case KeyEvent.VK_DEAD_SEMIVOICED_SOUND:
  66. return false;
  67. default:
  68. return true;
  69. }
  70. } //}}}
  71. //{{{ isPrintable() method
  72. /**
  73. * We need to know if a keycode can potentially result in a
  74. * keytyped.
  75. * @since jEdit 4.3pre2
  76. */
  77. public static boolean isPrintable(int keyCode)
  78. {
  79. switch(keyCode)
  80. {
  81. /* case KeyEvent.VK_ENTER:
  82. case KeyEvent.VK_TAB: */
  83. case KeyEvent.VK_SPACE:
  84. case KeyEvent.VK_COMMA:
  85. case KeyEvent.VK_MINUS:
  86. case KeyEvent.VK_PERIOD:
  87. case KeyEvent.VK_SLASH:
  88. case KeyEvent.VK_0:
  89. case KeyEvent.VK_1:
  90. case KeyEvent.VK_2:
  91. case KeyEvent.VK_3:
  92. case KeyEvent.VK_4:
  93. case KeyEvent.VK_5:
  94. case KeyEvent.VK_6:
  95. case KeyEvent.VK_7:
  96. case KeyEvent.VK_8:
  97. case KeyEvent.VK_9:
  98. case KeyEvent.VK_SEMICOLON:
  99. case KeyEvent.VK_EQUALS :
  100. case KeyEvent.VK_A:
  101. case KeyEvent.VK_B:
  102. case KeyEvent.VK_C:
  103. case KeyEvent.VK_D:
  104. case KeyEvent.VK_E:
  105. case KeyEvent.VK_F:
  106. case KeyEvent.VK_G:
  107. case KeyEvent.VK_H:
  108. case KeyEvent.VK_I:
  109. case KeyEvent.VK_J:
  110. case KeyEvent.VK_K:
  111. case KeyEvent.VK_L:
  112. case KeyEvent.VK_M:
  113. case KeyEvent.VK_N:
  114. case KeyEvent.VK_O:
  115. case KeyEvent.VK_P:
  116. case KeyEvent.VK_Q:
  117. case KeyEvent.VK_R:
  118. case KeyEvent.VK_S:
  119. case KeyEvent.VK_T:
  120. case KeyEvent.VK_U:
  121. case KeyEvent.VK_V:
  122. case KeyEvent.VK_W:
  123. case KeyEvent.VK_X:
  124. case KeyEvent.VK_Y:
  125. case KeyEvent.VK_Z:
  126. case KeyEvent.VK_OPEN_BRACKET :
  127. case KeyEvent.VK_BACK_SLASH :
  128. case KeyEvent.VK_CLOSE_BRACKET:
  129. /* case KeyEvent.VK_NUMPAD0 :
  130. case KeyEvent.VK_NUMPAD1 :
  131. case KeyEvent.VK_NUMPAD2 :
  132. case KeyEvent.VK_NUMPAD3 :
  133. case KeyEvent.VK_NUMPAD4 :
  134. case KeyEvent.VK_NUMPAD5 :
  135. case KeyEvent.VK_NUMPAD6 :
  136. case KeyEvent.VK_NUMPAD7 :
  137. case KeyEvent.VK_NUMPAD8 :
  138. case KeyEvent.VK_NUMPAD9 :
  139. case KeyEvent.VK_MULTIPLY:
  140. case KeyEvent.VK_ADD :
  141. case KeyEvent.VK_SEPARATOR:
  142. case KeyEvent.VK_SUBTRACT :
  143. case KeyEvent.VK_DECIMAL :
  144. case KeyEvent.VK_DIVIDE :*/
  145. case KeyEvent.VK_BACK_QUOTE:
  146. case KeyEvent.VK_QUOTE:
  147. case KeyEvent.VK_DEAD_GRAVE:
  148. case KeyEvent.VK_DEAD_ACUTE:
  149. case KeyEvent.VK_DEAD_CIRCUMFLEX:
  150. case KeyEvent.VK_DEAD_TILDE:
  151. case KeyEvent.VK_DEAD_MACRON:
  152. case KeyEvent.VK_DEAD_BREVE:
  153. case KeyEvent.VK_DEAD_ABOVEDOT:
  154. case KeyEvent.VK_DEAD_DIAERESIS:
  155. case KeyEvent.VK_DEAD_ABOVERING:
  156. case KeyEvent.VK_DEAD_DOUBLEACUTE:
  157. case KeyEvent.VK_DEAD_CARON:
  158. case KeyEvent.VK_DEAD_CEDILLA:
  159. case KeyEvent.VK_DEAD_OGONEK:
  160. case KeyEvent.VK_DEAD_IOTA:
  161. case KeyEvent.VK_DEAD_VOICED_SOUND:
  162. case KeyEvent.VK_DEAD_SEMIVOICED_SOUND:
  163. case KeyEvent.VK_AMPERSAND:
  164. case KeyEvent.VK_ASTERISK:
  165. case KeyEvent.VK_QUOTEDBL:
  166. case KeyEvent.VK_LESS:
  167. case KeyEvent.VK_GREATER:
  168. case KeyEvent.VK_BRACELEFT:
  169. case KeyEvent.VK_BRACERIGHT:
  170. case KeyEvent.VK_AT:
  171. case KeyEvent.VK_COLON:
  172. case KeyEvent.VK_CIRCUMFLEX:
  173. case KeyEvent.VK_DOLLAR:
  174. case KeyEvent.VK_EURO_SIGN:
  175. case KeyEvent.VK_EXCLAMATION_MARK:
  176. case KeyEvent.VK_INVERTED_EXCLAMATION_MARK:
  177. case KeyEvent.VK_LEFT_PARENTHESIS:
  178. case KeyEvent.VK_NUMBER_SIGN:
  179. case KeyEvent.VK_PLUS:
  180. case KeyEvent.VK_RIGHT_PARENTHESIS:
  181. case KeyEvent.VK_UNDERSCORE:
  182. return true;
  183. default:
  184. return false;
  185. }
  186. } //}}}
  187. //{{{ isMacControl() method
  188. /**
  189. * Apple sucks.
  190. */
  191. public static boolean isMacControl(KeyEvent evt)
  192. {
  193. return (OperatingSystem.isMacOS() &&
  194. (evt.getModifiers() & InputEvent.CTRL_MASK) != 0
  195. && evt.getKeyChar() <= 0x2B);
  196. } //}}}
  197. //{{{ isNumericKeypad() method
  198. public static boolean isNumericKeypad(int keyCode)
  199. {
  200. switch(keyCode)
  201. {
  202. case KeyEvent.VK_NUMPAD0:
  203. case KeyEvent.VK_NUMPAD1:
  204. case KeyEvent.VK_NUMPAD2:
  205. case KeyEvent.VK_NUMPAD3:
  206. case KeyEvent.VK_NUMPAD4:
  207. case KeyEvent.VK_NUMPAD5:
  208. case KeyEvent.VK_NUMPAD6:
  209. case KeyEvent.VK_NUMPAD7:
  210. case KeyEvent.VK_NUMPAD8:
  211. case KeyEvent.VK_NUMPAD9:
  212. case KeyEvent.VK_MULTIPLY:
  213. case KeyEvent.VK_ADD:
  214. /* case KeyEvent.VK_SEPARATOR: */
  215. case KeyEvent.VK_SUBTRACT:
  216. case KeyEvent.VK_DECIMAL:
  217. case KeyEvent.VK_DIVIDE:
  218. return true;
  219. default:
  220. return false;
  221. }
  222. } //}}}
  223. //{{{ processKeyEvent() method
  224. public static KeyEvent processKeyEvent(KeyEvent evt)
  225. {
  226. int keyCode = evt.getKeyCode();
  227. char ch = evt.getKeyChar();
  228. int modifiers = evt.getModifiers();
  229. switch(evt.getID())
  230. {
  231. //{{{ KEY_PRESSED...
  232. case KeyEvent.KEY_PRESSED:
  233. // get rid of keys we never need to handle
  234. switch(keyCode)
  235. {
  236. case '\0':
  237. return null;
  238. case KeyEvent.VK_ALT:
  239. case KeyEvent.VK_ALT_GRAPH:
  240. case KeyEvent.VK_CONTROL:
  241. case KeyEvent.VK_SHIFT:
  242. case KeyEvent.VK_META:
  243. break;
  244. default:
  245. if(!evt.isMetaDown())
  246. {
  247. if(!evt.isControlDown()
  248. && !evt.isAltDown())
  249. {
  250. if(isPrintable(keyCode))
  251. {
  252. return null;
  253. }
  254. }
  255. }
  256. if(Debug.ALT_KEY_PRESSED_DISABLED)
  257. {
  258. /* we don't handle key pressed A+ */
  259. /* they're too troublesome */
  260. if((modifiers & InputEvent.ALT_MASK) != 0)
  261. return null;
  262. }
  263. if(isNumericKeypad(keyCode))
  264. last = LAST_NUMKEYPAD;
  265. else
  266. last = LAST_NOTHING;
  267. break;
  268. }
  269. break;
  270. //}}}
  271. //{{{ KEY_TYPED...
  272. case KeyEvent.KEY_TYPED:
  273. // need to let \b through so that backspace will work
  274. // in HistoryTextFields
  275. if(!isMacControl(evt)
  276. && (ch < 0x20 || ch == 0x7f || ch == 0xff)
  277. && ch != '\b' && ch != '\t' && ch != '\n')
  278. {
  279. return null;
  280. }
  281. if(Debug.DUMP_KEY_EVENTS)
  282. {
  283. Log.log(Log.DEBUG,"KEWa","Key event (working around): "
  284. + AbstractInputHandler.toString(evt)+": last="+last+".");
  285. }
  286. if(!Debug.ALTERNATIVE_DISPATCHER)
  287. {
  288. if(((modifiers & InputEvent.CTRL_MASK) != 0
  289. ^ (modifiers & InputEvent.ALT_MASK) != 0)
  290. || (modifiers & InputEvent.META_MASK) != 0)
  291. {
  292. return null;
  293. }
  294. }
  295. // if the last key was a numeric keypad key
  296. // and NumLock is off, filter it out
  297. if(last == LAST_NUMKEYPAD)
  298. {
  299. last = LAST_NOTHING;
  300. if((ch >= '0' && ch <= '9') || ch == '.'
  301. || ch == '/' || ch == '*'
  302. || ch == '-' || ch == '+')
  303. {
  304. return null;
  305. }
  306. }
  307. // Windows JDK workaround
  308. else if(last == LAST_ALT)
  309. {
  310. last = LAST_NOTHING;
  311. switch(ch)
  312. {
  313. case 'B':
  314. case 'M':
  315. case 'X':
  316. case 'c':
  317. case '!':
  318. case ',':
  319. case '?':
  320. return null;
  321. }
  322. }
  323. break;
  324. //}}}
  325. //{{{ KEY_RELEASED...
  326. case KeyEvent.KEY_RELEASED:
  327. switch(keyCode)
  328. {
  329. case KeyEvent.VK_ALT:
  330. // we consume this to work around the bug
  331. // where A+TAB window switching activates
  332. // the menu bar on Windows.
  333. // http://bugs.sun.com/view_bug.do?bug_id=6458497
  334. //
  335. // This should be removed if the fix for the
  336. // above problem became widely available, to
  337. // allow the menu bar activation.
  338. evt.consume();
  339. break;
  340. case KeyEvent.VK_ALT_GRAPH:
  341. case KeyEvent.VK_CONTROL:
  342. case KeyEvent.VK_SHIFT:
  343. case KeyEvent.VK_META:
  344. break;
  345. case KeyEvent.VK_LEFT:
  346. case KeyEvent.VK_RIGHT:
  347. case KeyEvent.VK_UP:
  348. case KeyEvent.VK_DOWN:
  349. case KeyEvent.VK_PAGE_UP:
  350. case KeyEvent.VK_PAGE_DOWN:
  351. case KeyEvent.VK_END:
  352. case KeyEvent.VK_HOME:
  353. /* workaround for A+keys producing
  354. * garbage on Windows */
  355. if(modifiers == InputEvent.ALT_MASK)
  356. last = LAST_ALT;
  357. break;
  358. }
  359. break;
  360. //}}}
  361. }
  362. return evt;
  363. } //}}}
  364. //{{{ numericKeypadKey() method
  365. /**
  366. * A workaround for non-working NumLock status in some Java versions.
  367. * @since jEdit 4.0pre8
  368. */
  369. public static void numericKeypadKey()
  370. {
  371. last = LAST_NOTHING;
  372. } //}}}
  373. //{{{ Private members
  374. private static int last;
  375. private static final int LAST_NOTHING = 0;
  376. private static final int LAST_NUMKEYPAD = 1;
  377. private static final int LAST_ALT = 2;
  378. //}}}
  379. }