PageRenderTime 41ms CodeModel.GetById 17ms app.highlight 20ms RepoModel.GetById 0ms app.codeStats 0ms

/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}