Source code: org/gjt/sp/jedit/gui/DefaultInputHandler.java
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, 2003 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
23 package org.gjt.sp.jedit.gui;
24
25 //{{{ Imports
26 import javax.swing.KeyStroke;
27 import java.awt.event.KeyEvent;
28 import java.awt.event.InputEvent;
29 import java.awt.Toolkit;
30 import java.util.Hashtable;
31 import java.util.StringTokenizer;
32 import org.gjt.sp.jedit.*;
33 import org.gjt.sp.util.Log;
34 //}}}
35
36 /**
37 * The default input handler. It maps sequences of keystrokes into actions
38 * and inserts key typed events into the text area.
39 * @author Slava Pestov
40 * @version $Id: DefaultInputHandler.java,v 1.37 2003/08/28 22:05:24 spestov Exp $
41 */
42 public class DefaultInputHandler extends InputHandler
43 {
44 //{{{ DefaultInputHandler constructor
45 /**
46 * Creates a new input handler with no key bindings defined.
47 * @param view The view
48 */
49 public DefaultInputHandler(View view)
50 {
51 super(view);
52
53 bindings = currentBindings = new Hashtable();
54 } //}}}
55
56 //{{{ DefaultInputHandler constructor
57 /**
58 * Creates a new input handler with the same set of key bindings
59 * as the one specified. Note that both input handlers share
60 * a pointer to exactly the same key binding table; so adding
61 * a key binding in one will also add it to the other.
62 * @param copy The input handler to copy key bindings from
63 * @param view The view
64 */
65 public DefaultInputHandler(View view, DefaultInputHandler copy)
66 {
67 super(view);
68
69 bindings = currentBindings = copy.bindings;
70 } //}}}
71
72 //{{{ addKeyBinding() method
73 /**
74 * Adds a key binding to this input handler. The key binding is
75 * a list of white space separated key strokes of the form
76 * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
77 * or S for Shift, and key is either a character (a-z) or a field
78 * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
79 * @param keyBinding The key binding
80 * @param action The action
81 * @since jEdit 4.2pre1
82 */
83 public void addKeyBinding(String keyBinding, String action)
84 {
85 _addKeyBinding(keyBinding,(Object)action);
86 } //}}}
87
88 //{{{ addKeyBinding() method
89 /**
90 * Adds a key binding to this input handler. The key binding is
91 * a list of white space separated key strokes of the form
92 * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
93 * or S for Shift, and key is either a character (a-z) or a field
94 * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
95 * @param keyBinding The key binding
96 * @param action The action
97 */
98 public void addKeyBinding(String keyBinding, EditAction action)
99 {
100 _addKeyBinding(keyBinding,(Object)action);
101 } //}}}
102
103 //{{{ removeKeyBinding() method
104 /**
105 * Removes a key binding from this input handler. This is not yet
106 * implemented.
107 * @param keyBinding The key binding
108 */
109 public void removeKeyBinding(String keyBinding)
110 {
111 Hashtable current = bindings;
112
113 StringTokenizer st = new StringTokenizer(keyBinding);
114 while(st.hasMoreTokens())
115 {
116 String keyCodeStr = st.nextToken();
117 KeyEventTranslator.Key keyStroke = KeyEventTranslator.parseKey(keyCodeStr);
118 if(keyStroke == null)
119 return;
120
121 if(st.hasMoreTokens())
122 {
123 Object o = current.get(keyStroke);
124 if(o instanceof Hashtable)
125 current = ((Hashtable)o);
126 else if(o != null)
127 {
128 // we have binding foo
129 // but user asks to remove foo bar?
130 current.remove(keyStroke);
131 return;
132 }
133 else
134 {
135 // user asks to remove non-existent
136 return;
137 }
138 }
139 else
140 current.remove(keyStroke);
141 }
142 } //}}}
143
144 //{{{ removeAllKeyBindings() method
145 /**
146 * Removes all key bindings from this input handler.
147 */
148 public void removeAllKeyBindings()
149 {
150 bindings.clear();
151 } //}}}
152
153 //{{{ getKeyBinding() method
154 /**
155 * Returns either an edit action, or a hashtable if the specified key
156 * is a prefix.
157 * @param keyBinding The key binding
158 * @since jEdit 3.2pre5
159 */
160 public Object getKeyBinding(String keyBinding)
161 {
162 Hashtable current = bindings;
163 StringTokenizer st = new StringTokenizer(keyBinding);
164
165 while(st.hasMoreTokens())
166 {
167 KeyEventTranslator.Key keyStroke = KeyEventTranslator.parseKey(keyBinding);
168 if(keyStroke == null)
169 return null;
170
171 if(st.hasMoreTokens())
172 {
173 Object o = current.get(keyStroke);
174 if(o instanceof Hashtable)
175 current = (Hashtable)o;
176 else
177 return o;
178 }
179 else
180 {
181 return current.get(keyStroke);
182 }
183 }
184
185 return null;
186 } //}}}
187
188 //{{{ isPrefixActive() method
189 /**
190 * Returns if a prefix key has been pressed.
191 */
192 public boolean isPrefixActive()
193 {
194 return bindings != currentBindings;
195 } //}}}
196
197 //{{{ handleKey() method
198 /**
199 * Handles the given keystroke.
200 * @param keyStroke The key stroke
201 * @since jEdit 4.2pre5
202 */
203 public boolean handleKey(KeyEventTranslator.Key keyStroke)
204 {
205 char input = '\0';
206 if(keyStroke.modifiers == null
207 || keyStroke.modifiers.equals("S"))
208 {
209 switch(keyStroke.key)
210 {
211 case '\n':
212 case '\t':
213 input = (char)keyStroke.key;
214 break;
215 default:
216 input = keyStroke.input;
217 break;
218 }
219 }
220
221 if(readNextChar != null)
222 {
223 if(input != '\0')
224 {
225 setCurrentBindings(bindings);
226 invokeReadNextChar(input);
227 repeatCount = 1;
228 return true;
229 }
230 else
231 {
232 readNextChar = null;
233 view.getStatus().setMessage(null);
234 }
235 }
236
237 Object o = currentBindings.get(keyStroke);
238 if(o == null)
239 {
240 // Don't beep if the user presses some
241 // key we don't know about unless a
242 // prefix is active. Otherwise it will
243 // beep when caps lock is pressed, etc.
244 if(currentBindings != bindings)
245 {
246 Toolkit.getDefaultToolkit().beep();
247 // F10 should be passed on, but C+e F10
248 // shouldn't
249 repeatCount = 1;
250 setCurrentBindings(bindings);
251 }
252
253 if(input != '\0')
254 userInput(input);
255 else
256 {
257 // this is retarded. excuse me while I drool
258 // and make stupid noises
259 switch(keyStroke.key)
260 {
261 case KeyEvent.VK_NUMPAD0:
262 case KeyEvent.VK_NUMPAD1:
263 case KeyEvent.VK_NUMPAD2:
264 case KeyEvent.VK_NUMPAD3:
265 case KeyEvent.VK_NUMPAD4:
266 case KeyEvent.VK_NUMPAD5:
267 case KeyEvent.VK_NUMPAD6:
268 case KeyEvent.VK_NUMPAD7:
269 case KeyEvent.VK_NUMPAD8:
270 case KeyEvent.VK_NUMPAD9:
271 case KeyEvent.VK_MULTIPLY:
272 case KeyEvent.VK_ADD:
273 /* case KeyEvent.VK_SEPARATOR: */
274 case KeyEvent.VK_SUBTRACT:
275 case KeyEvent.VK_DECIMAL:
276 case KeyEvent.VK_DIVIDE:
277 KeyEventWorkaround.numericKeypadKey();
278 break;
279 }
280 }
281 }
282 else if(o instanceof Hashtable)
283 {
284 setCurrentBindings((Hashtable)o);
285 return true;
286 }
287 else if(o instanceof String)
288 {
289 setCurrentBindings(bindings);
290 invokeAction((String)o);
291 return true;
292 }
293 else if(o instanceof EditAction)
294 {
295 setCurrentBindings(bindings);
296 invokeAction((EditAction)o);
297 return true;
298 }
299
300 return false;
301 } //}}}
302
303 //{{{ getSymbolicModifierName() method
304 /**
305 * Returns a the symbolic modifier name for the specified Java modifier
306 * flag.
307 *
308 * @param mod A modifier constant from <code>InputEvent</code>
309 *
310 * @since jEdit 4.1pre3
311 */
312 public static char getSymbolicModifierName(int mod)
313 {
314 return KeyEventTranslator.getSymbolicModifierName(mod);
315 } //}}}
316
317 //{{{ getModifierString() method
318 /**
319 * Returns a string containing symbolic modifier names set in the
320 * specified event.
321 *
322 * @param evt The event
323 *
324 * @since jEdit 4.1pre3
325 */
326 public static String getModifierString(InputEvent evt)
327 {
328 return KeyEventTranslator.getModifierString(evt);
329 } //}}}
330
331 //{{{ parseKeyStroke() method
332 /**
333 * @deprecated We don't use Swing KeyStrokes anymore.
334 */
335 public static KeyStroke parseKeyStroke(String keyStroke)
336 {
337 if(keyStroke == null)
338 return null;
339 int modifiers = 0;
340 int index = keyStroke.indexOf('+');
341 if(index != -1)
342 {
343 for(int i = 0; i < index; i++)
344 {
345 switch(Character.toUpperCase(keyStroke
346 .charAt(i)))
347 {
348 case 'A':
349 modifiers |= KeyEventTranslator.a;
350 break;
351 case 'C':
352 modifiers |= KeyEventTranslator.c;
353 break;
354 case 'M':
355 modifiers |= KeyEventTranslator.m;
356 break;
357 case 'S':
358 modifiers |= KeyEventTranslator.s;
359 break;
360 }
361 }
362 }
363 String key = keyStroke.substring(index + 1);
364 if(key.length() == 1)
365 {
366 char ch = key.charAt(0);
367 if(modifiers == 0)
368 return KeyStroke.getKeyStroke(ch);
369 else
370 {
371 return KeyStroke.getKeyStroke(Character.toUpperCase(ch),
372 modifiers);
373 }
374 }
375 else if(key.length() == 0)
376 {
377 Log.log(Log.ERROR,DefaultInputHandler.class,
378 "Invalid key stroke: " + keyStroke);
379 return null;
380 }
381 else
382 {
383 int ch;
384
385 try
386 {
387 ch = KeyEvent.class.getField("VK_".concat(key))
388 .getInt(null);
389 }
390 catch(Exception e)
391 {
392 Log.log(Log.ERROR,DefaultInputHandler.class,
393 "Invalid key stroke: "
394 + keyStroke);
395 return null;
396 }
397
398 return KeyStroke.getKeyStroke(ch,modifiers);
399 }
400 } //}}}
401
402 //{{{ Private members
403
404 // Stores prefix name in bindings hashtable
405 private static Object PREFIX_STR = "PREFIX_STR";
406
407 private Hashtable bindings;
408 private Hashtable currentBindings;
409
410 //{{{ setCurrentBindings() method
411 private void setCurrentBindings(Hashtable bindings)
412 {
413 view.getStatus().setMessage((String)bindings.get(PREFIX_STR));
414 currentBindings = bindings;
415 } //}}}
416
417 //{{{ _addKeyBinding() method
418 /**
419 * Adds a key binding to this input handler. The key binding is
420 * a list of white space separated key strokes of the form
421 * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
422 * or S for Shift, and key is either a character (a-z) or a field
423 * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
424 * @param keyBinding The key binding
425 * @param action The action
426 */
427 public void _addKeyBinding(String keyBinding, Object action)
428 {
429 Hashtable current = bindings;
430
431 String prefixStr = null;
432
433 StringTokenizer st = new StringTokenizer(keyBinding);
434 while(st.hasMoreTokens())
435 {
436 String keyCodeStr = st.nextToken();
437 if(prefixStr == null)
438 prefixStr = keyCodeStr;
439 else
440 prefixStr = prefixStr + " " + keyCodeStr;
441
442 KeyEventTranslator.Key keyStroke = KeyEventTranslator.parseKey(keyCodeStr);
443 if(keyStroke == null)
444 return;
445
446 if(st.hasMoreTokens())
447 {
448 Object o = current.get(keyStroke);
449 if(o instanceof Hashtable)
450 current = (Hashtable)o;
451 else
452 {
453 Hashtable hash = new Hashtable();
454 hash.put(PREFIX_STR,prefixStr);
455 o = hash;
456 current.put(keyStroke,o);
457 current = (Hashtable)o;
458 }
459 }
460 else
461 current.put(keyStroke,action);
462 }
463 } //}}}
464
465 //}}}
466 }