View Javadoc

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