PageRenderTime 127ms CodeModel.GetById 111ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/input/AbstractInputHandler.java

#
Java | 424 lines | 220 code | 46 blank | 158 comment | 25 complexity | 88927f514e4d5523025dbbf77d442efb MD5 | raw file
  1/*
  2 * AbstractInputHandler.java - Manages key bindings and executes actions
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2006 Matthieu Casanova
  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 */
 22package org.gjt.sp.jedit.input;
 23
 24import org.gjt.sp.jedit.gui.KeyEventTranslator;
 25import org.gjt.sp.jedit.Debug;
 26import org.gjt.sp.util.Log;
 27
 28import java.awt.event.KeyListener;
 29import java.awt.event.KeyEvent;
 30import java.util.Hashtable;
 31import java.util.StringTokenizer;
 32import org.gjt.sp.jedit.JEditAbstractEditAction;
 33import org.gjt.sp.jedit.gui.ShortcutPrefixActiveEvent;
 34
 35/**
 36 * The abstract input handler manage the keyboard handling.
 37 * The entry point is
 38 * {@link #processKeyEvent(java.awt.event.KeyEvent, int, boolean)}
 39 * 
 40 * @author Matthieu Casanova
 41 * @version $Id: FoldHandler.java 5568 2006-07-10 20:52:23Z kpouer $
 42 */
 43public abstract class AbstractInputHandler<E extends JEditAbstractEditAction>
 44{
 45	protected int lastActionCount;
 46	/** This listener will receive keyboard events if it is not null. */
 47	protected KeyListener keyEventInterceptor;
 48	protected String readNextChar;
 49	protected int repeatCount;
 50	protected E lastAction;
 51
 52
 53	protected static final int REPEAT_COUNT_THRESHOLD = 20;
 54
 55	//{{{ AbstractInputHandler constructor
 56	public AbstractInputHandler()
 57	{
 58		repeatCount = 1;
 59	} //}}}
 60	
 61	//{{{ addKeyBinding() method
 62	/**
 63	 * Adds a key binding to this input handler. The key binding is
 64	 * a list of white space separated key strokes of the form
 65	 * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
 66	 * or S for Shift, and key is either a character (a-z) or a field
 67	 * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
 68	 * @param keyBinding The key binding
 69	 * @param action The action
 70	 * @since jEdit 4.2pre1
 71	 */
 72	public void addKeyBinding(String keyBinding, String action)
 73	{
 74		addKeyBinding(keyBinding,(Object)action);
 75	} //}}}
 76
 77	//{{{ addKeyBinding() method
 78	/**
 79	 * Adds a key binding to this input handler. The key binding is
 80	 * a list of white space separated key strokes of the form
 81	 * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
 82	 * or S for Shift, and key is either a character (a-z) or a field
 83	 * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
 84	 * @param keyBinding The key binding
 85	 * @param action The action
 86	 */
 87	public void addKeyBinding(String keyBinding, E action)
 88	{
 89		addKeyBinding(keyBinding,(Object)action);
 90	} //}}}
 91
 92	//{{{ addKeyBinding() method
 93	/**
 94	 * Adds a key binding to this input handler. The key binding is
 95	 * a list of white space separated key strokes of the form
 96	 * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
 97	 * or S for Shift, and key is either a character (a-z) or a field
 98	 * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
 99	 * @param keyBinding The key binding
100	 * @param action The action
101	 * @since jEdit 4.3pre1
102	 */
103	public void addKeyBinding(String keyBinding, Object action)
104	{
105		Hashtable current = bindings;
106
107		String prefixStr = null;
108
109		StringTokenizer st = new StringTokenizer(keyBinding);
110		while(st.hasMoreTokens())
111		{
112			String keyCodeStr = st.nextToken();
113			if(prefixStr == null)
114				prefixStr = keyCodeStr;
115			else
116				prefixStr = prefixStr + " " + keyCodeStr;
117
118			KeyEventTranslator.Key keyStroke = KeyEventTranslator.parseKey(keyCodeStr);
119			if(keyStroke == null)
120				return;
121
122			if(st.hasMoreTokens())
123			{
124				Object o = current.get(keyStroke);
125				if(o instanceof Hashtable)
126					current = (Hashtable)o;
127				else
128				{
129					Hashtable hash = new Hashtable();
130					hash.put(PREFIX_STR,prefixStr);
131					o = hash;
132					current.put(keyStroke,o);
133					current = (Hashtable)o;
134				}
135			}
136			else
137				current.put(keyStroke,action);
138		}
139	} //}}}
140
141	//{{{ removeKeyBinding() method
142	/**
143	 * Removes a key binding from this input handler. This is not yet
144	 * implemented.
145	 * @param keyBinding The key binding
146	 */
147	public void removeKeyBinding(String keyBinding)
148	{
149		Hashtable current = bindings;
150
151		StringTokenizer st = new StringTokenizer(keyBinding);
152		while(st.hasMoreTokens())
153		{
154			String keyCodeStr = st.nextToken();
155			KeyEventTranslator.Key keyStroke = KeyEventTranslator.parseKey(keyCodeStr);
156			if(keyStroke == null)
157				return;
158
159			if(st.hasMoreTokens())
160			{
161				Object o = current.get(keyStroke);
162				if(o instanceof Hashtable)
163					current = ((Hashtable)o);
164				else if(o != null)
165				{
166					// we have binding foo
167					// but user asks to remove foo bar?
168					current.remove(keyStroke);
169					return;
170				}
171				else
172				{
173					// user asks to remove non-existent
174					return;
175				}
176			}
177			else
178				current.remove(keyStroke);
179		}
180	} //}}}
181
182	//{{{ removeAllKeyBindings() method
183	/**
184	 * Removes all key bindings from this input handler.
185	 */
186	public void removeAllKeyBindings()
187	{
188		bindings.clear();
189	} //}}}
190
191	//{{{ getKeyBinding() method
192	/**
193	 * Returns either an edit action, or a hashtable if the specified key
194	 * is a prefix.
195	 * @param keyBinding The key binding
196	 * @since jEdit 3.2pre5
197	 */
198	public Object getKeyBinding(String keyBinding)
199	{
200		Hashtable current = bindings;
201		StringTokenizer st = new StringTokenizer(keyBinding);
202
203		while(st.hasMoreTokens())
204		{
205			KeyEventTranslator.Key keyStroke = KeyEventTranslator.parseKey(
206				st.nextToken());
207			if(keyStroke == null)
208				return null;
209
210			if(st.hasMoreTokens())
211			{
212				Object o = current.get(keyStroke);
213				if(o instanceof Hashtable)
214				{
215					if(!st.hasMoreTokens())
216						return o;
217					else
218						current = (Hashtable)o;
219				}
220				else
221					return o;
222			}
223			else
224			{
225				return current.get(keyStroke);
226			}
227		}
228
229		return null;
230	} //}}}
231
232	//{{{ getLastActionCount() method
233	/**
234	 * Returns the number of times the last action was executed.
235	 * It can be used with smartHome and smartEnd
236	 * @return the number of times the last action was executed
237	 * @since jEdit 2.5pre5
238	 */
239	public int getLastActionCount()
240	{
241		return lastActionCount;
242	} //}}}
243
244	//{{{ resetLastActionCount() method
245	/**
246	 * Resets the last action count. This should be called when an
247	 * editing operation that is not an action is invoked, for example
248	 * a mouse click.
249	 * @since jEdit 4.0pre1
250	 */
251	public void resetLastActionCount()
252	{
253		lastActionCount = 0;
254	} //}}}
255
256	//{{{ getKeyEventInterceptor() method
257	public KeyListener getKeyEventInterceptor()
258	{
259		return keyEventInterceptor;
260	} //}}}
261
262	//{{{ setKeyEventInterceptor() method
263	/**
264	 * Sets the listener that will handle all key events in this
265	 * view. For example, the complete word command uses this so
266	 * that all key events are passed to the word list popup while
267	 * it is visible.
268	 * @param keyEventInterceptor the KeyListener that will receive the events
269	 */
270	public void setKeyEventInterceptor(KeyListener keyEventInterceptor)
271	{
272		this.keyEventInterceptor = keyEventInterceptor;
273	} //}}}
274
275	//{{{ isPrefixActive() method
276	/**
277	 * Returns if a prefix key has been pressed.
278	 */
279	public boolean isPrefixActive()
280	{
281		return readNextChar != null;
282	} //}}}
283	
284	//{{{ setBindings() method
285	/**
286	 * Replace the set of key bindings.
287	 * @since jEdit 4.3pre1
288	 */
289	public void setBindings(Hashtable bindings)
290	{
291		this.bindings = this.currentBindings = bindings;
292	} //}}}
293	
294	//{{{ setCurrentBindings() method
295	public void setCurrentBindings(Hashtable bindings)
296	{
297		currentBindings = bindings;
298	} //}}}
299
300	//{{{ handleKey() method
301	/**
302	 * Handles a keystroke.
303	 * @param keyStroke The key stroke.
304	 * @param dryRun only calculate the return value, do not have any other effect
305	 * @return true if the input could be handled.
306	 * @since jEdit 4.3pre7
307	 */
308	public abstract boolean handleKey(KeyEventTranslator.Key keyStroke,boolean dryRun);
309	//}}}
310
311	//{{{ processKeyEvent() method
312
313	/**
314	 * Process a keyboard event.
315	 * This is the entry point of the keyboard handling
316	 *
317	 * @param evt the keyboard event
318	 * @param from the source, it can be {@link org.gjt.sp.jedit.View#VIEW},
319	 * {@link org.gjt.sp.jedit.View#ACTION_BAR} or {@link org.gjt.sp.jedit.View#TEXT_AREA}
320	 * @param global tell if the event comes from the DefaultKeyboardFocusManager or not
321	 */
322	public abstract void processKeyEvent(KeyEvent evt, int from, boolean global); 
323	//}}}
324
325	//{{{ sendShortcutPrefixOff() method
326	protected void sendShortcutPrefixOff()
327	{
328		if(shortcutOn)
329		{
330			ShortcutPrefixActiveEvent.firePrefixStateChange(null, false);
331			shortcutOn = false;
332		}
333	} //}}}
334	
335	public abstract void invokeAction(String action);
336	
337	public abstract void invokeAction(E action);
338
339	//{{{ toString() method
340	/**
341	 * Return a String representation of the keyboard event for
342	 * debugging purpose.
343	 *
344	 * @param evt the keyboard event
345	 * @return a String representation for this keyboard event
346	 * @since jEdit 4.3pre15
347	 */
348	public static String toString(KeyEvent evt)
349	{
350		String id;
351		switch(evt.getID())
352		{
353		case KeyEvent.KEY_PRESSED:
354			id = "KEY_PRESSED";
355			break;
356		case KeyEvent.KEY_RELEASED:
357			id = "KEY_RELEASED";
358			break;
359		case KeyEvent.KEY_TYPED:
360			id = "KEY_TYPED";
361			break;
362		default:
363			id = "unknown type";
364			break;
365		}
366
367		StringBuilder b = new StringBuilder(50);
368
369		b.append(id);
370		b.append(",keyCode=0x").append(Integer.toString(evt.getKeyCode(), 16));
371		b.append(",keyChar=0x").append(Integer.toString(evt.getKeyChar(), 16));
372		b.append(",modifiers=0x").append(Integer.toString(evt.getModifiers(), 16));
373
374		b.append(",consumed=");
375		b.append(evt.isConsumed()?'1':'0');
376
377		return b.toString();
378	} //}}}
379
380	//{{{ processKeyEventKeyStrokeHandling() method
381	/**
382	 *
383	 * @param evt the keyboard event
384	 * @param from the source, it can be {@link org.gjt.sp.jedit.View#VIEW},
385	 * {@link org.gjt.sp.jedit.View#ACTION_BAR} or {@link org.gjt.sp.jedit.View#TEXT_AREA}
386	 * @param mode the mode is "press" or "type" and is used for debug only  
387	 * @param global tell if the event comes from the DefaultKeyboardFocusManager or not
388	 */
389	protected void processKeyEventKeyStrokeHandling(KeyEvent evt, int from, String mode, boolean global)
390	{
391		KeyEventTranslator.Key keyStroke = KeyEventTranslator.translateKeyEvent(evt);
392
393		if(keyStroke != null)
394		{
395			keyStroke.setIsFromGlobalContext(global);
396			if(Debug.DUMP_KEY_EVENTS)
397			{
398				Log.log(Log.DEBUG,this,"Translated (key "+mode+"): "+keyStroke+" from "+from);
399			}
400			boolean consumed = false;
401			if(handleKey(keyStroke, false))
402			{
403				evt.consume();
404
405				consumed = true;
406			}
407			if(Debug.DUMP_KEY_EVENTS)
408			{
409				Log.log(Log.DEBUG,this,"Translated (key "+mode+"): "+keyStroke+" from "+from+": consumed="+consumed+'.');
410			}
411		}
412	} //}}}
413	
414	//{{{ Private members
415
416	// Stores prefix name in bindings hashtable
417	public static Object PREFIX_STR = "PREFIX_STR";
418	protected boolean shortcutOn = false;
419	
420
421	protected Hashtable bindings;
422	protected Hashtable currentBindings;
423	//}}}
424}