001: // Copyright (c) 2002 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.jemacs.buffer;
005:
006: import gnu.lists.*;
007: import gnu.math.IntNum;
008: import gnu.text.Char;
009: import gnu.mapping.*;
010: import java.awt.event.KeyEvent;
011:
012: public class EKeymap extends gnu.kawa.util.RangeTable implements
013: gnu.mapping.Named {
014: public static final EKeymap[] empty = new EKeymap[0];
015: EKeymap[] parents = empty;
016:
017: public static int PRESSED = 0x100;
018: public static int RELEASED = 0x200;
019:
020: Object defaultBinding;
021:
022: String name;
023:
024: /** The magic key that indicates a (Emacs) meta prefix.
025: * I.e. we saw either an Escape or a meta modifier. */
026: public static final int metaKey = '\033';
027:
028: /** The Emacs global map. */
029: public static EKeymap globalKeymap = new EKeymap();
030:
031: /** The Emacs global escape (meta) map. */
032: public static EKeymap metaKeymap = new EKeymap("ESC-map");
033:
034: static {
035: globalKeymap.setAction(metaKey, metaKeymap);
036: }
037:
038: public static final int CTRL_MASK = java.awt.event.InputEvent.CTRL_MASK;
039: public static final int SHIFT_MASK = java.awt.event.InputEvent.SHIFT_MASK;
040: // Note ALT_MASK and META_MASK are shifted!
041: public static final int META_MASK = java.awt.event.InputEvent.ALT_MASK;
042: public static final int ALT_MASK = java.awt.event.InputEvent.META_MASK;
043:
044: public EKeymap(String name) {
045: this .name = name;
046: }
047:
048: public EKeymap() {
049: }
050:
051: public String getName() {
052: return name;
053: }
054:
055: public Object getSymbol() {
056: return name;
057: }
058:
059: public void setName(String name) {
060: this .name = name;
061: }
062:
063: public final Object getDefaultBinding() {
064: return defaultBinding;
065: }
066:
067: public void setDefaultBinding(Object value) {
068: defaultBinding = value;
069: }
070:
071: public static int getModifiers(int code) {
072: return (code >> 16) & 0xFF;
073: }
074:
075: public EKeymap[] getParents() {
076: return parents;
077: }
078:
079: public void setParents(EKeymap[] parents) {
080: this .parents = parents;
081: }
082:
083: public void setParent(EKeymap parent) {
084: if (parent == null)
085: this .parents = empty;
086: else
087: this .parents = new EKeymap[] { parent };
088: }
089:
090: public EKeymap getParent() {
091: int num = parents.length;
092: if (num == 0)
093: return null;
094: if (num == 1)
095: return parents[0];
096: throw new Error(
097: "multiple parents - set getParents, not getParent");
098: }
099:
100: public void setAction(int key, Object command) {
101: set(key, command);
102: }
103:
104: public Object get(int key, int modifiers, boolean acceptDefaults) {
105: return get(key | (modifiers << 16), acceptDefaults);
106: }
107:
108: protected Object get(int key, boolean acceptDefaults) {
109: Object value = super .lookup(key, null);
110: if (value != null)
111: return value;
112: if (acceptDefaults && defaultBinding != null)
113: return defaultBinding;
114: int plen = parents.length;
115: for (int i = 0; i < plen; i++) {
116: value = parents[i].get(key, acceptDefaults);
117: if (value != null)
118: return value;
119: }
120: return null;
121: }
122:
123: public String toString() {
124: StringBuffer sbuf = new StringBuffer(40);
125: sbuf.append("#<keymap ");
126: if (name != null) {
127: sbuf.append(name);
128: sbuf.append(' ');
129: }
130: /*
131: sbuf.append("size ");
132: sbuf.append('?');
133: */
134: sbuf.append("0x");
135: sbuf.append(Integer.toHexString(System.identityHashCode(this )));
136: sbuf.append('>');
137: return sbuf.toString();
138: }
139:
140: /** Get or create keymap associate with a prefix key in a given keymap. */
141: public EKeymap definePrefix(int key) {
142: Object command = get(key, false);
143: Object cc = command;
144: Object x;
145: if (command == null) {
146: EKeymap next = new EKeymap(null);
147: set(key, next);
148: return next;
149: }
150: command = Command.resolveSymbol(command);
151: if (command instanceof EKeymap)
152: return (EKeymap) command;
153: else {
154: throw new Error(
155: "prefix command cannot override exiting action: "
156: + cc + " : " + cc.getClass() + " -> "
157: + command + " key " + key + "="
158: + toString(key) + " in " + this );
159: }
160: }
161:
162: public void defineKey(Object keySpec, Object binding) {
163: EKeymap keymap = this ;
164: if (keySpec instanceof Sequence && !(keySpec instanceof LList)) {
165: // Handle key sequence.
166: Sequence value = (Sequence) keySpec;
167: boolean hackMeta = keySpec instanceof FString;
168: int len = value.size();
169: for (int i = 0; i < len;) {
170: Object keyValue = value.get(i);
171: boolean sawMeta = false;
172: i++;
173: int key = asKeyStroke(keyValue);
174: if (key == 0)
175: throw new Error("unknown keyspec: " + keyValue);
176: if (hackMeta && key > 127 && key <= 255) {
177: sawMeta = true;
178: key = key - 128;
179: }
180: if ((getModifiers(key) & META_MASK) != 0) {
181: key = stripMeta(key);
182: sawMeta = true;
183: }
184: if (sawMeta)
185: keymap = keymap.definePrefix(metaKey);
186: if (i < len)
187: keymap = keymap.definePrefix(key);
188: else
189: keymap.defineKey(key, binding);
190: }
191: } else {
192: // Handle single key.
193: int key = asKeyStroke(keySpec);
194: if (key == 0)
195: throw new Error("unknown keyspec: " + keySpec);
196: defineKey(key, binding);
197: }
198: }
199:
200: public void defineKey(int key, Object binding) {
201: boolean sawMeta = false;
202: if ((getModifiers(key) & META_MASK) != 0) {
203: key = stripMeta(key);
204: sawMeta = true;
205: }
206: EKeymap keymap = this ;
207: if (sawMeta)
208: keymap = keymap.definePrefix(metaKey);
209: keymap.setAction(key, binding);
210: }
211:
212: public static int asKeyStroke(char ch, int mods) {
213: if (mods == SHIFT_MASK && Character.isLetter(ch)) {
214: ch = Character.toUpperCase(ch);
215: mods = 0;
216: }
217: if (ch < ' ') {
218: mods |= CTRL_MASK | PRESSED;
219: ch = ch == '\0' ? ' ' : (char) ('@' + (ch & 31));
220: }
221: return ch | (mods << 16);
222: }
223:
224: /** Map an Emacs key name to one of the KeyEVent VK_XXX codes.
225: * Returns VK_UNDEFINED if the name isn't recognized.
226: */
227: public static int getKeyForName(String name) {
228: name = name.toLowerCase();
229: int len = name.length();
230: if (len == 0)
231: return KeyEvent.VK_UNDEFINED;
232: char c0 = name.charAt(0);
233: if (len == 1)
234: return c0;
235: switch (c0) {
236: case 'b':
237: if (name == "backspace")
238: return KeyEvent.VK_BACK_SPACE;
239: break;
240: case 'd':
241: if (name == "delete")
242: return KeyEvent.VK_DELETE;
243: if (name == "down")
244: return KeyEvent.VK_DOWN;
245: break;
246: case 'e':
247: if (name == "enter")
248: return KeyEvent.VK_ENTER;
249: break;
250: case 'f':
251: if (len == 2) {
252: char c1 = name.charAt(1);
253: if (c1 > '0' && c1 <= '9')
254: return KeyEvent.VK_F1 + c1 - '1';
255: } else if (len == 3 && name.charAt(0) == 'f') {
256: int c1 = name.charAt(1);
257: int c2 = name.charAt(2);
258: if (c1 > '0' && c1 <= '9' && c2 > '0' && c2 <= '9'
259: && (c1 = (c1 - '0') * 10 + (c2 - '0')) <= 24) {
260: if (c1 <= 12)
261: return KeyEvent.VK_F10 + c2 - '0';
262: else
263: return KeyEvent.VK_F13 + c1 - 13;
264: }
265: }
266: break;
267: case 'h':
268: if (name == "help")
269: return KeyEvent.VK_HELP;
270: break;
271: case 'k':
272: if (name == "kp-left")
273: return KeyEvent.VK_KP_LEFT;
274: else if (name == "kp-right")
275: return KeyEvent.VK_KP_RIGHT;
276: else if (name == "kp-up")
277: return KeyEvent.VK_KP_UP;
278: else if (name == "kp-down")
279: return KeyEvent.VK_KP_DOWN;
280: else if (name == "kp-delete")
281: return KeyEvent.VK_DELETE;
282: break;
283: case 'l':
284: if (name == "left")
285: return KeyEvent.VK_LEFT;
286: break;
287: case 'n':
288: if (name == "next")
289: return KeyEvent.VK_PAGE_DOWN;
290: break;
291: case 'p':
292: if (name == "prior")
293: return KeyEvent.VK_PAGE_UP;
294: break;
295: case 'r':
296: if (name == "enter")
297: return KeyEvent.VK_ENTER;
298: if (name == "return")
299: return '\r';
300: if (name == "right")
301: return KeyEvent.VK_RIGHT;
302: break;
303: case 't':
304: if (name == "tab")
305: return KeyEvent.VK_TAB;
306: break;
307: case 'u':
308: if (name == "up")
309: return KeyEvent.VK_UP;
310: break;
311: }
312: return KeyEvent.VK_UNDEFINED;
313: }
314:
315: public static int asKeyStroke(Object key) {
316: int m = 0;
317: while (key instanceof Pair) {
318: Pair pair = (Pair) key;
319: if (pair.cdr == LList.Empty)
320: key = pair.car;
321: else {
322: Object car = pair.car;
323: if (car instanceof Symbol)
324: car = ((Symbol) car).getName();
325: if (car == "control")
326: m |= CTRL_MASK;
327: if (car == "meta")
328: m |= META_MASK;
329: if (car == "shift")
330: m |= SHIFT_MASK;
331: if (car == "alt")
332: m |= ALT_MASK;
333: key = pair.cdr;
334: }
335: }
336: if (key instanceof Char) {
337: return asKeyStroke(((Char) key).charValue(), m);
338: }
339: if (key instanceof IntNum) {
340: return asKeyStroke((char) ((IntNum) key).intValue(), m);
341: }
342: if (key instanceof String || key instanceof Symbol) {
343: String name = key instanceof String ? (String) key
344: : ((Symbol) key).getName();
345: if (name.length() == 1) {
346: char ch = name.charAt(0);
347: if (m == 0)
348: return asKeyStroke((char) ch, 0);
349: else {
350: ch = Character.toUpperCase(ch);
351: return asKeyStroke(ch, m);
352: }
353: }
354: int code = getKeyForName(name);
355: if (code == KeyEvent.VK_UNDEFINED)
356: throw new Error("unknown key-name: " + name);
357: return code | ((m | PRESSED) << 16);
358: }
359: return 0; // FIXME toInt((KeyStroke) key);
360: }
361:
362: public static int stripMeta(int key) {
363: int mods = getModifiers(key);
364: if ((mods & META_MASK) == 0)
365: return key;
366: mods &= ~META_MASK;
367: int code = key & 0xFFFF;
368: boolean onRelease = (key & (RELEASED << 16)) != 0;
369: if ((mods & ~SHIFT_MASK) != 0 || onRelease || code > 127
370: || code < ' ')
371: return code | ((mods | RELEASED) << 16);
372: else {
373: if (code >= 'A' && code <= 'Z' && mods != SHIFT_MASK)
374: code = code + 'a' - 'A';
375: return code;
376: }
377: }
378:
379: /**
380: * True for a KeyStroke if the default action should be to ignore it.
381: * For example, pressing a shift key should not be an action!
382: * We also have the complication that both KEY-PRESSED and KEY_TYPED
383: * events and we typically want to ignore one but not both.
384: * (If both are handled, we have problems with default actions, as
385: * well as when to abort a prefix sequence. Swing does not have
386: * this problem because it does not have prefix sequences and hence state.)
387: */
388: public static boolean ignorable(int key) {
389: if ((key & (RELEASED << 16)) != 0)
390: return true;
391: int mods = getModifiers(key);
392: // If there are no modifiers, and it's a normal non-control character,
393: // we prefer the KEY_TYPED (keyChar) event.
394: // Otherwise, we prefer the KEY_PRESSED (keyCode) event.
395: int code = key & 0xFFFF;
396: if ((key & (PRESSED << 16)) == 0) { // It's a KEY_TYPED (keyChar) event.
397: char ch = (char) key;
398: return (ch < ' ' && ch != metaKey) || ch >= 127;
399: } else { // It's a KEY_PRESSED (keyCODE) event.
400: // Basically, in the case of KEY_PRESSED events that will
401: // map without loss of information into normal KEY_TYPED events,
402: // we prefer the KEY_TYPED events (as they don't depend on the
403: // keyboard layout).
404: if (code == KeyEvent.VK_CONTROL
405: || code == KeyEvent.VK_SHIFT
406: || code == KeyEvent.VK_ALT
407: || code == KeyEvent.VK_META
408: || code == KeyEvent.VK_ESCAPE)
409: return true;
410: return (mods & ~SHIFT_MASK) == 0
411: && code >= KeyEvent.VK_SPACE
412: && code < KeyEvent.VK_DELETE;
413: }
414: }
415:
416: /*
417: public static String toString(KeyStroke key)
418: {
419: StringBuffer sbuf = new StringBuffer();
420: sbuf.append('[');
421: char ch = key.getKeyChar();
422: if (ch != '\0')
423: {
424: sbuf.append("char:'");
425: gnu.jemacs.lang.ELisp.readableChar(ch, sbuf, true);
426: sbuf.append("'");
427: }
428: int code = key.getKeyCode();
429: if (code != 0)
430: {
431: sbuf.append("code:");
432: sbuf.append(code);
433: }
434: int mods = key.getModifiers();
435: if (mods != 0)
436: {
437: sbuf.append(" mods:");
438: sbuf.append(mods);
439: }
440: if (key.isOnKeyRelease())
441: sbuf.append(" release");
442: sbuf.append(']');
443: return sbuf.toString();
444: }
445: */
446:
447: public static String toString(int code) {
448: StringBuffer sbuf = new StringBuffer();
449: sbuf.append('[');
450: if (((code >> 16) & (PRESSED | RELEASED)) == 0) {
451: sbuf.append("char:'");
452: gnu.jemacs.lang.ELisp.readableChar((char) code, sbuf, true);
453: sbuf.append("'");
454: } else {
455: sbuf.append("code:");
456: sbuf.append(code & 0xFFFF);
457: }
458: int mods = (code >> 16) & 0xff;
459: if (mods != 0) {
460: sbuf.append(" mods:");
461: sbuf.append(mods);
462: }
463: if ((code & (RELEASED << 16)) != 0)
464: sbuf.append(" release");
465: sbuf.append(']');
466: return sbuf.toString();
467: }
468:
469: public Object lookupKey(Sequence keys, boolean acceptDefaults) {
470: int nKeys = keys.size();
471: int[] prefixKeys = new int[nKeys];
472: java.util.Enumeration enumKeys = keys.elements();
473: for (int i = 0; enumKeys.hasMoreElements(); i++) {
474: prefixKeys[i] = asKeyStroke(enumKeys.nextElement());
475: }
476: return lookupKey(prefixKeys, nKeys, 0, acceptDefaults);
477: }
478:
479: public Object lookupKey(int[] prefixKeys, int nPrefix, int key,
480: boolean acceptDefaults) {
481: EKeymap keymap = this ;
482: int nKeys = nPrefix + (key != 0 ? 1 : 0);
483: boolean pendingMeta = false;
484: if (nKeys == 0)
485: throw new Error("no keys");
486: for (int i = 0;;) {
487: int key_i = i == nPrefix ? key : prefixKeys[i];
488:
489: if (pendingMeta)
490: key_i |= ((META_MASK | PRESSED) << 16);
491: Object command = keymap.get(key_i, false);
492: Object metaCommand;
493: if (command == null
494: && (getModifiers(key_i) & META_MASK) != 0
495: && (metaCommand = keymap.get(metaKey, false)) instanceof EKeymap) {
496: command = ((EKeymap) metaCommand).get(stripMeta(key_i),
497: false);
498: }
499: i++;
500: if (command == null) {
501: if (ignorable(key)) {
502: return EToolkit.getInstance().getIgnoreAction();
503: } else
504: return keymap.getDefaultBinding();
505: }
506: if (i == nKeys)
507: return command;
508: Object comm;
509: if (command instanceof String || command instanceof Symbol)
510: command = Command.resolveSymbol(command);
511: if (command instanceof EKeymap)
512: keymap = (EKeymap) command;
513: else
514: return null;
515: }
516: }
517:
518: /*
519: * For debugging
520: */
521: public static String show(int binary) {
522: StringBuffer sb = new StringBuffer(Integer
523: .toBinaryString(binary));
524: for (int i = 32 - sb.length() - 1; i >= 0; i--)
525: sb.insert(0, '0');
526: return sb.toString();
527: }
528: }
|