PageRenderTime 120ms CodeModel.GetById 109ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 382 lines | 245 code | 38 blank | 99 comment | 62 complexity | 131a069ceb51bd04e594ab9034d929a1 MD5 | raw file
  1/*
  2 * TextAreaInputHandler.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
 24//{{{ Imports
 25import org.gjt.sp.jedit.Debug;
 26import org.gjt.sp.jedit.gui.KeyEventTranslator;
 27import org.gjt.sp.jedit.gui.KeyEventWorkaround;
 28import org.gjt.sp.jedit.textarea.TextArea;
 29import org.gjt.sp.util.Log;
 30import org.gjt.sp.util.StandardUtilities;
 31
 32import java.awt.*;
 33import java.awt.event.KeyEvent;
 34import java.util.Hashtable;
 35import org.gjt.sp.jedit.JEditBeanShellAction;
 36import org.gjt.sp.jedit.buffer.JEditBuffer;
 37import org.gjt.sp.jedit.gui.ShortcutPrefixActiveEvent;
 38//}}}
 39
 40/**
 41 * This class manage the key bindings and execute the actions binded on the
 42 * keyboard events for the standalone textarea.
 43 *
 44 * @author Matthieu Casanova
 45 * @version $Id: FoldHandler.java 5568 2006-07-10 20:52:23Z kpouer $
 46 */
 47public abstract class TextAreaInputHandler extends AbstractInputHandler<JEditBeanShellAction>
 48{
 49	private final TextArea textArea;
 50
 51	//{{{ TextAreaInputHandler constructor
 52	protected TextAreaInputHandler(TextArea textArea)
 53	{
 54		this.textArea = textArea;
 55		bindings = currentBindings = new Hashtable();
 56	} //}}}
 57
 58	//{{{ processKeyEvent() method
 59	/**
 60	 * Forwards key events directly to the input handler.
 61	 * This is slightly faster than using a KeyListener
 62	 * because some Swing overhead is avoided.
 63	 * @param evt the keyboard event
 64	 * @param from the source of the event. Since this is the input handler of the textarea, it should always be 1
 65	 * @param global it is only true if the event comes from the DefaultKeyboardFocusManager
 66	 * @since 4.3pre7
 67	 */
 68	@Override
 69	public void processKeyEvent(KeyEvent evt, int from, boolean global)
 70	{
 71		if(Debug.DUMP_KEY_EVENTS)
 72		{
 73			Log.log(Log.DEBUG,this,"Key event                 : "
 74				+ toString(evt) + " from " + from);
 75		//	Log.log(Log.DEBUG,this,view+".isFocused()="+view.isFocused()+'.',new Exception());
 76		}
 77
 78		evt = _preprocessKeyEvent(evt);
 79		if(evt == null)
 80			return;
 81
 82		if(Debug.DUMP_KEY_EVENTS)
 83		{
 84			Log.log(Log.DEBUG,this,"Key event after workaround: "
 85				+ toString(evt) + " from " + from);
 86		}
 87
 88		boolean focusOnTextArea = false;
 89		switch(evt.getID())
 90		{
 91		case KeyEvent.KEY_TYPED:
 92			// if the user pressed eg C+e n n in the
 93			// search bar we want focus to go back there
 94			// after the prefix is done
 95
 96
 97			if(keyEventInterceptor != null)
 98				keyEventInterceptor.keyTyped(evt);
 99			else if(isPrefixActive() || textArea.hasFocus())
100			{
101				processKeyEventKeyStrokeHandling(evt,from,"type ",global);
102			}
103
104
105			processKeyEventSub(focusOnTextArea);
106
107			break;
108		case KeyEvent.KEY_PRESSED:
109			if(keyEventInterceptor != null)
110				keyEventInterceptor.keyPressed(evt);
111			else if(KeyEventWorkaround.isBindable(evt.getKeyCode()))
112			{
113				processKeyEventKeyStrokeHandling(evt,from,"press",global);
114
115				processKeyEventSub(focusOnTextArea);
116
117			}
118			break;
119		case KeyEvent.KEY_RELEASED:
120			if(keyEventInterceptor != null)
121				keyEventInterceptor.keyReleased(evt);
122			break;
123		}
124	} //}}}
125
126	//{{{ _preprocessKeyEvent() method
127	/**
128	 * This method returns if the keyboard event can be handled or not.
129	 *
130	 * @param evt the keyboard event
131	 * @return null if the keyboard event cannot be handled, or the keyboard event itself
132	 * otherwise
133	 */
134	private KeyEvent _preprocessKeyEvent(KeyEvent evt)
135	{
136		if(evt.isConsumed())
137			return null;
138
139		if(Debug.DUMP_KEY_EVENTS)
140		{
141			Log.log(Log.DEBUG,this,"Key event (preprocessing) : "
142					+ toString(evt));
143		}
144
145		return KeyEventWorkaround.processKeyEvent(evt);
146	} //}}}
147
148	//{{{ processKeyEventSub() method
149	private void processKeyEventSub(boolean focusOnTextArea)
150	{
151		// this is a weird hack.
152		// we don't want C+e a to insert 'a' in the
153		// search bar if the search bar has focus...
154		if (isPrefixActive() && focusOnTextArea)
155		{
156			textArea.requestFocus();
157		}
158	} //}}}
159
160	//{{{ getAction() method
161	protected abstract JEditBeanShellAction getAction(String action);
162	//}}}
163
164	//{{{ invokeAction() method
165	/**
166	 * Invokes the specified action, repeating and recording it as
167	 * necessary.
168	 * @param action The action
169	 * @since jEdit 4.2pre1
170	 */
171	@Override
172	public void invokeAction(String action)
173	{
174		invokeAction(getAction(action));
175	} //}}}
176
177	//{{{ invokeAction() method
178	/**
179	 * Invokes the specified action, repeating and recording it as
180	 * necessary.
181	 * @param action The action
182	 */
183	@Override
184	public void invokeAction(JEditBeanShellAction action)
185	{
186		JEditBuffer buffer = textArea.getBuffer();
187
188		/* if(buffer.insideCompoundEdit())
189			buffer.endCompoundEdit(); */
190
191		// remember the last executed action
192		if(!action.noRememberLast())
193		{
194			if(lastAction == action)
195				lastActionCount++;
196			else
197			{
198				lastAction = action;
199				lastActionCount = 1;
200			}
201		}
202
203		// remember old values, in case action changes them
204		int _repeatCount = repeatCount;
205
206		// execute the action
207		if(action.noRepeat() || _repeatCount == 1)
208			action.invoke(textArea);
209		else
210		{
211			try
212			{
213				buffer.beginCompoundEdit();
214
215				for(int i = 0; i < _repeatCount; i++)
216					action.invoke(textArea);
217			}
218			finally
219			{
220				buffer.endCompoundEdit();
221			}
222		}
223
224		// If repeat was true originally, clear it
225		// Otherwise it might have been set by the action, etc
226		if(_repeatCount != 1)
227		{
228			// first of all, if this action set a
229			// readNextChar, do not clear the repeat
230			if(readNextChar != null)
231				return;
232
233			repeatCount = 1;
234		}
235	} //}}}
236
237	//{{{ handleKey() method
238	/**
239	 * Handles the given keystroke.
240	 * @param keyStroke The key stroke
241	 * @param dryRun only calculate the return value, do not have any other effect
242	 * @since jEdit 4.2pre5
243	 */
244	@Override
245	public boolean handleKey(KeyEventTranslator.Key keyStroke,boolean dryRun)
246	{
247		char input = '\0';
248		if(keyStroke.modifiers == null
249			|| keyStroke.modifiers.equals("S"))
250		{
251			switch(keyStroke.key)
252			{
253			case '\n':
254			case '\t':
255				input = (char)keyStroke.key;
256				break;
257			default:
258				input = keyStroke.input;
259				break;
260			}
261		}
262
263		if(readNextChar != null)
264		{
265			if(input != '\0')
266			{
267				if (!dryRun)
268				{
269					setCurrentBindings(bindings);
270					invokeReadNextChar(input);
271					repeatCount = 1;
272				}
273				return true;
274			}
275			else
276			{
277				if (!dryRun)
278				{
279					readNextChar = null;
280				}
281			}
282		}
283
284		Object o = currentBindings.get(keyStroke);
285		if(o == null)
286		{
287			if (!dryRun)
288			{
289				// Don't beep if the user presses some
290				// key we don't know about unless a
291				// prefix is active. Otherwise it will
292				// beep when caps lock is pressed, etc.
293				if(currentBindings != bindings)
294				{
295					Toolkit.getDefaultToolkit().beep();
296					// F10 should be passed on, but C+e F10
297					// shouldn't
298					repeatCount = 1;
299					setCurrentBindings(bindings);
300				}
301				else if(input != '\0')
302				{
303					if (!keyStroke.isFromGlobalContext())
304					{ // let user input be only local
305						userInput(input);
306					}
307				}
308				else
309				{
310					// this is retarded. excuse me while I drool
311					// and make stupid noises
312					if(KeyEventWorkaround.isNumericKeypad(keyStroke.key))
313						KeyEventWorkaround.numericKeypadKey();
314				}
315				sendShortcutPrefixOff();
316			}
317		}
318		else if(o instanceof Hashtable)
319		{
320			if (!dryRun)
321			{
322				setCurrentBindings((Hashtable)o);
323				ShortcutPrefixActiveEvent.firePrefixStateChange(currentBindings, true);
324				shortcutOn = true;
325			}
326			return true;
327		}
328		else if(o instanceof String)
329		{
330			if (!dryRun)
331			{
332				setCurrentBindings(bindings);
333				sendShortcutPrefixOff();
334				invokeAction((String)o);
335			}
336			return true;
337		}
338		else if(o instanceof JEditBeanShellAction)
339		{
340			if (!dryRun)
341			{
342				setCurrentBindings(bindings);
343				sendShortcutPrefixOff();
344				invokeAction((JEditBeanShellAction)o);
345			}
346			return true;
347		}
348		if (!dryRun)
349		{
350			sendShortcutPrefixOff();
351		}
352		return false;
353	} //}}}
354
355	//{{{ userInput() method
356	protected void userInput(char ch)
357	{
358		lastActionCount = 0;
359
360
361		if(repeatCount == 1)
362			textArea.userInput(ch);
363
364		repeatCount = 1;
365	} //}}}
366
367	//{{{ invokeReadNextChar() method
368	protected void invokeReadNextChar(char ch)
369	{
370		String charStr = StandardUtilities.charsToEscapes(String.valueOf(ch));
371
372		// this might be a bit slow if __char__ occurs a lot
373		int index;
374		while((index = readNextChar.indexOf("__char__")) != -1)
375		{
376			readNextChar = readNextChar.substring(0,index)
377				+ '\'' + charStr + '\''
378				+ readNextChar.substring(index + 8);
379		}
380		readNextChar = null;
381	} //}}}
382}