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