PageRenderTime 73ms CodeModel.GetById 57ms app.highlight 13ms RepoModel.GetById 0ms app.codeStats 1ms

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

#
Java | 515 lines | 334 code | 39 blank | 142 comment | 83 complexity | 013cf91481a95d16fd6d27e27e445c50 MD5 | raw file
  1/*
  2 * DefaultInputHandler.java - Default implementation of an input handler
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 1999, 2000, 2001 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.KeyStroke;
 27import java.awt.event.*;
 28import java.awt.Toolkit;
 29import java.util.Hashtable;
 30import java.util.StringTokenizer;
 31import org.gjt.sp.jedit.*;
 32import org.gjt.sp.util.Log;
 33//}}}
 34
 35/**
 36 * The default input handler. It maps sequences of keystrokes into actions
 37 * and inserts key typed events into the text area.
 38 * @author Slava Pestov
 39 * @version $Id: DefaultInputHandler.java 4305 2002-08-13 18:59:48Z spestov $
 40 */
 41public class DefaultInputHandler extends InputHandler
 42{
 43	//{{{ DefaultInputHandler constructor
 44	/**
 45	 * Creates a new input handler with no key bindings defined.
 46	 * @param view The view
 47	 */
 48	public DefaultInputHandler(View view)
 49	{
 50		super(view);
 51
 52		bindings = currentBindings = new Hashtable();
 53	} //}}}
 54
 55	//{{{ DefaultInputHandler constructor
 56	/**
 57	 * Creates a new input handler with the same set of key bindings
 58	 * as the one specified. Note that both input handlers share
 59	 * a pointer to exactly the same key binding table; so adding
 60	 * a key binding in one will also add it to the other.
 61	 * @param copy The input handler to copy key bindings from
 62	 * @param view The view
 63	 */
 64	public DefaultInputHandler(View view, DefaultInputHandler copy)
 65	{
 66		super(view);
 67
 68		bindings = currentBindings = copy.bindings;
 69	} //}}}
 70
 71	//{{{ addKeyBinding() method
 72	/**
 73	 * Adds a key binding to this input handler. The key binding is
 74	 * a list of white space separated key strokes of the form
 75	 * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
 76	 * or S for Shift, and key is either a character (a-z) or a field
 77	 * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
 78	 * @param keyBinding The key binding
 79	 * @param action The action
 80	 */
 81	public void addKeyBinding(String keyBinding, EditAction action)
 82	{
 83	        Hashtable current = bindings;
 84
 85		StringTokenizer st = new StringTokenizer(keyBinding);
 86		while(st.hasMoreTokens())
 87		{
 88			KeyStroke keyStroke = parseKeyStroke(st.nextToken());
 89			if(keyStroke == null)
 90				return;
 91
 92			if(st.hasMoreTokens())
 93			{
 94				Object o = current.get(keyStroke);
 95				if(o instanceof Hashtable)
 96					current = (Hashtable)o;
 97				else
 98				{
 99					o = new Hashtable();
100					current.put(keyStroke,o);
101					current = (Hashtable)o;
102				}
103			}
104			else
105				current.put(keyStroke,action);
106		}
107	} //}}}
108
109	//{{{ removeKeyBinding() method
110	/**
111	 * Removes a key binding from this input handler. This is not yet
112	 * implemented.
113	 * @param keyBinding The key binding
114	 */
115	public void removeKeyBinding(String keyBinding)
116	{
117		throw new InternalError("Not yet implemented");
118	} //}}}
119
120	//{{{ removeAllKeyBindings() method
121	/**
122	 * Removes all key bindings from this input handler.
123	 */
124	public void removeAllKeyBindings()
125	{
126		bindings.clear();
127	} //}}}
128
129	//{{{ getKeyBinding() method
130	/**
131	 * Returns either an edit action, or a hashtable if the specified key
132	 * is a prefix.
133	 * @param keyBinding The key binding
134	 * @since jEdit 3.2pre5
135	 */
136	public Object getKeyBinding(String keyBinding)
137	{
138		Hashtable current = bindings;
139		StringTokenizer st = new StringTokenizer(keyBinding);
140
141		while(st.hasMoreTokens())
142		{
143			KeyStroke keyStroke = parseKeyStroke(st.nextToken());
144			if(keyStroke == null)
145				return null;
146
147			if(st.hasMoreTokens())
148			{
149				Object o = current.get(keyStroke);
150				if(o instanceof Hashtable)
151					current = (Hashtable)o;
152				else
153					return o;
154			}
155			else
156			{
157				return current.get(keyStroke);
158			}
159		}
160
161		return null;
162	} //}}}
163
164	//{{{ isPrefixActive() method
165	/**
166	 * Returns if a prefix key has been pressed.
167	 */
168	public boolean isPrefixActive()
169	{
170		return bindings != currentBindings;
171	} //}}}
172
173	//{{{ keyPressed() method
174	/**
175	 * Handle a key pressed event. This will look up the binding for
176	 * the key stroke and execute it.
177	 */
178	public void keyPressed(KeyEvent evt)
179	{
180		int keyCode = evt.getKeyCode();
181		int modifiers = evt.getModifiers();
182
183		if(!(evt.isControlDown() || evt.isAltDown() || evt.isMetaDown()))
184		{
185			// if modifier active, handle all keys, otherwise
186			// only some
187			if((keyCode >= KeyEvent.VK_A && keyCode <= KeyEvent.VK_Z)
188				|| (keyCode >= KeyEvent.VK_0 && keyCode <= KeyEvent.VK_9))
189			{
190				return;
191			}
192			else if(keyCode == KeyEvent.VK_SPACE)
193			{
194				return;
195			}
196			else if(readNextChar != null)
197			{
198				if(keyCode == KeyEvent.VK_ESCAPE)
199				{
200					readNextChar = null;
201					view.getStatus().setMessage(null);
202				}
203				else if(!evt.isActionKey()
204					&& keyCode != KeyEvent.VK_TAB
205					&& keyCode != KeyEvent.VK_ENTER)
206				{
207					return;
208				}
209			}
210			else
211			{
212				// ok even with no modifiers
213			}
214		}
215
216		KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
217			modifiers);
218
219		Object o = currentBindings.get(keyStroke);
220		if(o == null)
221		{
222			// Don't beep if the user presses some
223			// key we don't know about unless a
224			// prefix is active. Otherwise it will
225			// beep when caps lock is pressed, etc.
226			if(currentBindings != bindings)
227			{
228				Toolkit.getDefaultToolkit().beep();
229				// F10 should be passed on, but C+e F10
230				// shouldn't
231				repeatCount = 0;
232				repeat = false;
233				evt.consume();
234				currentBindings = bindings;
235			}
236			else if(modifiers == 0 && (keyCode == KeyEvent.VK_ENTER
237				|| keyCode == KeyEvent.VK_TAB))
238			{
239				userInput((char)keyCode);
240				evt.consume();
241			}
242		}
243
244		if(readNextChar != null)
245		{
246			readNextChar = null;
247			view.getStatus().setMessage(null);
248		}
249
250		if(o instanceof EditAction)
251		{
252			currentBindings = bindings;
253			invokeAction((EditAction)o);
254			evt.consume();
255		}
256		else if(o instanceof Hashtable)
257		{
258			currentBindings = (Hashtable)o;
259			evt.consume();
260		}
261
262		if(o == null)
263		{
264			switch(evt.getKeyCode())
265			{
266				case KeyEvent.VK_NUMPAD0:   case KeyEvent.VK_NUMPAD1:
267				case KeyEvent.VK_NUMPAD2:   case KeyEvent.VK_NUMPAD3:
268				case KeyEvent.VK_NUMPAD4:   case KeyEvent.VK_NUMPAD5:
269				case KeyEvent.VK_NUMPAD6:   case KeyEvent.VK_NUMPAD7:
270				case KeyEvent.VK_NUMPAD8:   case KeyEvent.VK_NUMPAD9:
271				case KeyEvent.VK_MULTIPLY:  case KeyEvent.VK_ADD:
272				/* case KeyEvent.VK_SEPARATOR: */ case KeyEvent.VK_SUBTRACT:
273				case KeyEvent.VK_DECIMAL:   case KeyEvent.VK_DIVIDE:
274					KeyEventWorkaround.numericKeypadKey();
275					break;
276			}
277		}
278	} //}}}
279
280	//{{{ keyTyped() method
281	/**
282	 * Handle a key typed event. This inserts the key into the text area.
283	 */
284	public void keyTyped(KeyEvent evt)
285	{
286		char c = evt.getKeyChar();
287
288		// ignore
289		if(c == '\b')
290			return;
291
292		KeyStroke keyStroke;
293
294		// this is a hack. a literal space is impossible to
295		// insert in a key binding string, but you can write
296		// SPACE.
297		switch(c)
298		{
299		case ' ':
300			keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
301				evt.getModifiers());
302			break;
303		case '\t':
304			keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
305				evt.getModifiers());
306			break;
307		case '\n':
308			keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
309				evt.getModifiers());
310			break;
311		default:
312			keyStroke = KeyStroke.getKeyStroke(c);
313			break;
314		}
315
316		Object o = currentBindings.get(keyStroke);
317
318		if(o instanceof Hashtable)
319		{
320			currentBindings = (Hashtable)o;
321		}
322		else if(o instanceof EditAction)
323		{
324			currentBindings = bindings;
325			invokeAction((EditAction)o);
326		}
327		else
328		{
329			// otherwise, reset to default map and do user input
330			currentBindings = bindings;
331
332			if(repeat && Character.isDigit(c))
333			{
334				repeatCount *= 10;
335				repeatCount += (c - '0');
336				view.getStatus().setMessage(null);
337			}
338			else
339				userInput(c);
340		}
341	} //}}}
342
343	//{{{ setModifierMapping() method
344	/**
345	 * Changes the mapping between symbolic modifier key names
346	 * (<code>C</code>, <code>A</code>, <code>M</code>, <code>S</code>) and
347	 * Java modifier flags.
348	 *
349	 * @param c The modifier to map the <code>C</code> modifier to
350	 * @param a The modifier to map the <code>A</code> modifier to
351	 * @param m The modifier to map the <code>M</code> modifier to
352	 * @param s The modifier to map the <code>S</code> modifier to
353	 *
354	 * @since jEdit 4.1pre3
355	 */
356	public static void setModifierMapping(int c, int a, int m, int s)
357	{
358		DefaultInputHandler.c = c;
359		DefaultInputHandler.a = a;
360		DefaultInputHandler.m = m;
361		DefaultInputHandler.s = s;
362	} //}}}
363
364	//{{{ getSymbolicModifierName() method
365	/**
366	 * Returns a the symbolic modifier name for the specified Java modifier
367	 * flag.
368	 *
369	 * @param mod A modifier constant from <code>InputEvent</code>
370	 *
371	 * @since jEdit 4.1pre3
372	 */
373	public static char getSymbolicModifierName(int mod)
374	{
375		// this relies on the fact that if C is mapped to M, then
376		// M will be mapped to C.
377		if(mod == c)
378			return 'C';
379		else if(mod == a)
380			return 'A';
381		else if(mod == m)
382			return 'M';
383		else if(mod == s)
384			return 'S';
385		else
386			return '\0';
387	} //}}}
388
389	//{{{ getModifierString() method
390	/**
391	 * Returns a string containing symbolic modifier names set in the
392	 * specified event.
393	 *
394	 * @param evt The event
395	 *
396	 * @since jEdit 4.1pre3
397	 */
398	public static String getModifierString(InputEvent evt)
399	{
400		StringBuffer buf = new StringBuffer();
401		if(evt.isControlDown())
402			buf.append(getSymbolicModifierName(InputEvent.CTRL_MASK));
403		if(evt.isAltDown())
404			buf.append(getSymbolicModifierName(InputEvent.ALT_MASK));
405		if(evt.isMetaDown())
406			buf.append(getSymbolicModifierName(InputEvent.META_MASK));
407		if(evt.isShiftDown())
408			buf.append(getSymbolicModifierName(InputEvent.SHIFT_MASK));
409		return buf.toString();
410	} //}}}
411
412	//{{{ parseKeyStroke() method
413	/**
414	 * Converts a string to a keystroke. The string should be of the
415	 * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
416	 * is any combination of A for Alt, C for Control, S for Shift
417	 * or M for Meta, and <i>shortcut</i> is either a single character,
418	 * or a keycode name from the <code>KeyEvent</code> class, without
419	 * the <code>VK_</code> prefix.
420	 * @param keyStroke A string description of the key stroke
421	 */
422	public static KeyStroke parseKeyStroke(String keyStroke)
423	{
424		if(keyStroke == null)
425			return null;
426		int modifiers = 0;
427		int index = keyStroke.indexOf('+');
428		if(index != -1)
429		{
430			for(int i = 0; i < index; i++)
431			{
432				switch(Character.toUpperCase(keyStroke
433					.charAt(i)))
434				{
435				case 'A':
436					modifiers |= a;
437					break;
438				case 'C':
439					modifiers |= c;
440					break;
441				case 'M':
442					modifiers |= m;
443					break;
444				case 'S':
445					modifiers |= s;
446					break;
447				}
448			}
449		}
450		String key = keyStroke.substring(index + 1);
451		if(key.length() == 1)
452		{
453			char ch = key.charAt(0);
454			if(modifiers == 0)
455				return KeyStroke.getKeyStroke(ch);
456			else
457			{
458				return KeyStroke.getKeyStroke(Character.toUpperCase(ch),
459					modifiers);
460			}
461		}
462		else if(key.length() == 0)
463		{
464			Log.log(Log.ERROR,DefaultInputHandler.class,
465				"Invalid key stroke: " + keyStroke);
466			return null;
467		}
468		else
469		{
470			int ch;
471
472			try
473			{
474				ch = KeyEvent.class.getField("VK_".concat(key))
475					.getInt(null);
476			}
477			catch(Exception e)
478			{
479				Log.log(Log.ERROR,DefaultInputHandler.class,
480					"Invalid key stroke: "
481					+ keyStroke);
482				return null;
483			}
484
485			return KeyStroke.getKeyStroke(ch,modifiers);
486		}
487	} //}}}
488
489	//{{{ Private members
490	static
491	{
492		if(OperatingSystem.isMacOS())
493		{
494			setModifierMapping(
495				InputEvent.META_MASK,
496				InputEvent.ALT_MASK,
497				InputEvent.CTRL_MASK,
498				InputEvent.SHIFT_MASK);
499		}
500		else
501		{
502			setModifierMapping(
503				InputEvent.CTRL_MASK,
504				InputEvent.ALT_MASK,
505				InputEvent.META_MASK,
506				InputEvent.SHIFT_MASK);
507		}
508	}
509
510	private static int c, a, m, s;
511
512	private Hashtable bindings;
513	private Hashtable currentBindings;
514	//}}}
515}