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