PageRenderTime 87ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/gui/GrabKeyDialog.java

#
Java | 590 lines | 393 code | 77 blank | 120 comment | 102 complexity | 75b92ec6b0bd373597b41c325b5293a4 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. * GrabKeyDialog.java - Grabs keys from the keyboard
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2001, 2002 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.border.*;
  25. import javax.swing.*;
  26. import java.awt.event.*;
  27. import java.awt.*;
  28. import java.lang.reflect.Field;
  29. import java.util.*;
  30. import org.gjt.sp.jedit.*;
  31. import org.gjt.sp.util.Log;
  32. //}}}
  33. /**
  34. * A dialog for getting shortcut keys.
  35. */
  36. public class GrabKeyDialog extends JDialog
  37. {
  38. //{{{ toString() method
  39. public static String toString(KeyEvent evt)
  40. {
  41. String id;
  42. switch(evt.getID())
  43. {
  44. case KeyEvent.KEY_PRESSED:
  45. id = "KEY_PRESSED";
  46. break;
  47. case KeyEvent.KEY_RELEASED:
  48. id = "KEY_RELEASED";
  49. break;
  50. case KeyEvent.KEY_TYPED:
  51. id = "KEY_TYPED";
  52. break;
  53. default:
  54. id = "unknown type";
  55. break;
  56. }
  57. return id + ",keyCode=0x"
  58. + Integer.toString(evt.getKeyCode(),16)
  59. + ",keyChar=0x"
  60. + Integer.toString(evt.getKeyChar(),16)
  61. + ",modifiers=0x"
  62. + Integer.toString(evt.getModifiers(),16);
  63. } //}}}
  64. //{{{ GrabKeyDialog constructor
  65. /**
  66. * Create and show a new modal dialog.
  67. *
  68. * @param parent center dialog on this component.
  69. * @param binding the action/macro that should get a binding.
  70. * @param allBindings all other key bindings.
  71. * @param debugBuffer debug info will be dumped to this buffer
  72. * (may be null)
  73. * @since jEdit 4.1pre7
  74. */
  75. public GrabKeyDialog(Dialog parent, KeyBinding binding,
  76. Vector allBindings, Buffer debugBuffer)
  77. {
  78. super(parent,jEdit.getProperty("grab-key.title"),true);
  79. init(binding,allBindings,debugBuffer);
  80. } //}}}
  81. //{{{ GrabKeyDialog constructor
  82. /**
  83. * Create and show a new modal dialog.
  84. *
  85. * @param parent center dialog on this component.
  86. * @param binding the action/macro that should get a binding.
  87. * @param allBindings all other key bindings.
  88. * @param debugBuffer debug info will be dumped to this buffer
  89. * (may be null)
  90. * @since jEdit 4.1pre7
  91. */
  92. public GrabKeyDialog(Frame parent, KeyBinding binding,
  93. Vector allBindings, Buffer debugBuffer)
  94. {
  95. super(parent,jEdit.getProperty("grab-key.title"),true);
  96. init(binding,allBindings,debugBuffer);
  97. } //}}}
  98. //{{{ getShortcut() method
  99. /**
  100. * Returns the shortcut, or null if the current shortcut should be
  101. * removed or the dialog either has been cancelled. Use isOK()
  102. * to determine if the latter is true.
  103. */
  104. public String getShortcut()
  105. {
  106. if(isOK)
  107. return shortcut.getText();
  108. else
  109. return null;
  110. } //}}}
  111. //{{{ isOK() method
  112. /**
  113. * Returns true, if the dialog has not been cancelled.
  114. * @since jEdit 3.2pre9
  115. */
  116. public boolean isOK()
  117. {
  118. return isOK;
  119. } //}}}
  120. //{{{ isManagingFocus() method
  121. /**
  122. * Returns if this component can be traversed by pressing the
  123. * Tab key. This returns false.
  124. */
  125. public boolean isManagingFocus()
  126. {
  127. return false;
  128. } //}}}
  129. //{{{ getFocusTraversalKeysEnabled() method
  130. /**
  131. * Makes the tab key work in Java 1.4.
  132. * @since jEdit 3.2pre4
  133. */
  134. public boolean getFocusTraversalKeysEnabled()
  135. {
  136. return false;
  137. } //}}}
  138. //{{{ processKeyEvent() method
  139. protected void processKeyEvent(KeyEvent evt)
  140. {
  141. shortcut.processKeyEvent(evt);
  142. } //}}}
  143. //{{{ Private members
  144. //{{{ Instance variables
  145. private InputPane shortcut; // this is a bad hack
  146. private JLabel assignedTo;
  147. private JButton ok;
  148. private JButton remove;
  149. private JButton cancel;
  150. private JButton clear;
  151. private boolean isOK;
  152. private KeyBinding binding;
  153. private Vector allBindings;
  154. private Buffer debugBuffer;
  155. //}}}
  156. //{{{ init() method
  157. private void init(KeyBinding binding, Vector allBindings, Buffer debugBuffer)
  158. {
  159. this.binding = binding;
  160. this.allBindings = allBindings;
  161. this.debugBuffer = debugBuffer;
  162. enableEvents(AWTEvent.KEY_EVENT_MASK);
  163. // create a panel with a BoxLayout. Can't use Box here
  164. // because Box doesn't have setBorder().
  165. JPanel content = new JPanel(new GridLayout(0,1,0,6))
  166. {
  167. /**
  168. * Returns if this component can be traversed by pressing the
  169. * Tab key. This returns false.
  170. */
  171. public boolean isManagingFocus()
  172. {
  173. return false;
  174. }
  175. /**
  176. * Makes the tab key work in Java 1.4.
  177. * @since jEdit 3.2pre4
  178. */
  179. public boolean getFocusTraversalKeysEnabled()
  180. {
  181. return false;
  182. }
  183. };
  184. content.setBorder(new EmptyBorder(12,12,12,12));
  185. setContentPane(content);
  186. JLabel label = new JLabel(
  187. debugBuffer == null ? jEdit.getProperty(
  188. "grab-key.caption",new String[] { binding.label })
  189. : jEdit.getProperty("grab-key.keyboard-test"));
  190. Box input = Box.createHorizontalBox();
  191. shortcut = new InputPane();
  192. Dimension size = shortcut.getPreferredSize();
  193. size.width = Integer.MAX_VALUE;
  194. shortcut.setMaximumSize(size);
  195. input.add(shortcut);
  196. input.add(Box.createHorizontalStrut(12));
  197. clear = new JButton(jEdit.getProperty("grab-key.clear"));
  198. clear.addActionListener(new ActionHandler());
  199. input.add(clear);
  200. assignedTo = new JLabel();
  201. if(debugBuffer == null)
  202. updateAssignedTo(null);
  203. Box buttons = Box.createHorizontalBox();
  204. buttons.add(Box.createGlue());
  205. if(debugBuffer == null)
  206. {
  207. ok = new JButton(jEdit.getProperty("common.ok"));
  208. ok.addActionListener(new ActionHandler());
  209. buttons.add(ok);
  210. buttons.add(Box.createHorizontalStrut(12));
  211. if(binding.isAssigned()) {
  212. // show "remove" button
  213. remove = new JButton(jEdit.getProperty("grab-key.remove"));
  214. remove.addActionListener(new ActionHandler());
  215. buttons.add(remove);
  216. buttons.add(Box.createHorizontalStrut(12));
  217. }
  218. }
  219. cancel = new JButton(jEdit.getProperty("common.cancel"));
  220. cancel.addActionListener(new ActionHandler());
  221. buttons.add(cancel);
  222. buttons.add(Box.createGlue());
  223. content.add(label);
  224. content.add(input);
  225. if(debugBuffer == null)
  226. content.add(assignedTo);
  227. content.add(buttons);
  228. setDefaultCloseOperation(DISPOSE_ON_CLOSE);
  229. pack();
  230. setLocationRelativeTo(getParent());
  231. setResizable(false);
  232. setVisible(true);
  233. } //}}}
  234. //{{{ getSymbolicName() method
  235. public static String getSymbolicName(int keyCode)
  236. {
  237. if (Debug.DUMP_KEY_EVENTS) {
  238. Log.log(Log.DEBUG,GrabKeyDialog.class,"getSymbolicName("+keyCode+").");
  239. }
  240. if(keyCode == KeyEvent.VK_UNDEFINED)
  241. return null;
  242. /* else if(keyCode == KeyEvent.VK_OPEN_BRACKET)
  243. return "[";
  244. else if(keyCode == KeyEvent.VK_CLOSE_BRACKET)
  245. return "]"; */
  246. if(keyCode >= KeyEvent.VK_A && keyCode <= KeyEvent.VK_Z)
  247. return String.valueOf(Character.toLowerCase((char)keyCode));
  248. try
  249. {
  250. Field[] fields = KeyEvent.class.getFields();
  251. for(int i = 0; i < fields.length; i++)
  252. {
  253. Field field = fields[i];
  254. String name = field.getName();
  255. if(name.startsWith("VK_")
  256. && field.getInt(null) == keyCode)
  257. {
  258. return name.substring(3);
  259. }
  260. }
  261. }
  262. catch(Exception e)
  263. {
  264. Log.log(Log.ERROR,GrabKeyDialog.class,e);
  265. }
  266. return null;
  267. } //}}}
  268. //{{{ updateAssignedTo() method
  269. private void updateAssignedTo(String shortcut)
  270. {
  271. String text = jEdit.getProperty("grab-key.assigned-to.none");
  272. KeyBinding kb = getKeyBinding(shortcut);
  273. if(kb != null)
  274. if(kb.isPrefix)
  275. text = jEdit.getProperty(
  276. "grab-key.assigned-to.prefix",
  277. new String[] { shortcut });
  278. else
  279. text = kb.label;
  280. if(ok != null)
  281. ok.setEnabled(kb == null || !kb.isPrefix);
  282. assignedTo.setText(
  283. jEdit.getProperty("grab-key.assigned-to",
  284. new String[] { text }));
  285. } //}}}
  286. //{{{ getKeyBinding() method
  287. private KeyBinding getKeyBinding(String shortcut)
  288. {
  289. if(shortcut == null || shortcut.length() == 0)
  290. return null;
  291. String spacedShortcut = shortcut + " ";
  292. Enumeration e = allBindings.elements();
  293. while(e.hasMoreElements())
  294. {
  295. KeyBinding kb = (KeyBinding)e.nextElement();
  296. if(!kb.isAssigned())
  297. continue;
  298. String spacedKbShortcut = kb.shortcut + " ";
  299. // eg, trying to bind C+n C+p if C+n already bound
  300. if(spacedShortcut.startsWith(spacedKbShortcut))
  301. return kb;
  302. // eg, trying to bind C+e if C+e is a prefix
  303. if(spacedKbShortcut.startsWith(spacedShortcut))
  304. {
  305. // create a temporary (synthetic) prefix
  306. // KeyBinding, that won't be saved
  307. return new KeyBinding(kb.name,kb.label,
  308. shortcut,true);
  309. }
  310. }
  311. return null;
  312. } //}}}
  313. //}}}
  314. //{{{ KeyBinding class
  315. /**
  316. * A jEdit action or macro with its two possible shortcuts.
  317. * @since jEdit 3.2pre8
  318. */
  319. public static class KeyBinding
  320. {
  321. public KeyBinding(String name, String label,
  322. String shortcut, boolean isPrefix)
  323. {
  324. this.name = name;
  325. this.label = label;
  326. this.shortcut = shortcut;
  327. this.isPrefix = isPrefix;
  328. }
  329. public String name;
  330. public String label;
  331. public String shortcut;
  332. public boolean isPrefix;
  333. public boolean isAssigned()
  334. {
  335. return shortcut != null && shortcut.length() > 0;
  336. }
  337. } //}}}
  338. //{{{ InputPane class
  339. class InputPane extends JTextField
  340. {
  341. //{{{ getFocusTraversalKeysEnabled() method
  342. /**
  343. * Makes the tab key work in Java 1.4.
  344. * @since jEdit 3.2pre4
  345. */
  346. public boolean getFocusTraversalKeysEnabled()
  347. {
  348. return false;
  349. } //}}}
  350. //{{{ processKeyEvent() method
  351. protected void processKeyEvent(KeyEvent _evt)
  352. {
  353. KeyEvent evt = KeyEventWorkaround.processKeyEvent(_evt);
  354. if(!KeyEventWorkaround.isBindable(_evt.getKeyCode()))
  355. evt = null;
  356. if(debugBuffer != null)
  357. {
  358. debugBuffer.insert(debugBuffer.getLength(),
  359. "Event " + GrabKeyDialog.toString(_evt)
  360. + (evt == null ? " filtered\n"
  361. : " passed\n"));
  362. }
  363. if(evt == null)
  364. return;
  365. evt.consume();
  366. KeyEventTranslator.Key key = KeyEventTranslator
  367. .translateKeyEvent(evt);
  368. if (Debug.DUMP_KEY_EVENTS) {
  369. Log.log(Log.DEBUG,GrabKeyDialog.class,"processKeyEvent() key="+key+", _evt="+_evt+".");
  370. }
  371. if(key == null)
  372. return;
  373. if(debugBuffer != null)
  374. {
  375. debugBuffer.insert(debugBuffer.getLength(),
  376. "==> Translated to " + key + "\n");
  377. }
  378. StringBuffer keyString = new StringBuffer(getText());
  379. if(getDocument().getLength() != 0)
  380. keyString.append(' ');
  381. if (!Debug.SIMPLIFIED_KEY_HANDLING) {
  382. if(key.modifiers != null)
  383. keyString.append(key.modifiers).append('+');
  384. if(key.input == ' ')
  385. keyString.append("SPACE");
  386. else if(key.input != '\0')
  387. keyString.append(key.input);
  388. else
  389. {
  390. String symbolicName = getSymbolicName(key.key);
  391. if(symbolicName == null)
  392. return;
  393. keyString.append(symbolicName);
  394. }
  395. } else {
  396. if(key.modifiers != null) {
  397. keyString.append(key.modifiers).append('+');
  398. }
  399. String symbolicName = getSymbolicName(key.key);
  400. if(symbolicName != null) {
  401. keyString.append(symbolicName);
  402. } else {
  403. if (key.input != '\0') {
  404. if (key.input == ' ') {
  405. keyString.append("SPACE");
  406. } else {
  407. keyString.append(key.input);
  408. }
  409. } else {
  410. return;
  411. }
  412. }
  413. }
  414. setText(keyString.toString());
  415. if(debugBuffer == null)
  416. updateAssignedTo(keyString.toString());
  417. } //}}}
  418. } //}}}
  419. //{{{ ActionHandler class
  420. class ActionHandler implements ActionListener
  421. {
  422. //{{{ actionPerformed() method
  423. public void actionPerformed(ActionEvent evt)
  424. {
  425. if(evt.getSource() == ok)
  426. {
  427. if(canClose())
  428. dispose();
  429. }
  430. else if(evt.getSource() == remove)
  431. {
  432. shortcut.setText(null);
  433. isOK = true;
  434. dispose();
  435. }
  436. else if(evt.getSource() == cancel)
  437. dispose();
  438. else if(evt.getSource() == clear)
  439. {
  440. shortcut.setText(null);
  441. if(debugBuffer == null)
  442. updateAssignedTo(null);
  443. shortcut.requestFocus();
  444. }
  445. } //}}}
  446. //{{{ canClose() method
  447. private boolean canClose()
  448. {
  449. String shortcutString = shortcut.getText();
  450. if(shortcutString.length() == 0
  451. && binding.isAssigned())
  452. {
  453. // ask whether to remove the old shortcut
  454. int answer = GUIUtilities.confirm(
  455. GrabKeyDialog.this,
  456. "grab-key.remove-ask",
  457. null,
  458. JOptionPane.YES_NO_OPTION,
  459. JOptionPane.QUESTION_MESSAGE);
  460. if(answer == JOptionPane.YES_OPTION)
  461. {
  462. shortcut.setText(null);
  463. isOK = true;
  464. }
  465. else
  466. return false;
  467. }
  468. // check whether this shortcut already exists
  469. KeyBinding other = getKeyBinding(shortcutString);
  470. if(other == null || other == binding)
  471. {
  472. isOK = true;
  473. return true;
  474. }
  475. // check whether the other shortcut is the alt. shortcut
  476. if(other.name == binding.name)
  477. {
  478. // we don't need two identical shortcuts
  479. GUIUtilities.error(GrabKeyDialog.this,
  480. "grab-key.duplicate-alt-shortcut",
  481. null);
  482. return false;
  483. }
  484. // check whether shortcut is a prefix to others
  485. if(other.isPrefix)
  486. {
  487. // can't override prefix shortcuts
  488. GUIUtilities.error(GrabKeyDialog.this,
  489. "grab-key.prefix-shortcut",
  490. null);
  491. return false;
  492. }
  493. // ask whether to override that other shortcut
  494. int answer = GUIUtilities.confirm(GrabKeyDialog.this,
  495. "grab-key.duplicate-shortcut",
  496. new Object[] { other.label },
  497. JOptionPane.YES_NO_OPTION,
  498. JOptionPane.QUESTION_MESSAGE);
  499. if(answer == JOptionPane.YES_OPTION)
  500. {
  501. if(other.shortcut != null
  502. && shortcutString.startsWith(other.shortcut))
  503. {
  504. other.shortcut = null;
  505. }
  506. isOK = true;
  507. return true;
  508. }
  509. else
  510. return false;
  511. } //}}}
  512. } //}}}
  513. }