/jEdit/tags/jedit-4-0-pre3/org/gjt/sp/jedit/gui/InputHandler.java
Java | 400 lines | 212 code | 52 blank | 136 comment | 40 complexity | 7aab0cb85c103cb34df44604946791f9 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
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 3917 2001-11-25 03:42:16Z 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 //{{{ addKeyBinding() method
61 /**
62 * Adds a key binding to this input handler.
63 * @param keyBinding The key binding (the format of this is
64 * input-handler specific)
65 * @param action The action
66 */
67 public abstract void addKeyBinding(String keyBinding, EditAction action);
68 //}}}
69
70 //{{{ removeKeyBinding() method
71 /**
72 * Removes a key binding from this input handler.
73 * @param keyBinding The key binding
74 */
75 public abstract void removeKeyBinding(String keyBinding);
76 //}}}
77
78 //{{{ removeAllKeyBindings() method
79 /**
80 * Removes all key bindings from this input handler.
81 */
82 public abstract void removeAllKeyBindings();
83 //}}}
84
85 //{{{ isPrefixActive() method
86 /**
87 * Returns if a prefix key has been pressed.
88 */
89 public boolean isPrefixActive()
90 {
91 return false;
92 } //}}}
93
94 //{{{ isRepeatEnabled() method
95 /**
96 * Returns if repeating is enabled. When repeating is enabled,
97 * actions will be executed multiple times. This is usually
98 * invoked with a special key stroke in the input handler.
99 */
100 public boolean isRepeatEnabled()
101 {
102 return repeat;
103 } //}}}
104
105 //{{{ setRepeatEnabled() method
106 /**
107 * Enables repeating. When repeating is enabled, actions will be
108 * executed multiple times. Once repeating is enabled, the input
109 * handler should read a number from the keyboard.
110 */
111 public void setRepeatEnabled(boolean repeat)
112 {
113 boolean oldRepeat = this.repeat;
114 this.repeat = repeat;
115 repeatCount = 0;
116 if(oldRepeat != repeat)
117 view.getStatus().setMessage(null);
118 } //}}}
119
120 //{{{ getRepeatCount() method
121 /**
122 * Returns the number of times the next action will be repeated.
123 */
124 public int getRepeatCount()
125 {
126 return (repeat && repeatCount > 0 ? repeatCount : 1);
127 } //}}}
128
129 //{{{ setRepeatCount() method
130 /**
131 * Sets the number of times the next action will be repeated.
132 * @param repeatCount The repeat count
133 */
134 public void setRepeatCount(int repeatCount)
135 {
136 boolean oldRepeat = this.repeat;
137 repeat = true;
138 this.repeatCount = repeatCount;
139 if(oldRepeat != repeat)
140 view.getStatus().setMessage(null);
141 } //}}}
142
143 //{{{ getLastAction() method
144 /**
145 * Returns the last executed action.
146 * @since jEdit 2.5pre5
147 */
148 public EditAction getLastAction()
149 {
150 return lastAction;
151 } //}}}
152
153 //{{{ getLastActionCount() method
154 /**
155 * Returns the number of times the last action was executed.
156 * @since jEdit 2.5pre5
157 */
158 public int getLastActionCount()
159 {
160 return lastActionCount;
161 } //}}}
162
163 //{{{ readNextChar() method
164 /**
165 * Invokes the specified BeanShell code, replacing __char__ in the
166 * code with the next input character.
167 * @param msg The prompt to display in the status bar
168 * @param code The code
169 * @since jEdit 3.2pre2
170 */
171 public void readNextChar(String msg, String code)
172 {
173 view.getStatus().setMessage(msg);
174 readNextChar(code);
175 } //}}}
176
177 //{{{ readNextChar() method
178 /**
179 * @deprecated Use the other form of this method instead
180 */
181 public void readNextChar(String code)
182 {
183 readNextChar = code;
184 } //}}}
185
186 //{{{ resetLastActionCount() method
187 /**
188 * Resets the last action count. This should be called when an
189 * editing operation that is not an action is invoked, for example
190 * a mouse click.
191 * @since jEdit 4.0pre1
192 */
193 public void resetLastActionCount()
194 {
195 lastAction = null;
196 lastActionCount = 0;
197 } //}}}
198
199 //{{{ invokeAction() method
200 /**
201 * Invokes the specified action, repeating and recording it as
202 * necessary.
203 * @param action The action
204 * @param source The event source
205 */
206 public void invokeAction(EditAction action)
207 {
208 Buffer buffer = view.getBuffer();
209
210 if(buffer.insideCompoundEdit())
211 buffer.endCompoundEdit();
212
213 // remember the last executed action
214 if(lastAction == action)
215 lastActionCount++;
216 else
217 {
218 lastAction = action;
219 lastActionCount = 1;
220 }
221
222 // remember old values, in case action changes them
223 boolean _repeat = repeat;
224 int _repeatCount = getRepeatCount();
225
226 // execute the action
227 if(action.noRepeat() || _repeatCount == 1)
228 action.invoke(view);
229 else
230 {
231 // stop people doing dumb stuff like C+ENTER 100 C+n
232 if(_repeatCount > REPEAT_COUNT_THRESHOLD)
233 {
234 String label = jEdit.getProperty(action.getLabel());
235 if(label == null)
236 label = action.getName();
237 else
238 label = GUIUtilities.prettifyMenuLabel(label);
239
240 Object[] pp = { label, new Integer(_repeatCount) };
241
242 if(GUIUtilities.confirm(view,"large-repeat-count",pp,
243 JOptionPane.WARNING_MESSAGE,
244 JOptionPane.YES_NO_OPTION)
245 != JOptionPane.YES_OPTION)
246 {
247 repeat = false;
248 repeatCount = 0;
249 view.getStatus().setMessage(null);
250 return;
251 }
252 }
253
254 try
255 {
256 buffer.beginCompoundEdit();
257
258 for(int i = 0; i < _repeatCount; i++)
259 action.invoke(view);
260 }
261 finally
262 {
263 buffer.endCompoundEdit();
264 }
265 }
266
267 Macros.Recorder recorder = view.getMacroRecorder();
268
269 if(recorder != null && !action.noRecord())
270 recorder.record(_repeatCount,action.getCode());
271
272 // If repeat was true originally, clear it
273 // Otherwise it might have been set by the action, etc
274 if(_repeat)
275 {
276 // first of all, if this action set a
277 // readNextChar, do not clear the repeat
278 if(readNextChar != null)
279 return;
280
281 repeat = false;
282 repeatCount = 0;
283 view.getStatus().setMessage(null);
284 }
285 } //}}}
286
287 //{{{ Protected members
288 private static final int REPEAT_COUNT_THRESHOLD = 20;
289
290 //{{{ Instance variables
291 protected View view;
292 protected boolean repeat;
293 protected int repeatCount;
294
295 protected EditAction lastAction;
296 protected int lastActionCount;
297
298 protected String readNextChar;
299 //}}}
300
301 //{{{ userInput() method
302 protected void userInput(char ch)
303 {
304 lastAction = null;
305
306 if(readNextChar != null)
307 invokeReadNextChar(ch);
308 else
309 {
310 JEditTextArea textArea = view.getTextArea();
311
312 Buffer buffer = view.getBuffer();
313 if(!buffer.insideCompoundEdit())
314 buffer.beginCompoundEdit();
315
316 int _repeatCount = getRepeatCount();
317 if(_repeatCount == 1)
318 textArea.userInput(ch);
319 else
320 {
321 // stop people doing dumb stuff like C+ENTER 100 C+n
322 if(_repeatCount > REPEAT_COUNT_THRESHOLD)
323 {
324 Object[] pp = { String.valueOf(ch),
325 new Integer(_repeatCount) };
326
327 if(GUIUtilities.confirm(view,
328 "large-repeat-count.user-input",pp,
329 JOptionPane.WARNING_MESSAGE,
330 JOptionPane.YES_NO_OPTION)
331 != JOptionPane.YES_OPTION)
332 {
333 repeat = false;
334 repeatCount = 0;
335 view.getStatus().setMessage(null);
336 return;
337 }
338 }
339
340 for(int i = 0; i < _repeatCount; i++)
341 textArea.userInput(ch);
342 }
343
344 Macros.Recorder recorder = view.getMacroRecorder();
345
346 if(recorder != null)
347 recorder.record(_repeatCount,ch);
348 }
349
350 setRepeatEnabled(false);
351 } //}}}
352
353 //{{{ invokeReadNextChar() method
354 protected void invokeReadNextChar(char ch)
355 {
356 Buffer buffer = view.getBuffer();
357
358 if(buffer.insideCompoundEdit())
359 buffer.endCompoundEdit();
360
361 String charStr = MiscUtilities.charsToEscapes(String.valueOf(ch));
362
363 // this might be a bit slow if __char__ occurs a lot
364 int index;
365 while((index = readNextChar.indexOf("__char__")) != -1)
366 {
367 readNextChar = readNextChar.substring(0,index)
368 + '\'' + charStr + '\''
369 + readNextChar.substring(index + 8);
370 }
371
372 Macros.Recorder recorder = view.getMacroRecorder();
373 if(recorder != null)
374 recorder.record(getRepeatCount(),readNextChar);
375
376 if(getRepeatCount() != 1)
377 {
378 try
379 {
380 buffer.beginCompoundEdit();
381
382 BeanShell.eval(view,"for(int i = 1; i < "
383 + getRepeatCount() + "; i++)\n{\n"
384 + readNextChar + "\n}",false);
385 }
386 finally
387 {
388 buffer.endCompoundEdit();
389 }
390 }
391 else
392 BeanShell.eval(view,readNextChar,false);
393
394 readNextChar = null;
395
396 view.getStatus().setMessage(null);
397 } //}}}
398
399 //}}}
400}