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

# · Java · 597 lines · 397 code · 80 blank · 120 comment · 102 complexity · 1f71fc78bd210c782b158d5904ddc03e MD5 · raw file

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