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

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