PageRenderTime 90ms CodeModel.GetById 46ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 0ms

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

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