PageRenderTime 83ms CodeModel.GetById 66ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 422 lines | 227 code | 53 blank | 142 comment | 40 complexity | e8d7044a1e1b927909dfdc69061f21a7 MD5 | raw file
  1/*
  2 * InputHandler.java - Manages key bindings and executes actions
  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.JOptionPane;
 27import javax.swing.JPopupMenu;
 28import java.awt.event.*;
 29import java.awt.Component;
 30import java.util.*;
 31import org.gjt.sp.jedit.textarea.JEditTextArea;
 32import org.gjt.sp.jedit.*;
 33import org.gjt.sp.util.Log;
 34//}}}
 35
 36/**
 37 * An input handler converts the user's key strokes into concrete actions.
 38 * It also takes care of macro recording and action repetition.<p>
 39 *
 40 * This class provides all the necessary support code for an input
 41 * handler, but doesn't actually do any key binding logic. It is up
 42 * to the implementations of this class to do so.
 43 *
 44 * @author Slava Pestov
 45 * @version $Id: InputHandler.java 4012 2002-02-05 06:28:10Z spestov $
 46 * @see org.gjt.sp.jedit.gui.DefaultInputHandler
 47 */
 48public abstract class InputHandler extends KeyAdapter
 49{
 50	//{{{ InputHandler constructor
 51	/**
 52	 * Creates a new input handler.
 53	 * @param view The view
 54	 */
 55	public InputHandler(View view)
 56	{
 57		this.view = view;
 58	} //}}}
 59
 60	//{{{ processKeyEvent() method
 61	/**
 62	 * Utility method, calls one of <code>keyPressed()</code>,
 63	 * <code>keyReleased()</code>, or <code>keyTyped()</code>.
 64	 * @since jEdit 4.0pre4
 65	 */
 66	public void processKeyEvent(KeyEvent evt)
 67	{
 68		switch(evt.getID())
 69		{
 70		case KeyEvent.KEY_TYPED:
 71			keyTyped(evt);
 72			break;
 73		case KeyEvent.KEY_PRESSED:
 74			keyPressed(evt);
 75			break;
 76		case KeyEvent.KEY_RELEASED:
 77			keyReleased(evt);
 78			break;
 79		}
 80	} //}}}
 81
 82	//{{{ addKeyBinding() method
 83	/**
 84	 * Adds a key binding to this input handler.
 85	 * @param keyBinding The key binding (the format of this is
 86	 * input-handler specific)
 87	 * @param action The action
 88	 */
 89	public abstract void addKeyBinding(String keyBinding, EditAction action);
 90	//}}}
 91
 92	//{{{ removeKeyBinding() method
 93	/**
 94	 * Removes a key binding from this input handler.
 95	 * @param keyBinding The key binding
 96	 */
 97	public abstract void removeKeyBinding(String keyBinding);
 98	//}}}
 99
100	//{{{ removeAllKeyBindings() method
101	/**
102	 * Removes all key bindings from this input handler.
103	 */
104	public abstract void removeAllKeyBindings();
105	//}}}
106
107	//{{{ isPrefixActive() method
108	/**
109	 * Returns if a prefix key has been pressed.
110	 */
111	public boolean isPrefixActive()
112	{
113		return false;
114	} //}}}
115
116	//{{{ isRepeatEnabled() method
117	/**
118	 * Returns if repeating is enabled. When repeating is enabled,
119	 * actions will be executed multiple times. This is usually
120	 * invoked with a special key stroke in the input handler.
121	 */
122	public boolean isRepeatEnabled()
123	{
124		return repeat;
125	} //}}}
126
127	//{{{ setRepeatEnabled() method
128	/**
129	 * Enables repeating. When repeating is enabled, actions will be
130	 * executed multiple times. Once repeating is enabled, the input
131	 * handler should read a number from the keyboard.
132	 */
133	public void setRepeatEnabled(boolean repeat)
134	{
135		boolean oldRepeat = this.repeat;
136		this.repeat = repeat;
137		repeatCount = 0;
138		if(oldRepeat != repeat)
139			view.getStatus().setMessage(null);
140	} //}}}
141
142	//{{{ getRepeatCount() method
143	/**
144	 * Returns the number of times the next action will be repeated.
145	 */
146	public int getRepeatCount()
147	{
148		return (repeat && repeatCount > 0 ? repeatCount : 1);
149	} //}}}
150
151	//{{{ setRepeatCount() method
152	/**
153	 * Sets the number of times the next action will be repeated.
154	 * @param repeatCount The repeat count
155	 */
156	public void setRepeatCount(int repeatCount)
157	{
158		boolean oldRepeat = this.repeat;
159		repeat = true;
160		this.repeatCount = repeatCount;
161		if(oldRepeat != repeat)
162			view.getStatus().setMessage(null);
163	} //}}}
164
165	//{{{ getLastAction() method
166	/**
167	 * Returns the last executed action.
168	 * @since jEdit 2.5pre5
169	 */
170	public EditAction getLastAction()
171	{
172		return lastAction;
173	} //}}}
174
175	//{{{ getLastActionCount() method
176	/**
177	 * Returns the number of times the last action was executed.
178	 * @since jEdit 2.5pre5
179	 */
180	public int getLastActionCount()
181	{
182		return lastActionCount;
183	} //}}}
184
185	//{{{ readNextChar() method
186	/**
187	 * Invokes the specified BeanShell code, replacing __char__ in the
188	 * code with the next input character.
189	 * @param msg The prompt to display in the status bar
190	 * @param code The code
191	 * @since jEdit 3.2pre2
192	 */
193	public void readNextChar(String msg, String code)
194	{
195		view.getStatus().setMessage(msg);
196		readNextChar = code;
197	} //}}}
198
199	//{{{ readNextChar() method
200	/**
201	 * @deprecated Use the other form of this method instead
202	 */
203	public void readNextChar(String code)
204	{
205		readNextChar = code;
206	} //}}}
207
208	//{{{ resetLastActionCount() method
209	/**
210	 * Resets the last action count. This should be called when an
211	 * editing operation that is not an action is invoked, for example
212	 * a mouse click.
213	 * @since jEdit 4.0pre1
214	 */
215	public void resetLastActionCount()
216	{
217		lastAction = null;
218		lastActionCount = 0;
219	} //}}}
220
221	//{{{ invokeAction() method
222	/**
223	 * Invokes the specified action, repeating and recording it as
224	 * necessary.
225	 * @param action The action
226	 * @param source The event source
227	 */
228	public void invokeAction(EditAction action)
229	{
230		Buffer buffer = view.getBuffer();
231
232		if(buffer.insideCompoundEdit())
233			buffer.endCompoundEdit();
234
235		// remember the last executed action
236		if(lastAction == action)
237			lastActionCount++;
238		else
239		{
240			lastAction = action;
241			lastActionCount = 1;
242		}
243
244		// remember old values, in case action changes them
245		boolean _repeat = repeat;
246		int _repeatCount = getRepeatCount();
247
248		// execute the action
249		if(action.noRepeat() || _repeatCount == 1)
250			action.invoke(view);
251		else
252		{
253			// stop people doing dumb stuff like C+ENTER 100 C+n
254			if(_repeatCount > REPEAT_COUNT_THRESHOLD)
255			{
256				String label = jEdit.getProperty(action.getLabel());
257				if(label == null)
258					label = action.getName();
259				else
260					label = GUIUtilities.prettifyMenuLabel(label);
261
262				Object[] pp = { label, new Integer(_repeatCount) };
263
264				if(GUIUtilities.confirm(view,"large-repeat-count",pp,
265					JOptionPane.WARNING_MESSAGE,
266					JOptionPane.YES_NO_OPTION)
267					!= JOptionPane.YES_OPTION)
268				{
269					repeat = false;
270					repeatCount = 0;
271					view.getStatus().setMessage(null);
272					return;
273				}
274			}
275
276			try
277			{
278				buffer.beginCompoundEdit();
279
280				for(int i = 0; i < _repeatCount; i++)
281					action.invoke(view);
282			}
283			finally
284			{
285				buffer.endCompoundEdit();
286			}
287		}
288
289		Macros.Recorder recorder = view.getMacroRecorder();
290
291		if(recorder != null && !action.noRecord())
292			recorder.record(_repeatCount,action.getCode());
293
294		// If repeat was true originally, clear it
295		// Otherwise it might have been set by the action, etc
296		if(_repeat)
297		{
298			// first of all, if this action set a
299			// readNextChar, do not clear the repeat
300			if(readNextChar != null)
301				return;
302
303			repeat = false;
304			repeatCount = 0;
305			view.getStatus().setMessage(null);
306		}
307	} //}}}
308
309	//{{{ Protected members
310	private static final int REPEAT_COUNT_THRESHOLD = 20;
311
312	//{{{ Instance variables
313	protected View view;
314	protected boolean repeat;
315	protected int repeatCount;
316
317	protected EditAction lastAction;
318	protected int lastActionCount;
319
320	protected String readNextChar;
321	//}}}
322
323	//{{{ userInput() method
324	protected void userInput(char ch)
325	{
326		lastAction = null;
327
328		if(readNextChar != null)
329			invokeReadNextChar(ch);
330		else
331		{
332			JEditTextArea textArea = view.getTextArea();
333
334			Buffer buffer = view.getBuffer();
335			if(!buffer.insideCompoundEdit())
336				buffer.beginCompoundEdit();
337
338			int _repeatCount = getRepeatCount();
339			if(_repeatCount == 1)
340				textArea.userInput(ch);
341			else
342			{
343				// stop people doing dumb stuff like C+ENTER 100 C+n
344				if(_repeatCount > REPEAT_COUNT_THRESHOLD)
345				{
346					Object[] pp = { String.valueOf(ch),
347						new Integer(_repeatCount) };
348
349					if(GUIUtilities.confirm(view,
350						"large-repeat-count.user-input",pp,
351						JOptionPane.WARNING_MESSAGE,
352						JOptionPane.YES_NO_OPTION)
353						!= JOptionPane.YES_OPTION)
354					{
355						repeat = false;
356						repeatCount = 0;
357						view.getStatus().setMessage(null);
358						return;
359					}
360				}
361
362				for(int i = 0; i < _repeatCount; i++)
363					textArea.userInput(ch);
364			}
365
366			Macros.Recorder recorder = view.getMacroRecorder();
367
368			if(recorder != null)
369				recorder.record(_repeatCount,ch);
370		}
371
372		setRepeatEnabled(false);
373	} //}}}
374
375	//{{{ invokeReadNextChar() method
376	protected void invokeReadNextChar(char ch)
377	{
378		Buffer buffer = view.getBuffer();
379
380		if(buffer.insideCompoundEdit())
381			buffer.endCompoundEdit();
382
383		String charStr = MiscUtilities.charsToEscapes(String.valueOf(ch));
384
385		// this might be a bit slow if __char__ occurs a lot
386		int index;
387		while((index = readNextChar.indexOf("__char__")) != -1)
388		{
389			readNextChar = readNextChar.substring(0,index)
390				+ '\'' + charStr + '\''
391				+ readNextChar.substring(index + 8);
392		}
393
394		Macros.Recorder recorder = view.getMacroRecorder();
395		if(recorder != null)
396			recorder.record(getRepeatCount(),readNextChar);
397
398		if(getRepeatCount() != 1)
399		{
400			try
401			{
402				buffer.beginCompoundEdit();
403
404				BeanShell.eval(view,"for(int i = 1; i < "
405					+ getRepeatCount() + "; i++)\n{\n"
406					+ readNextChar + "\n}",false);
407			}
408			finally
409			{
410				buffer.endCompoundEdit();
411			}
412		}
413		else
414			BeanShell.eval(view,readNextChar,false);
415
416		readNextChar = null;
417
418		view.getStatus().setMessage(null);
419	} //}}}
420
421	//}}}
422}