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