001: /*
002: * DefaultInputHandler.java - Default implementation of an input handler
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 1999, 2003 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.gui;
024:
025: //{{{ Imports
026: import java.awt.event.InputEvent;
027: import java.awt.Toolkit;
028: import java.util.Hashtable;
029: import java.util.StringTokenizer;
030: import org.gjt.sp.jedit.*;
031:
032: //}}}
033:
034: /**
035: * The default input handler. It maps sequences of keystrokes into actions
036: * and inserts key typed events into the text area.
037: * @author Slava Pestov
038: * @version $Id: DefaultInputHandler.java 11035 2007-11-12 19:44:17Z kpouer $
039: */
040: public class DefaultInputHandler extends InputHandler {
041: //{{{ DefaultInputHandler constructor
042: /**
043: * Creates a new input handler with no key bindings defined.
044: * @param view The view
045: * @param bindings An explicitly-specified set of key bindings,
046: * must not be null.
047: * @since jEdit 4.3pre1
048: */
049: public DefaultInputHandler(View view, Hashtable bindings) {
050: super (view);
051:
052: if (bindings == null)
053: throw new NullPointerException();
054: this .bindings = this .currentBindings = bindings;
055: } //}}}
056:
057: //{{{ DefaultInputHandler constructor
058: /**
059: * Creates a new input handler with no key bindings defined.
060: * @param view The view
061: */
062: public DefaultInputHandler(View view) {
063: this (view, new Hashtable());
064: } //}}}
065:
066: //{{{ DefaultInputHandler constructor
067: /**
068: * Creates a new input handler with the same set of key bindings
069: * as the one specified. Note that both input handlers share
070: * a pointer to exactly the same key binding table; so adding
071: * a key binding in one will also add it to the other.
072: * @param copy The input handler to copy key bindings from
073: * @param view The view
074: */
075: public DefaultInputHandler(View view, DefaultInputHandler copy) {
076: this (view, copy.bindings);
077: } //}}}
078:
079: //{{{ addKeyBinding() method
080: /**
081: * Adds a key binding to this input handler. The key binding is
082: * a list of white space separated key strokes of the form
083: * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
084: * or S for Shift, and key is either a character (a-z) or a field
085: * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
086: * @param keyBinding The key binding
087: * @param action The action
088: * @since jEdit 4.2pre1
089: */
090: public void addKeyBinding(String keyBinding, String action) {
091: addKeyBinding(keyBinding, (Object) action);
092: } //}}}
093:
094: //{{{ addKeyBinding() method
095: /**
096: * Adds a key binding to this input handler. The key binding is
097: * a list of white space separated key strokes of the form
098: * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
099: * or S for Shift, and key is either a character (a-z) or a field
100: * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
101: * @param keyBinding The key binding
102: * @param action The action
103: */
104: public void addKeyBinding(String keyBinding, EditAction action) {
105: addKeyBinding(keyBinding, (Object) action);
106: } //}}}
107:
108: //{{{ addKeyBinding() method
109: /**
110: * Adds a key binding to this input handler. The key binding is
111: * a list of white space separated key strokes of the form
112: * <i>[modifiers+]key</i> where modifier is C for Control, A for Alt,
113: * or S for Shift, and key is either a character (a-z) or a field
114: * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE)
115: * @param keyBinding The key binding
116: * @param action The action
117: * @since jEdit 4.3pre1
118: */
119: public void addKeyBinding(String keyBinding, Object action) {
120: Hashtable current = bindings;
121:
122: String prefixStr = null;
123:
124: StringTokenizer st = new StringTokenizer(keyBinding);
125: while (st.hasMoreTokens()) {
126: String keyCodeStr = st.nextToken();
127: if (prefixStr == null)
128: prefixStr = keyCodeStr;
129: else
130: prefixStr = prefixStr + " " + keyCodeStr;
131:
132: KeyEventTranslator.Key keyStroke = KeyEventTranslator
133: .parseKey(keyCodeStr);
134: if (keyStroke == null)
135: return;
136:
137: if (st.hasMoreTokens()) {
138: Object o = current.get(keyStroke);
139: if (o instanceof Hashtable)
140: current = (Hashtable) o;
141: else {
142: Hashtable hash = new Hashtable();
143: hash.put(PREFIX_STR, prefixStr);
144: o = hash;
145: current.put(keyStroke, o);
146: current = (Hashtable) o;
147: }
148: } else
149: current.put(keyStroke, action);
150: }
151: } //}}}
152:
153: //{{{ removeKeyBinding() method
154: /**
155: * Removes a key binding from this input handler. This is not yet
156: * implemented.
157: * @param keyBinding The key binding
158: */
159: public void removeKeyBinding(String keyBinding) {
160: Hashtable current = bindings;
161:
162: StringTokenizer st = new StringTokenizer(keyBinding);
163: while (st.hasMoreTokens()) {
164: String keyCodeStr = st.nextToken();
165: KeyEventTranslator.Key keyStroke = KeyEventTranslator
166: .parseKey(keyCodeStr);
167: if (keyStroke == null)
168: return;
169:
170: if (st.hasMoreTokens()) {
171: Object o = current.get(keyStroke);
172: if (o instanceof Hashtable)
173: current = ((Hashtable) o);
174: else if (o != null) {
175: // we have binding foo
176: // but user asks to remove foo bar?
177: current.remove(keyStroke);
178: return;
179: } else {
180: // user asks to remove non-existent
181: return;
182: }
183: } else
184: current.remove(keyStroke);
185: }
186: } //}}}
187:
188: //{{{ removeAllKeyBindings() method
189: /**
190: * Removes all key bindings from this input handler.
191: */
192: public void removeAllKeyBindings() {
193: bindings.clear();
194: } //}}}
195:
196: //{{{ getKeyBinding() method
197: /**
198: * Returns either an edit action, or a hashtable if the specified key
199: * is a prefix.
200: * @param keyBinding The key binding
201: * @since jEdit 3.2pre5
202: */
203: public Object getKeyBinding(String keyBinding) {
204: Hashtable current = bindings;
205: StringTokenizer st = new StringTokenizer(keyBinding);
206:
207: while (st.hasMoreTokens()) {
208: KeyEventTranslator.Key keyStroke = KeyEventTranslator
209: .parseKey(st.nextToken());
210: if (keyStroke == null)
211: return null;
212:
213: if (st.hasMoreTokens()) {
214: Object o = current.get(keyStroke);
215: if (o instanceof Hashtable) {
216: if (!st.hasMoreTokens())
217: return o;
218: else
219: current = (Hashtable) o;
220: } else
221: return o;
222: } else {
223: return current.get(keyStroke);
224: }
225: }
226:
227: return null;
228: } //}}}
229:
230: //{{{ isPrefixActive() method
231: /**
232: * Returns if a prefix key has been pressed.
233: */
234: public boolean isPrefixActive() {
235: return bindings != currentBindings || super .isPrefixActive();
236: } //}}}
237:
238: //{{{ setBindings() method
239: /**
240: * Replace the set of key bindings.
241: * @since jEdit 4.3pre1
242: */
243: public void setBindings(Hashtable bindings) {
244: this .bindings = this .currentBindings = bindings;
245: } //}}}
246:
247: //{{{ setCurrentBindings() method
248: public void setCurrentBindings(Hashtable bindings) {
249: view.getStatus().setMessage((String) bindings.get(PREFIX_STR));
250: currentBindings = bindings;
251: } //}}}
252:
253: //{{{ handleKey() method
254: /**
255: * Handles the given keystroke.
256: * @param keyStroke The key stroke
257: * @param dryRun only calculate the return value, do not have any other effect
258: * @since jEdit 4.2pre5
259: */
260: public boolean handleKey(KeyEventTranslator.Key keyStroke,
261: boolean dryRun) {
262: char input = '\0';
263: if (keyStroke.modifiers == null
264: || keyStroke.modifiers.equals("S")) {
265: switch (keyStroke.key) {
266: case '\n':
267: case '\t':
268: input = (char) keyStroke.key;
269: break;
270: default:
271: input = keyStroke.input;
272: break;
273: }
274: }
275:
276: if (readNextChar != null) {
277: if (input != '\0') {
278: if (!dryRun) {
279: setCurrentBindings(bindings);
280: invokeReadNextChar(input);
281: repeatCount = 1;
282: }
283: return true;
284: } else {
285: if (!dryRun) {
286: readNextChar = null;
287: view.getStatus().setMessage(null);
288: }
289: }
290: }
291:
292: Object o = currentBindings.get(keyStroke);
293: if (o == null) {
294: if (!dryRun) {
295: // Don't beep if the user presses some
296: // key we don't know about unless a
297: // prefix is active. Otherwise it will
298: // beep when caps lock is pressed, etc.
299: if (currentBindings != bindings) {
300: Toolkit.getDefaultToolkit().beep();
301: // F10 should be passed on, but C+e F10
302: // shouldn't
303: repeatCount = 1;
304: setCurrentBindings(bindings);
305: } else if (input != '\0') {
306: if (!keyStroke.isFromGlobalContext()) { // let user input be only local
307: userInput(input);
308: }
309: } else {
310: // this is retarded. excuse me while I drool
311: // and make stupid noises
312: if (KeyEventWorkaround
313: .isNumericKeypad(keyStroke.key))
314: KeyEventWorkaround.numericKeypadKey();
315: }
316: sendShortcutPrefixOff();
317: }
318: } else if (o instanceof Hashtable) {
319: if (!dryRun) {
320: setCurrentBindings((Hashtable) o);
321: ShortcutPrefixActiveEvent.firePrefixStateChange(
322: currentBindings, true);
323: shortcutOn = true;
324: }
325: return true;
326: } else if (o instanceof String) {
327: if (!dryRun) {
328: setCurrentBindings(bindings);
329: sendShortcutPrefixOff();
330: invokeAction((String) o);
331: }
332: return true;
333: } else if (o instanceof EditAction) {
334: if (!dryRun) {
335: setCurrentBindings(bindings);
336: sendShortcutPrefixOff();
337: invokeAction((EditAction) o);
338: }
339: return true;
340: }
341: if (!dryRun) {
342: sendShortcutPrefixOff();
343: }
344: return false;
345: } //}}}
346:
347: //{{{ handleKey() methodprotected void sendShortcutPrefixOff()
348: /**
349: * If
350: */
351: protected void sendShortcutPrefixOff() {
352: if (shortcutOn) {
353: ShortcutPrefixActiveEvent
354: .firePrefixStateChange(null, false);
355: shortcutOn = false;
356: }
357: } //}}}
358:
359: protected boolean shortcutOn = false;
360:
361: //{{{ getSymbolicModifierName() method
362: /**
363: * Returns a the symbolic modifier name for the specified Java modifier
364: * flag.
365: *
366: * @param mod A modifier constant from <code>InputEvent</code>
367: *
368: * @since jEdit 4.1pre3
369: */
370: public static char getSymbolicModifierName(int mod) {
371: return KeyEventTranslator.getSymbolicModifierName(mod);
372: } //}}}
373:
374: //{{{ getModifierString() method
375: /**
376: * Returns a string containing symbolic modifier names set in the
377: * specified event.
378: *
379: * @param evt The event
380: *
381: * @since jEdit 4.1pre3
382: */
383: public static String getModifierString(InputEvent evt) {
384: return KeyEventTranslator.getModifierString(evt);
385: } //}}}
386:
387: //{{{ Private members
388:
389: // Stores prefix name in bindings hashtable
390: public static Object PREFIX_STR = "PREFIX_STR";
391:
392: private Hashtable bindings;
393: private Hashtable currentBindings;
394: //}}}
395:
396: }
|