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 (modifiers & KeyEvent.CTRL_MASK) == 0 &&
233 (modifiers & KeyEvent.META_MASK) == 0)
234 {
235 if(c >= 0x20 && c != 0x7f)
236 {
237 KeyStroke keyStroke = KeyStroke.getKeyStroke(
238 Character.toUpperCase(c));
239 Object o = currentBindings.get(keyStroke);
240
241 if(o instanceof Hashtable)
242 {
243 currentBindings = (Hashtable)o;
244 return;
245 }
246 else if(o instanceof ActionListener)
247 {
248 currentBindings = bindings;
249 executeAction((ActionListener)o,
250 evt.getSource(),
251 String.valueOf(c));
252 return;
253 }
254
255 currentBindings = bindings;
256
257 if(grabAction != null)
258 {
259 handleGrabAction(evt);
260 return;
261 }
262
263
264 if(repeat && Character.isDigit(c))
265 {
266 repeatCount *= 10;
267 repeatCount += (c - '0');
268 return;
269 }
270
271 executeAction(INSERT_CHAR,evt.getSource(),
272 String.valueOf(evt.getKeyChar()));
273
274 repeatCount = 0;
275 repeat = false;
276 }
277 }
278 }
279
280 /***
281 * Converts a string to a keystroke. The string should be of the
282 * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
283 * is any combination of A for Alt, C for Control, S for Shift
284 * or M for Meta, and <i>shortcut</i> is either a single character,
285 * or a keycode name from the <code>KeyEvent</code> class, without
286 * the <code>VK_</code> prefix.
287 * @param keyStroke A string description of the key stroke
288 */
289 public static KeyStroke parseKeyStroke(String keyStroke)
290 {
291 if(keyStroke == null)
292 return null;
293 int modifiers = 0;
294 int index = keyStroke.indexOf('+');
295 if(index != -1)
296 {
297 for(int i = 0; i < index; i++)
298 {
299 switch(Character.toUpperCase(keyStroke
300 .charAt(i)))
301 {
302 case 'A':
303 modifiers |= InputEvent.ALT_MASK;
304 break;
305 case 'C':
306 modifiers |= Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
307 break;
308 case 'M':
309 modifiers |= InputEvent.META_MASK;
310 break;
311 case 'S':
312 modifiers |= InputEvent.SHIFT_MASK;
313 break;
314 }
315 }
316 }
317 String key = keyStroke.substring(index + 1);
318 if(key.length() == 1)
319 {
320 char ch = Character.toUpperCase(key.charAt(0));
321 if(modifiers == 0)
322 return KeyStroke.getKeyStroke(ch);
323 else
324 return KeyStroke.getKeyStroke(ch,modifiers);
325 }
326 else if(key.length() == 0)
327 {
328 System.err.println("Invalid key stroke: " + keyStroke);
329 return null;
330 }
331 else
332 {
333 int ch;
334
335 try
336 {
337 ch = KeyEvent.class.getField("VK_".concat(key))
338 .getInt(null);
339 }
340 catch(Exception e)
341 {
342 System.err.println("Invalid key stroke: "
343 + keyStroke);
344 return null;
345 }
346
347 return KeyStroke.getKeyStroke(ch,modifiers);
348 }
349 }
350
351
352 private Hashtable bindings;
353 private Hashtable currentBindings;
354
355 private DefaultInputHandler(DefaultInputHandler copy)
356 {
357 bindings = currentBindings = copy.bindings;
358 }
359 }