001: package workbench.gui.editor;
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:
012: import java.awt.event.ActionListener;
013: import java.awt.event.InputEvent;
014: import java.awt.event.KeyEvent;
015: import java.util.HashMap;
016: import java.util.StringTokenizer;
017:
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.26 2008/01/18 23:40:09 thomas 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 HashMap();
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+Q",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: addKeyBinding("C+U", MAKE_UPPER_CASE);
078: addKeyBinding("C+L", MAKE_LOWER_CASE);
079: //addKeyBinding("C+B", MATCH_BRACKET);
080: addKeyBinding("C+Z", UNDO);
081: addKeyBinding("C+Y", REDO);
082: }
083:
084: /**
085: * Adds a key binding to this input handler. The key binding is
086: * a list of white space separated key strokes of the form
087: * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
088: * or S for Shift, and key is either a character (a-z) or a field
089: * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
090: * @param keyBinding The key binding
091: * @param action The action
092: */
093: public void addKeyBinding(String keyBinding, ActionListener action) {
094: HashMap current = bindings;
095:
096: StringTokenizer st = new StringTokenizer(keyBinding);
097: while (st.hasMoreTokens()) {
098: KeyStroke keyStroke = parseKeyStroke(st.nextToken());
099: if (keyStroke == null)
100: return;
101:
102: if (st.hasMoreTokens()) {
103: Object o = current.get(keyStroke);
104: if (!(o instanceof HashMap)) {
105: o = new HashMap();
106: current.put(keyStroke, o);
107: }
108: current = (HashMap) o;
109: } else {
110: current.put(keyStroke, action);
111: }
112: }
113: this .currentBindings = bindings;
114: }
115:
116: public void addKeyBinding(KeyStroke key, ActionListener action) {
117: this .bindings.put(key, action);
118: this .currentBindings = this .bindings;
119: }
120:
121: /**
122: * Removes a key binding from this input handler. This is not yet
123: * implemented.
124: * @param key The key binding
125: */
126: public void removeKeyBinding(KeyStroke key) {
127: bindings.remove(key);
128: }
129:
130: /**
131: * Removes all key bindings from this input handler.
132: */
133: public void removeAllKeyBindings() {
134: bindings.clear();
135: }
136:
137: /**
138: * Returns a copy of this input handler that shares the same
139: * key bindings. Setting key bindings in the copy will also
140: * set them in the original.
141: */
142: public InputHandler copy() {
143: return new DefaultInputHandler(this );
144: }
145:
146: /**
147: * Handle a key pressed event. This will look up the binding for
148: * the key stroke and execute it.
149: */
150: public void keyPressed(KeyEvent evt) {
151: int keyCode = evt.getKeyCode();
152: int modifiers = evt.getModifiers();
153:
154: if (keyCode == KeyEvent.VK_CONTROL
155: || keyCode == KeyEvent.VK_SHIFT
156: || keyCode == KeyEvent.VK_ALT
157: || keyCode == KeyEvent.VK_META)
158: return;
159:
160: if ((modifiers & ~KeyEvent.SHIFT_MASK) != 0
161: || evt.isActionKey()
162: || keyCode == KeyEvent.VK_BACK_SPACE
163: || keyCode == KeyEvent.VK_DELETE
164: || keyCode == KeyEvent.VK_ENTER
165: || keyCode == KeyEvent.VK_TAB
166: || keyCode == KeyEvent.VK_ESCAPE) {
167: if (grabAction != null) {
168: handleGrabAction(evt);
169: return;
170: }
171:
172: if (keyCode == KeyEvent.VK_TAB) {
173: JEditTextArea area = getTextArea(evt);
174: int start = area.getSelectionStart();
175: int end = area.getSelectionEnd();
176: if (start < end) {
177: TextIndenter indenter = new TextIndenter(area);
178: if ((modifiers & KeyEvent.SHIFT_MASK) == KeyEvent.SHIFT_MASK) {
179: indenter.unIndentSelection();
180: } else {
181: indenter.indentSelection();
182: }
183: return;
184: }
185: }
186:
187: KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode,
188: modifiers);
189: Object o = currentBindings.get(keyStroke);
190:
191: if (o == null) {
192: // Don't beep if the user presses some
193: // key we don't know about unless a
194: // prefix is active. Otherwise it will
195: // beep when caps lock is pressed, etc.
196: /*
197: if(currentBindings != bindings)
198: {
199: Toolkit.getDefaultToolkit().beep();
200: // F10 should be passed on, but C+e F10
201: // shouldn't
202: repeatCount = 0;
203: repeat = false;
204: evt.consume();
205: }*/
206: currentBindings = bindings;
207: return;
208: } else if (o instanceof ActionListener) {
209: currentBindings = bindings;
210:
211: executeAction(((ActionListener) o), evt.getSource(),
212: null);
213:
214: evt.consume();
215: return;
216: } else if (o instanceof HashMap) {
217: currentBindings = (HashMap) o;
218: evt.consume();
219: return;
220: }
221: }
222: }
223:
224: /**
225: * Handle a key typed event. This inserts the key into the text area.
226: */
227: public void keyTyped(KeyEvent evt) {
228: //
229: if (evt.isControlDown() || evt.isAltDown())
230: return;
231:
232: char c = evt.getKeyChar();
233:
234: if (c != KeyEvent.CHAR_UNDEFINED) {
235: if (c >= 0x20 && c != 0x7f) {
236: KeyStroke keyStroke = KeyStroke.getKeyStroke(Character
237: .toUpperCase(c));
238: Object o = currentBindings.get(keyStroke);
239:
240: if (o instanceof HashMap) {
241: currentBindings = (HashMap) o;
242: return;
243: } else if (o instanceof ActionListener) {
244: currentBindings = bindings;
245: executeAction((ActionListener) o, evt.getSource(),
246: String.valueOf(c));
247: return;
248: }
249:
250: currentBindings = bindings;
251:
252: if (grabAction != null) {
253: handleGrabAction(evt);
254: return;
255: }
256:
257: // 0-9 adds another 'digit' to the repeat number
258: if (repeat && Character.isDigit(c)) {
259: repeatCount *= 10;
260: repeatCount += (c - '0');
261: return;
262: }
263:
264: executeAction(INSERT_CHAR, evt.getSource(), String
265: .valueOf(evt.getKeyChar()));
266:
267: repeatCount = 0;
268: repeat = false;
269: }
270: }
271: }
272:
273: /**
274: * Converts a string to a keystroke. The string should be of the
275: * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
276: * is any combination of A for Alt, C for Control, S for Shift
277: * or M for Meta, and <i>shortcut</i> is either a single character,
278: * or a keycode name from the <code>KeyEvent</code> class, without
279: * the <code>VK_</code> prefix.
280: * @param keyStroke A string description of the key stroke
281: */
282: public static KeyStroke parseKeyStroke(String keyStroke) {
283: if (keyStroke == null)
284: return null;
285: int modifiers = 0;
286: int index = keyStroke.indexOf('+');
287: if (index != -1) {
288: for (int i = 0; i < index; i++) {
289: switch (Character.toUpperCase(keyStroke.charAt(i))) {
290: case 'A':
291: modifiers |= InputEvent.ALT_MASK;
292: break;
293: case 'C':
294: modifiers |= InputEvent.CTRL_MASK;
295: break;
296: case 'M':
297: modifiers |= InputEvent.META_MASK;
298: break;
299: case 'S':
300: modifiers |= InputEvent.SHIFT_MASK;
301: break;
302: }
303: }
304: }
305: String key = keyStroke.substring(index + 1);
306: if (key.length() == 1) {
307: char ch = Character.toUpperCase(key.charAt(0));
308: if (modifiers == 0)
309: return KeyStroke.getKeyStroke(ch);
310: else
311: return KeyStroke.getKeyStroke(ch, modifiers);
312: } else if (key.length() == 0) {
313: System.err.println("Invalid key stroke: " + keyStroke);
314: return null;
315: } else {
316: int ch;
317:
318: try {
319: ch = KeyEvent.class.getField("VK_".concat(key)).getInt(
320: null);
321: } catch (Exception e) {
322: System.err.println("Invalid key stroke: " + keyStroke);
323: return null;
324: }
325:
326: return KeyStroke.getKeyStroke(ch, modifiers);
327: }
328: }
329:
330: // private members
331: private HashMap bindings;
332: private HashMap currentBindings;
333:
334: private DefaultInputHandler(DefaultInputHandler copy) {
335: bindings = currentBindings = copy.bindings;
336: }
337: }
|