/jEdit/tags/jedit-4-1-pre8/org/gjt/sp/jedit/gui/GrabKeyDialog.java

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