1
2
3
4
5
6
7
8
9
10 package org.syntax.jedit;
11
12 import java.awt.Toolkit;
13 import java.awt.event.ActionListener;
14 import java.awt.event.InputEvent;
15 import java.awt.event.KeyEvent;
16 import java.util.Hashtable;
17 import java.util.StringTokenizer;
18
19 import javax.swing.KeyStroke;
20
21 /***
22 * The default input handler. It maps sequences of keystrokes into actions
23 * and inserts key typed events into the text area.
24 * @author Slava Pestov
25 * @version $Id$
26 */
27 public class DefaultInputHandler extends InputHandler
28 {
29 /***
30 * Creates a new input handler with no key bindings defined.
31 */
32 public DefaultInputHandler()
33 {
34 bindings = currentBindings = new Hashtable();
35 }
36
37 /***
38 * Sets up the default key bindings.
39 */
40 public void addDefaultKeyBindings()
41 {
42 addKeyBinding("BACK_SPACE",BACKSPACE);
43 addKeyBinding("C+BACK_SPACE",BACKSPACE_WORD);
44 addKeyBinding("DELETE",DELETE);
45 addKeyBinding("C+DELETE",DELETE_WORD);
46
47 addKeyBinding("ENTER",INSERT_BREAK);
48 addKeyBinding("TAB",INSERT_TAB);
49
50 addKeyBinding("INSERT",OVERWRITE);
51 addKeyBinding("C+//",TOGGLE_RECT);
52
53 addKeyBinding("HOME",HOME);
54 addKeyBinding("END",END);
55 addKeyBinding("C+A",SELECT_ALL);
56 addKeyBinding("S+HOME",SELECT_HOME);
57 addKeyBinding("S+END",SELECT_END);
58 addKeyBinding("C+HOME",DOCUMENT_HOME);
59 addKeyBinding("C+END",DOCUMENT_END);
60 addKeyBinding("CS+HOME",SELECT_DOC_HOME);
61 addKeyBinding("CS+END",SELECT_DOC_END);
62
63 addKeyBinding("PAGE_UP",PREV_PAGE);
64 addKeyBinding("PAGE_DOWN",NEXT_PAGE);
65 addKeyBinding("S+PAGE_UP",SELECT_PREV_PAGE);
66 addKeyBinding("S+PAGE_DOWN",SELECT_NEXT_PAGE);
67
68 addKeyBinding("LEFT",PREV_CHAR);
69 addKeyBinding("S+LEFT",SELECT_PREV_CHAR);
70 addKeyBinding("C+LEFT",PREV_WORD);
71 addKeyBinding("CS+LEFT",SELECT_PREV_WORD);
72 addKeyBinding("RIGHT",NEXT_CHAR);
73 addKeyBinding("S+RIGHT",SELECT_NEXT_CHAR);
74 addKeyBinding("C+RIGHT",NEXT_WORD);
75 addKeyBinding("CS+RIGHT",SELECT_NEXT_WORD);
76 addKeyBinding("UP",PREV_LINE);
77 addKeyBinding("S+UP",SELECT_PREV_LINE);
78 addKeyBinding("DOWN",NEXT_LINE);
79 addKeyBinding("S+DOWN",SELECT_NEXT_LINE);
80
81 addKeyBinding("C+ENTER",REPEAT);
82
83
84 addKeyBinding("C+C", CLIP_COPY);
85 addKeyBinding("C+V", CLIP_PASTE);
86 addKeyBinding("C+X", CLIP_CUT);
87 }
88
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, ActionListener action)
99 {
100 Hashtable current = bindings;
101
102 StringTokenizer st = new StringTokenizer(keyBinding);
103 while(st.hasMoreTokens())
104 {
105 KeyStroke keyStroke = parseKeyStroke(st.nextToken());
106 if(keyStroke == null)
107 return;
108
109 if(st.hasMoreTokens())
110 {
111 Object o = current.get(keyStroke);
112 if(o instanceof Hashtable)
113 current = (Hashtable)o;
114 else
115 {
116 o = new Hashtable();
117 current.put(keyStroke,o);
118 current = (Hashtable)o;
119 }
120 }
121 else
122 current.put(keyStroke,action);
123 }
124 }
125
126 /***
127 * Removes a key binding from this input handler. This is not yet
128 * implemented.
129 * @param keyBinding The key binding
130 */
131 public void removeKeyBinding(String keyBinding)
132 {
133 throw new InternalError("Not yet implemented");
134 }
135
136 /***
137 * Removes all key bindings from this input handler.
138 */
139 public void removeAllKeyBindings()
140 {
141 bindings.clear();
142 }
143
144 /***
145 * Returns a copy of this input handler that shares the same
146 * key bindings. Setting key bindings in the copy will also
147 * set them in the original.
148 */
149 public InputHandler copy()
150 {
151 return new DefaultInputHandler(this);
152 }
153
154 /***
155 * Handle a key pressed event. This will look up the binding for
156 * the key stroke and execute it.
157 */
158 public void keyPressed(KeyEvent evt)
159 {
160 int keyCode = evt.getKeyCode();
161 int modifiers = evt.getModifiers();
162
163 if(keyCode == KeyEvent.VK_CONTROL ||
164 keyCode == KeyEvent.VK_SHIFT ||
165 keyCode == KeyEvent.VK_ALT ||
166 keyCode == KeyEvent.VK_META)
167 return;
168
169 if((modifiers & ~KeyEvent.SHIFT_MASK) != 0
170 || evt.isActionKey()
171 || keyCode == KeyEvent.VK_BACK_SPACE
172 || keyCode == KeyEvent.VK_DELETE
173 || keyCode == KeyEvent.VK_ENTER
174 || keyCode == KeyEvent.VK_TAB
175 || keyCode == KeyEvent.VK_ESCAPE)
176 {
177 if(grabAction != null)
178 {
179 handleGrabAction(evt);
180 return;
181 }
182
183 KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
184 modifiers);
185 Object o = currentBindings.get(keyStroke);
186 if(o == null)
187 {
188
189
190
191
192 if(currentBindings != bindings)
193 {
194 Toolkit.getDefaultToolkit().beep();
195
196
197 repeatCount = 0;
198 repeat = false;
199 evt.consume();
200 }
201 currentBindings = bindings;
202 return;
203 }
204 else if(o instanceof ActionListener)
205 {
206 currentBindings = bindings;
207
208 executeAction(((ActionListener)o),
209 evt.getSource(),null);
210
211 evt.consume();
212 return;
213 }
214 else if(o instanceof Hashtable)
215 {
216 currentBindings = (Hashtable)o;
217 evt.consume();
218 return;
219 }
220 }
221 }
222
223 /***
224 * Handle a key typed event. This inserts the key into the text area.
225 */
226 public void keyTyped(KeyEvent evt)
227 {
228 int modifiers = evt.getModifiers();
229 char c = evt.getKeyChar();
230 if(c != KeyEvent.CHAR_UNDEFINED &&
231 (modifiers & KeyEvent.ALT_MASK) == 0)
232 {
233 if(c >= 0x20 && c != 0x7f)
234 {
235 KeyStroke keyStroke = KeyStroke.getKeyStroke(
236 Character.toUpperCase(c));
237 Object o = currentBindings.get(keyStroke);
238
239 if(o instanceof Hashtable)
240 {
241 currentBindings = (Hashtable)o;
242 return;
243 }
244 else if(o instanceof ActionListener)
245 {
246 currentBindings = bindings;
247 executeAction((ActionListener)o,
248 evt.getSource(),
249 String.valueOf(c));
250 return;
251 }
252
253 currentBindings = bindings;
254
255 if(grabAction != null)
256 {
257 handleGrabAction(evt);
258 return;
259 }
260
261
262 if(repeat && Character.isDigit(c))
263 {
264 repeatCount *= 10;
265 repeatCount += (c - '0');
266 return;
267 }
268
269 executeAction(INSERT_CHAR,evt.getSource(),
270 String.valueOf(evt.getKeyChar()));
271
272 repeatCount = 0;
273 repeat = false;
274 }
275 }
276 }
277
278 /***
279 * Converts a string to a keystroke. The string should be of the
280 * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
281 * is any combination of A for Alt, C for Control, S for Shift
282 * or M for Meta, and <i>shortcut</i> is either a single character,
283 * or a keycode name from the <code>KeyEvent</code> class, without
284 * the <code>VK_</code> prefix.
285 * @param keyStroke A string description of the key stroke
286 */
287 public static KeyStroke parseKeyStroke(String keyStroke)
288 {
289 if(keyStroke == null)
290 return null;
291 int modifiers = 0;
292 int index = keyStroke.indexOf('+');
293 if(index != -1)
294 {
295 for(int i = 0; i < index; i++)
296 {
297 switch(Character.toUpperCase(keyStroke
298 .charAt(i)))
299 {
300 case 'A':
301 modifiers |= InputEvent.ALT_MASK;
302 break;
303 case 'C':
304 modifiers |= InputEvent.CTRL_MASK;
305 break;
306 case 'M':
307 modifiers |= InputEvent.META_MASK;
308 break;
309 case 'S':
310 modifiers |= InputEvent.SHIFT_MASK;
311 break;
312 }
313 }
314 }
315 String key = keyStroke.substring(index + 1);
316 if(key.length() == 1)
317 {
318 char ch = Character.toUpperCase(key.charAt(0));
319 if(modifiers == 0)
320 return KeyStroke.getKeyStroke(ch);
321 else
322 return KeyStroke.getKeyStroke(ch,modifiers);
323 }
324 else if(key.length() == 0)
325 {
326 System.err.println("Invalid key stroke: " + keyStroke);
327 return null;
328 }
329 else
330 {
331 int ch;
332
333 try
334 {
335 ch = KeyEvent.class.getField("VK_".concat(key))
336 .getInt(null);
337 }
338 catch(Exception e)
339 {
340 System.err.println("Invalid key stroke: "
341 + keyStroke);
342 return null;
343 }
344
345 return KeyStroke.getKeyStroke(ch,modifiers);
346 }
347 }
348
349
350 private Hashtable bindings;
351 private Hashtable currentBindings;
352
353 private DefaultInputHandler(DefaultInputHandler copy)
354 {
355 bindings = currentBindings = copy.bindings;
356 }
357 }