PageRenderTime 121ms CodeModel.GetById 94ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 414 lines | 272 code | 33 blank | 109 comment | 66 complexity | 1a1f3d771f86efd62bd89e354d385c8a 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 4012 2002-02-05 06:28:10Z 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())
184			|| evt.isMetaDown()))
185		{
186			// if modifier active, handle all keys, otherwise
187			// only some
188			if((keyCode >= KeyEvent.VK_A && keyCode <= KeyEvent.VK_Z)
189				|| (keyCode >= KeyEvent.VK_0 && keyCode <= KeyEvent.VK_9))
190			{
191				return;
192			}
193			else if(keyCode == KeyEvent.VK_SPACE)
194			{
195				return;
196			}
197			else if(readNextChar != null)
198			{
199				if(keyCode == KeyEvent.VK_ESCAPE)
200				{
201					readNextChar = null;
202					view.getStatus().setMessage(null);
203				}
204				else if(!evt.isActionKey()
205					&& keyCode != KeyEvent.VK_TAB
206					&& keyCode != KeyEvent.VK_ENTER)
207				{
208					return;
209				}
210			}
211			else
212			{
213				// ok even with no modifiers
214			}
215		}
216
217		KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
218			modifiers);
219
220		Object o = currentBindings.get(keyStroke);
221		if(o == null)
222		{
223			// Don't beep if the user presses some
224			// key we don't know about unless a
225			// prefix is active. Otherwise it will
226			// beep when caps lock is pressed, etc.
227			if(currentBindings != bindings)
228			{
229				Toolkit.getDefaultToolkit().beep();
230				// F10 should be passed on, but C+e F10
231				// shouldn't
232				repeatCount = 0;
233				repeat = false;
234				evt.consume();
235				currentBindings = bindings;
236			}
237			else if(modifiers == 0 && (keyCode == KeyEvent.VK_ENTER
238				|| keyCode == KeyEvent.VK_TAB))
239			{
240				userInput((char)keyCode);
241				evt.consume();
242			}
243		}
244
245		if(readNextChar != null)
246		{
247			readNextChar = null;
248			view.getStatus().setMessage(null);
249		}
250
251		if(o instanceof EditAction)
252		{
253			currentBindings = bindings;
254			invokeAction((EditAction)o);
255			evt.consume();
256		}
257		else if(o instanceof Hashtable)
258		{
259			currentBindings = (Hashtable)o;
260			evt.consume();
261		}
262	} //}}}
263
264	//{{{ keyTyped() method
265	/**
266	 * Handle a key typed event. This inserts the key into the text area.
267	 */
268	public void keyTyped(KeyEvent evt)
269	{
270		char c = evt.getKeyChar();
271
272		// ignore
273		if(c == '\b')
274			return;
275
276		KeyStroke keyStroke;
277
278		// this is a hack. a literal space is impossible to
279		// insert in a key binding string, but you can write
280		// SPACE.
281		switch(c)
282		{
283		case ' ':
284			keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
285				evt.getModifiers());
286			break;
287		case '\t':
288			keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
289				evt.getModifiers());
290			break;
291		case '\n':
292			keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
293				evt.getModifiers());
294			break;
295		default:
296			keyStroke = KeyStroke.getKeyStroke(c);
297			break;
298		}
299
300		Object o = currentBindings.get(keyStroke);
301
302		if(o instanceof Hashtable)
303		{
304			currentBindings = (Hashtable)o;
305		}
306		else if(o instanceof EditAction)
307		{
308			currentBindings = bindings;
309			invokeAction((EditAction)o);
310		}
311		else
312		{
313			// otherwise, reset to default map and do user input
314			currentBindings = bindings;
315
316			if(repeat && Character.isDigit(c))
317			{
318				repeatCount *= 10;
319				repeatCount += (c - '0');
320				view.getStatus().setMessage(null);
321			}
322			else
323				userInput(c);
324		}
325	} //}}}
326
327	//{{{ parseKeyStroke() method
328	/**
329	 * Converts a string to a keystroke. The string should be of the
330	 * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
331	 * is any combination of A for Alt, C for Control, S for Shift
332	 * or M for Meta, and <i>shortcut</i> is either a single character,
333	 * or a keycode name from the <code>KeyEvent</code> class, without
334	 * the <code>VK_</code> prefix.
335	 * @param keyStroke A string description of the key stroke
336	 */
337	public static KeyStroke parseKeyStroke(String keyStroke)
338	{
339		if(keyStroke == null)
340			return null;
341		int modifiers = 0;
342		int index = keyStroke.indexOf('+');
343		if(index != -1)
344		{
345			for(int i = 0; i < index; i++)
346			{
347				switch(Character.toUpperCase(keyStroke
348					.charAt(i)))
349				{
350				case 'A':
351					modifiers |= InputEvent.ALT_MASK;
352					break;
353				case 'C':
354					if(OperatingSystem.isMacOS())
355						modifiers |= InputEvent.META_MASK;
356					else
357						modifiers |= InputEvent.CTRL_MASK;
358					break;
359				case 'M':
360					if(OperatingSystem.isMacOS())
361						modifiers |= InputEvent.CTRL_MASK;
362					else
363						modifiers |= InputEvent.META_MASK;
364					break;
365				case 'S':
366					modifiers |= InputEvent.SHIFT_MASK;
367					break;
368				}
369			}
370		}
371		String key = keyStroke.substring(index + 1);
372		if(key.length() == 1)
373		{
374			char ch = key.charAt(0);
375			if(modifiers == 0)
376				return KeyStroke.getKeyStroke(ch);
377			else
378			{
379				return KeyStroke.getKeyStroke(Character.toUpperCase(ch),
380					modifiers);
381			}
382		}
383		else if(key.length() == 0)
384		{
385			Log.log(Log.ERROR,DefaultInputHandler.class,
386				"Invalid key stroke: " + keyStroke);
387			return null;
388		}
389		else
390		{
391			int ch;
392
393			try
394			{
395				ch = KeyEvent.class.getField("VK_".concat(key))
396					.getInt(null);
397			}
398			catch(Exception e)
399			{
400				Log.log(Log.ERROR,DefaultInputHandler.class,
401					"Invalid key stroke: "
402					+ keyStroke);
403				return null;
404			}
405
406			return KeyStroke.getKeyStroke(ch,modifiers);
407		}
408	} //}}}
409
410	//{{{ Private members
411	private Hashtable bindings;
412	private Hashtable currentBindings;
413	//}}}
414}