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