001: package net.xoetrope.builder.editor.syntaxhighlight;
002:
003: /*
004: * DefaultInputHandler.java - Default implementation of an input handler
005: * Copyright (C) 1999 Slava Pestov
006: *
007: * You may use and modify this package for any purpose. Redistribution is
008: * permitted, in both source and binary form, provided that this notice
009: * remains intact in all source distributions of this package.
010: */
011: import java.util.Hashtable;
012: import java.util.StringTokenizer;
013:
014: import java.awt.Toolkit;
015: import java.awt.event.ActionListener;
016: import java.awt.event.InputEvent;
017: import java.awt.event.KeyEvent;
018: import javax.swing.KeyStroke;
019:
020: /**
021: * The default input handler. It maps sequences of keystrokes into actions
022: * and inserts key typed events into the text area.
023: * @author Slava Pestov
024: * @version $Id: DefaultInputHandler.java,v 1.22 2005/01/05 17:20:48 luano Exp $
025: */
026: public class DefaultInputHandler extends InputHandler {
027: /**
028: * Creates a new input handler with no key bindings defined.
029: */
030: public DefaultInputHandler() {
031: bindings = currentBindings = new Hashtable();
032: }
033:
034: /**
035: * Sets up the default key bindings.
036: */
037: public void addDefaultKeyBindings() {
038: addKeyBinding("BACK_SPACE", BACKSPACE);
039: addKeyBinding("C+BACK_SPACE", BACKSPACE_WORD);
040: addKeyBinding("DELETE", DELETE);
041: addKeyBinding("C+DELETE", DELETE_WORD);
042:
043: addKeyBinding("ENTER", INSERT_BREAK);
044: addKeyBinding("TAB", INSERT_TAB);
045:
046: addKeyBinding("INSERT", OVERWRITE);
047: addKeyBinding("C+\\", TOGGLE_RECT);
048:
049: addKeyBinding("HOME", HOME);
050: addKeyBinding("END", END);
051: addKeyBinding("S+HOME", SELECT_HOME);
052: addKeyBinding("S+END", SELECT_END);
053: addKeyBinding("C+HOME", DOCUMENT_HOME);
054: addKeyBinding("C+END", DOCUMENT_END);
055: addKeyBinding("CS+HOME", SELECT_DOC_HOME);
056: addKeyBinding("CS+END", SELECT_DOC_END);
057:
058: addKeyBinding("PAGE_UP", PREV_PAGE);
059: addKeyBinding("PAGE_DOWN", NEXT_PAGE);
060: addKeyBinding("S+PAGE_UP", SELECT_PREV_PAGE);
061: addKeyBinding("S+PAGE_DOWN", SELECT_NEXT_PAGE);
062:
063: addKeyBinding("LEFT", PREV_CHAR);
064: addKeyBinding("S+LEFT", SELECT_PREV_CHAR);
065: addKeyBinding("C+LEFT", PREV_WORD);
066: addKeyBinding("CS+LEFT", SELECT_PREV_WORD);
067: addKeyBinding("RIGHT", NEXT_CHAR);
068: addKeyBinding("S+RIGHT", SELECT_NEXT_CHAR);
069: addKeyBinding("C+RIGHT", NEXT_WORD);
070: addKeyBinding("CS+RIGHT", SELECT_NEXT_WORD);
071: addKeyBinding("UP", PREV_LINE);
072: addKeyBinding("S+UP", SELECT_PREV_LINE);
073: addKeyBinding("DOWN", NEXT_LINE);
074: addKeyBinding("S+DOWN", SELECT_NEXT_LINE);
075:
076: addKeyBinding("C+ENTER", REPEAT);
077: }
078:
079: /**
080: * Adds a key binding to this input handler. The key binding is
081: * a list of white space separated key strokes of the form
082: * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
083: * or S for Shift, and key is either a character (a-z) or a field
084: * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
085: * @param keyBinding The key binding
086: * @param action The action
087: */
088: public void addKeyBinding(String keyBinding, ActionListener action) {
089: Hashtable current = bindings;
090:
091: StringTokenizer st = new StringTokenizer(keyBinding);
092: while (st.hasMoreTokens()) {
093: KeyStroke keyStroke = parseKeyStroke(st.nextToken());
094: if (keyStroke == null)
095: return;
096:
097: if (st.hasMoreTokens()) {
098: Object o = current.get(keyStroke);
099: if (o instanceof Hashtable)
100: current = (Hashtable) o;
101: else {
102: o = new Hashtable();
103: current.put(keyStroke, o);
104: current = (Hashtable) o;
105: }
106: } else
107: current.put(keyStroke, action);
108: }
109: }
110:
111: /**
112: * Removes a key binding from this input handler. This is not yet
113: * implemented.
114: * @param keyBinding The key binding
115: */
116: public void removeKeyBinding(String keyBinding) {
117: throw new InternalError("Not yet implemented");
118: }
119:
120: /**
121: * Removes all key bindings from this input handler.
122: */
123: public void removeAllKeyBindings() {
124: bindings.clear();
125: }
126:
127: /**
128: * Returns a copy of this input handler that shares the same
129: * key bindings. Setting key bindings in the copy will also
130: * set them in the original.
131: */
132: public InputHandler copy() {
133: return new DefaultInputHandler(this );
134: }
135:
136: /**
137: * Handle a key pressed event. This will look up the binding for
138: * the key stroke and execute it.
139: */
140: public void keyPressed(KeyEvent evt) {
141: int keyCode = evt.getKeyCode();
142: int modifiers = evt.getModifiers();
143:
144: if (keyCode == KeyEvent.VK_CONTROL
145: || keyCode == KeyEvent.VK_SHIFT
146: || keyCode == KeyEvent.VK_ALT
147: || keyCode == KeyEvent.VK_META)
148: return;
149:
150: if ((modifiers & ~KeyEvent.SHIFT_MASK) != 0
151: || evt.isActionKey()
152: || keyCode == KeyEvent.VK_BACK_SPACE
153: || keyCode == KeyEvent.VK_DELETE
154: || keyCode == KeyEvent.VK_ENTER
155: || keyCode == KeyEvent.VK_TAB
156: || keyCode == KeyEvent.VK_ESCAPE) {
157: if (grabAction != null) {
158: handleGrabAction(evt);
159: return;
160: }
161:
162: KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
163: modifiers);
164: Object o = currentBindings.get(keyStroke);
165: if (o == null) {
166: // Don't beep if the user presses some
167: // key we don't know about unless a
168: // prefix is active. Otherwise it will
169: // beep when caps lock is pressed, etc.
170: if (currentBindings != bindings) {
171: Toolkit.getDefaultToolkit().beep();
172: // F10 should be passed on, but C+e F10
173: // shouldn't
174: repeatCount = 0;
175: repeat = false;
176: evt.consume();
177: }
178: currentBindings = bindings;
179: return;
180: } else if (o instanceof ActionListener) {
181: currentBindings = bindings;
182:
183: executeAction(((ActionListener) o), evt.getSource(),
184: null);
185:
186: evt.consume();
187: return;
188: } else if (o instanceof Hashtable) {
189: currentBindings = (Hashtable) o;
190: evt.consume();
191: return;
192: }
193: }
194: }
195:
196: /**
197: * Handle a key typed event. This inserts the key into the text area.
198: */
199: public void keyTyped(KeyEvent evt) {
200: int modifiers = evt.getModifiers();
201: char c = evt.getKeyChar();
202: if (c != KeyEvent.CHAR_UNDEFINED
203: && (modifiers & KeyEvent.ALT_MASK) == 0) {
204: if (c >= 0x20 && c != 0x7f) {
205: KeyStroke keyStroke = KeyStroke.getKeyStroke(Character
206: .toUpperCase(c));
207: Object o = currentBindings.get(keyStroke);
208:
209: if (o instanceof Hashtable) {
210: currentBindings = (Hashtable) o;
211: return;
212: } else if (o instanceof ActionListener) {
213: currentBindings = bindings;
214: executeAction((ActionListener) o, evt.getSource(),
215: String.valueOf(c));
216: return;
217: }
218:
219: currentBindings = bindings;
220:
221: if (grabAction != null) {
222: handleGrabAction(evt);
223: return;
224: }
225:
226: // 0-9 adds another 'digit' to the repeat number
227: if (repeat && Character.isDigit(c)) {
228: repeatCount *= 10;
229: repeatCount += (c - '0');
230: return;
231: }
232:
233: executeAction(INSERT_CHAR, evt.getSource(), String
234: .valueOf(evt.getKeyChar()));
235:
236: repeatCount = 0;
237: repeat = false;
238: }
239: }
240: }
241:
242: /**
243: * Converts a string to a keystroke. The string should be of the
244: * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
245: * is any combination of A for Alt, C for Control, S for Shift
246: * or M for Meta, and <i>shortcut</i> is either a single character,
247: * or a keycode name from the <code>KeyEvent</code> class, without
248: * the <code>VK_</code> prefix.
249: * @param keyStroke A string description of the key stroke
250: */
251: public static KeyStroke parseKeyStroke(String keyStroke) {
252: if (keyStroke == null)
253: return null;
254: int modifiers = 0;
255: int index = keyStroke.indexOf('+');
256: if (index != -1) {
257: for (int i = 0; i < index; i++) {
258: switch (Character.toUpperCase(keyStroke.charAt(i))) {
259: case 'A':
260: modifiers |= InputEvent.ALT_MASK;
261: break;
262: case 'C':
263: modifiers |= InputEvent.CTRL_MASK;
264: break;
265: case 'M':
266: modifiers |= InputEvent.META_MASK;
267: break;
268: case 'S':
269: modifiers |= InputEvent.SHIFT_MASK;
270: break;
271: }
272: }
273: }
274: String key = keyStroke.substring(index + 1);
275: if (key.length() == 1) {
276: char ch = Character.toUpperCase(key.charAt(0));
277: if (modifiers == 0)
278: return KeyStroke.getKeyStroke(ch);
279: else
280: return KeyStroke.getKeyStroke(ch, modifiers);
281: } else if (key.length() == 0) {
282: System.err.println("Invalid key stroke: " + keyStroke);
283: return null;
284: } else {
285: int ch;
286:
287: try {
288: ch = KeyEvent.class.getField("VK_".concat(key)).getInt(
289: null);
290: } catch (Exception e) {
291: System.err.println("Invalid key stroke: " + keyStroke);
292: return null;
293: }
294:
295: return KeyStroke.getKeyStroke(ch, modifiers);
296: }
297: }
298:
299: // private members
300: private Hashtable bindings;
301: private Hashtable currentBindings;
302:
303: private DefaultInputHandler(DefaultInputHandler copy) {
304: bindings = currentBindings = copy.bindings;
305: }
306: }
|