PageRenderTime 178ms CodeModel.GetById 137ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 550 lines | 358 code | 68 blank | 124 comment | 99 complexity | 745a33b1a7c54d0638229ecd0ad751fb 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	//{{{ 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				String modifiers = DefaultInputHandler
416					.getModifierString(evt);
417				if(modifiers.length() != 0)
418				{
419					keyString.append(modifiers);
420					keyString.append('+');
421				}
422
423				int keyCode = evt.getKeyCode();
424
425				if(((keyCode >= KeyEvent.VK_A && keyCode <= KeyEvent.VK_Z)
426					|| (keyCode >= KeyEvent.VK_0 && keyCode <= KeyEvent.VK_9))
427					&& modifiers.length() == 0)
428				{
429					// will be handled by KEY_TYPED
430					return;
431				}
432
433				String symbolicName = getSymbolicName(keyCode);
434
435				if(symbolicName == null)
436					return;
437
438				keyString.append(symbolicName);
439			}
440			else if(evt.getID() == KeyEvent.KEY_RELEASED)
441				return;
442
443			setText(keyString.toString());
444			if(debugBuffer == null)
445				updateAssignedTo(keyString.toString());
446		} //}}}
447	} //}}}
448
449	//{{{ ActionHandler class
450	class ActionHandler implements ActionListener
451	{
452		//{{{ actionPerformed() method
453		public void actionPerformed(ActionEvent evt)
454		{
455			if(evt.getSource() == ok)
456			{
457				if(canClose())
458					dispose();
459			}
460			else if(evt.getSource() == remove)
461			{
462				shortcut.setText(null);
463				isOK = true;
464				dispose();
465			}
466			else if(evt.getSource() == cancel)
467				dispose();
468			else if(evt.getSource() == clear)
469			{
470				shortcut.setText(null);
471				if(debugBuffer == null)
472					updateAssignedTo(null);
473			}
474		} //}}}
475
476		//{{{ canClose() method
477		private boolean canClose()
478		{
479			String shortcutString = shortcut.getText();
480			if(shortcutString.length() == 0
481				&& binding.isAssigned())
482			{
483				// ask whether to remove the old shortcut
484				int answer = GUIUtilities.confirm(
485					GrabKeyDialog.this,
486					"grab-key.remove-ask",
487					null,
488					JOptionPane.YES_NO_CANCEL_OPTION,
489					JOptionPane.QUESTION_MESSAGE);
490				if(answer == JOptionPane.YES_OPTION)
491				{
492					shortcut.setText(null);
493					isOK = true;
494				}
495				else if(answer == JOptionPane.CANCEL_OPTION)
496					return false;
497				return true;
498			}
499
500			// check whether this shortcut already exists
501			KeyBinding other = getKeyBinding(shortcutString);
502			if(other == null || other == binding)
503			{
504				isOK = true;
505				return true;
506			}
507
508			// check whether the other shortcut is the alt. shortcut
509			if(other.name == binding.name)
510			{
511				// we don't need two identical shortcuts
512				GUIUtilities.error(GrabKeyDialog.this,
513					"grab-key.duplicate-alt-shortcut",
514					null);
515				return false;
516			}
517
518			// check whether shortcut is a prefix to others
519			if(other.isPrefix)
520			{
521				// can't override prefix shortcuts
522				GUIUtilities.error(GrabKeyDialog.this,
523					"grab-key.prefix-shortcut",
524					null);
525				return false;
526			}
527
528			// ask whether to override that other shortcut
529			int answer = GUIUtilities.confirm(GrabKeyDialog.this,
530				"grab-key.duplicate-shortcut",
531				new Object[] { other.label },
532				JOptionPane.YES_NO_CANCEL_OPTION,
533				JOptionPane.QUESTION_MESSAGE);
534			if(answer == JOptionPane.YES_OPTION)
535			{
536				if(other.shortcut != null
537					&& shortcutString.startsWith(other.shortcut))
538				{
539					other.shortcut = null;
540				}
541				isOK = true;
542				return true;
543			}
544			else if(answer == JOptionPane.CANCEL_OPTION)
545				return false;
546
547			return true;
548		} //}}}
549	} //}}}
550}