KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > gnu > jemacs > buffer > EKeymap


1 // Copyright (c) 2002 Per M.A. Bothner.
2
// This is free software; for terms and warranty disclaimer see ./COPYING.
3

4 package gnu.jemacs.buffer;
5 import gnu.lists.*;
6 import gnu.math.IntNum;
7 import gnu.text.Char;
8 import gnu.mapping.*;
9 import java.awt.event.KeyEvent JavaDoc;
10
11 public class EKeymap extends gnu.kawa.util.RangeTable
12 implements gnu.mapping.Named
13 {
14   public static final EKeymap[] empty = new EKeymap[0];
15   EKeymap[] parents = empty;
16
17   public static int PRESSED = 0x100;
18   public static int RELEASED = 0x200;
19
20   Object JavaDoc defaultBinding;
21
22   String JavaDoc name;
23
24   /** The magic key that indicates a (Emacs) meta prefix.
25    * I.e. we saw either an Escape or a meta modifier. */

26   public static final int metaKey = '\033';
27
28   /** The Emacs global map. */
29   public static EKeymap globalKeymap = new EKeymap();
30
31   /** The Emacs global escape (meta) map. */
32   public static EKeymap metaKeymap = new EKeymap("ESC-map");
33
34   static
35   {
36     globalKeymap.setAction(metaKey, metaKeymap);
37   }
38
39   public static final int CTRL_MASK = java.awt.event.InputEvent.CTRL_MASK;
40   public static final int SHIFT_MASK = java.awt.event.InputEvent.SHIFT_MASK;
41   // Note ALT_MASK and META_MASK are shifted!
42
public static final int META_MASK = java.awt.event.InputEvent.ALT_MASK;
43   public static final int ALT_MASK = java.awt.event.InputEvent.META_MASK;
44
45   public EKeymap (String JavaDoc name)
46   {
47     this.name = name;
48   }
49
50   public EKeymap ()
51   {
52   }
53
54   public String JavaDoc getName()
55   {
56     return name;
57   }
58
59   public Object JavaDoc getSymbol ()
60   {
61     return name;
62   }
63
64   public void setName (String JavaDoc name)
65   {
66     this.name = name;
67   }
68
69   public final Object JavaDoc getDefaultBinding ()
70   {
71     return defaultBinding;
72   }
73
74   public void setDefaultBinding (Object JavaDoc value)
75   {
76     defaultBinding = value;
77   }
78
79   public static int getModifiers (int code)
80   {
81     return (code >> 16) & 0xFF;
82   }
83
84   public EKeymap[] getParents ()
85   {
86     return parents;
87   }
88
89   public void setParents (EKeymap[] parents)
90   {
91     this.parents = parents;
92   }
93
94   public void setParent (EKeymap parent)
95   {
96     if (parent == null)
97       this.parents = empty;
98     else
99       this.parents = new EKeymap[] { parent };
100   }
101
102   public EKeymap getParent ()
103   {
104     int num = parents.length;
105     if (num == 0)
106       return null;
107     if (num == 1)
108       return parents[0];
109     throw new Error JavaDoc("multiple parents - set getParents, not getParent");
110   }
111
112   public void setAction(int key, Object JavaDoc command)
113   {
114     set(key, command);
115   }
116
117   public Object JavaDoc get (int key, int modifiers, boolean acceptDefaults)
118   {
119     return get (key | (modifiers << 16), acceptDefaults);
120   }
121
122   protected Object JavaDoc get (int key, boolean acceptDefaults)
123   {
124     Object JavaDoc value = super.lookup(key, null);
125     if (value != null)
126       return value;
127     if (acceptDefaults && defaultBinding != null)
128       return defaultBinding;
129     int plen = parents.length;
130     for (int i = 0; i <plen; i++)
131       {
132     value = parents[i].get(key, acceptDefaults);
133     if (value != null)
134       return value;
135       }
136     return null;
137   }
138
139   public String JavaDoc toString ()
140   {
141     StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc(40);
142     sbuf.append("#<keymap ");
143     if (name != null)
144       {
145     sbuf.append(name);
146     sbuf.append(' ');
147       }
148     /*
149     sbuf.append("size ");
150     sbuf.append('?');
151     */

152     sbuf.append("0x");
153     sbuf.append(Integer.toHexString(System.identityHashCode(this)));
154     sbuf.append('>');
155     return sbuf.toString();
156   }
157
158   /** Get or create keymap associate with a prefix key in a given keymap. */
159   public EKeymap definePrefix(int key)
160   {
161     Object JavaDoc command = get(key, false);
162     Object JavaDoc cc = command;
163     Object JavaDoc x;
164     if (command == null)
165       {
166         EKeymap next = new EKeymap(null);
167         set(key, next);
168         return next;
169       }
170     command = Command.resolveSymbol(command);
171     if (command instanceof EKeymap)
172       return (EKeymap) command;
173     else
174       {
175         throw new Error JavaDoc("prefix command cannot override exiting action: "
176             + cc+ " : "+cc.getClass()+" -> "+command+" key "+key+"="+toString(key)+" in "+this);
177       }
178   }
179
180   public void defineKey(Object JavaDoc keySpec, Object JavaDoc binding)
181   {
182     EKeymap keymap = this;
183     if (keySpec instanceof Sequence && ! (keySpec instanceof LList))
184       {
185         // Handle key sequence.
186
Sequence value = (Sequence) keySpec;
187         boolean hackMeta = keySpec instanceof FString;
188         int len = value.size();
189         for (int i = 0; i < len; )
190           {
191             Object JavaDoc keyValue = value.get(i);
192             boolean sawMeta = false;
193             i++;
194         int key = asKeyStroke(keyValue);
195         if (key == 0)
196           throw new Error JavaDoc("unknown keyspec: "+keyValue);
197         if (hackMeta && key > 127 && key <= 255)
198           {
199         sawMeta = true;
200         key = key - 128;
201           }
202         if ((getModifiers(key) & META_MASK) != 0)
203           {
204         key = stripMeta(key);
205         sawMeta = true;
206           }
207             if (sawMeta)
208               keymap = keymap.definePrefix(metaKey);
209             if (i < len)
210               keymap = keymap.definePrefix(key);
211             else
212               keymap.defineKey(key, binding);
213           }
214       }
215     else
216       {
217         // Handle single key.
218
int key = asKeyStroke(keySpec);
219     if (key == 0)
220       throw new Error JavaDoc("unknown keyspec: "+keySpec);
221         defineKey(key, binding);
222       }
223   }
224
225   public void defineKey(int key, Object JavaDoc binding)
226   {
227     boolean sawMeta = false;
228     if ((getModifiers(key) & META_MASK) != 0)
229       {
230     key = stripMeta(key);
231     sawMeta = true;
232       }
233     EKeymap keymap = this;
234     if (sawMeta)
235       keymap = keymap.definePrefix(metaKey);
236     keymap.setAction(key, binding);
237   }
238
239   public static int asKeyStroke(char ch, int mods)
240   {
241     if (mods == SHIFT_MASK && Character.isLetter(ch))
242       {
243     ch = Character.toUpperCase(ch);
244     mods = 0;
245       }
246     if (ch < ' ')
247       {
248         mods |= CTRL_MASK|PRESSED;
249         ch = ch == '\0' ? ' ' : (char) ('@' + (ch & 31));
250       }
251     return ch | (mods << 16);
252   }
253
254   /** Map an Emacs key name to one of the KeyEVent VK_XXX codes.
255    * Returns VK_UNDEFINED if the name isn't recognized.
256    */

257   public static int getKeyForName (String JavaDoc name)
258   {
259     name = name.toLowerCase();
260     int len = name.length();
261     if (len == 0)
262       return KeyEvent.VK_UNDEFINED;
263     char c0 = name.charAt(0);
264     if (len == 1)
265       return c0;
266     switch (c0)
267       {
268       case 'b':
269     if (name == "backspace") return KeyEvent.VK_BACK_SPACE;
270     break;
271       case 'd':
272     if (name == "delete") return KeyEvent.VK_DELETE;
273     if (name == "down") return KeyEvent.VK_DOWN;
274     break;
275       case 'e':
276     if (name == "enter") return KeyEvent.VK_ENTER;
277     break;
278       case 'f':
279     if (len == 2)
280       {
281         char c1 = name.charAt(1);
282         if (c1 > '0' && c1 <= '9')
283           return KeyEvent.VK_F1 + c1 - '1';
284       }
285     else if (len == 3 && name.charAt(0) == 'f')
286       {
287         int c1 = name.charAt(1);
288         int c2 = name.charAt(2);
289         if (c1 > '0' && c1 <= '9'
290         && c2 > '0' && c2 <= '9'
291         && (c1 = (c1 - '0') * 10 + (c2 - '0')) <= 24)
292           {
293         if (c1 <= 12)
294           return KeyEvent.VK_F10 + c2 - '0';
295         else
296           return KeyEvent.VK_F13 + c1 - 13;
297           }
298       }
299     break;
300       case 'h':
301     if (name == "help") return KeyEvent.VK_HELP;
302     break;
303       case 'k':
304     if (name == "kp-left") return KeyEvent.VK_KP_LEFT;
305     else if (name == "kp-right") return KeyEvent.VK_KP_RIGHT;
306     else if (name == "kp-up") return KeyEvent.VK_KP_UP;
307     else if (name == "kp-down") return KeyEvent.VK_KP_DOWN;
308     else if (name == "kp-delete") return KeyEvent.VK_DELETE;
309     break;
310       case 'l':
311     if (name == "left") return KeyEvent.VK_LEFT;
312     break;
313       case 'n':
314     if (name == "next") return KeyEvent.VK_PAGE_DOWN;
315     break;
316       case 'p':
317     if (name == "prior") return KeyEvent.VK_PAGE_UP;
318     break;
319       case 'r':
320     if (name == "enter") return KeyEvent.VK_ENTER;
321     if (name == "return") return '\r';
322     if (name == "right") return KeyEvent.VK_RIGHT;
323     break;
324       case 't':
325     if (name == "tab") return KeyEvent.VK_TAB;
326     break;
327       case 'u':
328     if (name == "up") return KeyEvent.VK_UP;
329     break;
330       }
331     return KeyEvent.VK_UNDEFINED;
332   }
333
334   public static int asKeyStroke(Object JavaDoc key)
335   {
336     int m = 0;
337     while (key instanceof Pair)
338       {
339     Pair pair = (Pair) key;
340     if (pair.cdr == LList.Empty)
341       key = pair.car;
342     else
343       {
344         Object JavaDoc car = pair.car;
345         if (car instanceof Symbol)
346           car = ((Symbol) car).getName();
347         if (car == "control")
348           m |= CTRL_MASK;
349         if (car == "meta")
350           m |= META_MASK;
351         if (car == "shift")
352           m |= SHIFT_MASK;
353         if (car == "alt")
354           m |= ALT_MASK;
355         key = pair.cdr;
356       }
357       }
358     if (key instanceof Char)
359       {
360     return asKeyStroke(((Char) key).charValue(), m);
361       }
362     if (key instanceof IntNum)
363       {
364     return asKeyStroke((char) ((IntNum) key).intValue(), m);
365       }
366     if (key instanceof String JavaDoc || key instanceof Symbol)
367       {
368     String JavaDoc name = key instanceof String JavaDoc ? (String JavaDoc) key
369       : ((Symbol) key).getName();
370     if (name.length() == 1)
371       {
372         char ch = name.charAt(0);
373         if (m == 0)
374           return asKeyStroke((char) ch, 0);
375         else
376           {
377         ch = Character.toUpperCase(ch);
378         return asKeyStroke(ch, m);
379           }
380       }
381     int code = getKeyForName(name);
382     if (code == KeyEvent.VK_UNDEFINED)
383       throw new Error JavaDoc("unknown key-name: "+name);
384     return code | ((m|PRESSED) << 16);
385       }
386     return 0; // FIXME toInt((KeyStroke) key);
387
}
388
389   public static int stripMeta(int key)
390   {
391     int mods = getModifiers(key);
392     if ((mods & META_MASK) == 0)
393       return key;
394     mods &= ~ META_MASK;
395     int code = key & 0xFFFF;
396     boolean onRelease = (key & (RELEASED << 16)) != 0;
397     if ((mods & ~SHIFT_MASK) != 0 || onRelease
398         || code > 127 || code < ' ')
399       return code | ((mods|RELEASED) << 16);
400     else
401       {
402         if (code >= 'A' && code <= 'Z'&& mods != SHIFT_MASK)
403           code = code + 'a' - 'A';
404         return code;
405       }
406   }
407
408   /**
409    * True for a KeyStroke if the default action should be to ignore it.
410    * For example, pressing a shift key should not be an action!
411    * We also have the complication that both KEY-PRESSED and KEY_TYPED
412    * events and we typically want to ignore one but not both.
413    * (If both are handled, we have problems with default actions, as
414    * well as when to abort a prefix sequence. Swing does not have
415    * this problem because it does not have prefix sequences and hence state.)
416    */

417   public static boolean ignorable (int key)
418   {
419     if ((key & (RELEASED << 16)) != 0)
420       return true;
421     int mods = getModifiers(key);
422     // If there are no modifiers, and it's a normal non-control character,
423
// we prefer the KEY_TYPED (keyChar) event.
424
// Otherwise, we prefer the KEY_PRESSED (keyCode) event.
425
int code = key & 0xFFFF;
426     if ((key & (PRESSED << 16)) == 0)
427       { // It's a KEY_TYPED (keyChar) event.
428
char ch = (char) key;
429         return (ch < ' ' && ch != metaKey) || ch >= 127;
430       }
431     else
432       { // It's a KEY_PRESSED (keyCODE) event.
433
// Basically, in the case of KEY_PRESSED events that will
434
// map without loss of information into normal KEY_TYPED events,
435
// we prefer the KEY_TYPED events (as they don't depend on the
436
// keyboard layout).
437
if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_SHIFT
438         || code == KeyEvent.VK_ALT || code == KeyEvent.VK_META
439             || code == KeyEvent.VK_ESCAPE)
440       return true;
441         return (mods & ~SHIFT_MASK) == 0
442       && code >= KeyEvent.VK_SPACE && code < KeyEvent.VK_DELETE;
443       }
444   }
445
446   /*
447   public static String toString(KeyStroke key)
448   {
449     StringBuffer sbuf = new StringBuffer();
450     sbuf.append('[');
451     char ch = key.getKeyChar();
452     if (ch != '\0')
453       {
454         sbuf.append("char:'");
455         gnu.jemacs.lang.ELisp.readableChar(ch, sbuf, true);
456         sbuf.append("'");
457       }
458     int code = key.getKeyCode();
459     if (code != 0)
460       {
461         sbuf.append("code:");
462         sbuf.append(code);
463       }
464     int mods = key.getModifiers();
465     if (mods != 0)
466       {
467         sbuf.append(" mods:");
468         sbuf.append(mods);
469       }
470     if (key.isOnKeyRelease())
471       sbuf.append(" release");
472     sbuf.append(']');
473     return sbuf.toString();
474   }
475   */

476
477   public static String JavaDoc toString(int code)
478   {
479     StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
480     sbuf.append('[');
481     if (((code >> 16) & (PRESSED|RELEASED)) == 0)
482       {
483         sbuf.append("char:'");
484         gnu.jemacs.lang.ELisp.readableChar((char) code, sbuf, true);
485         sbuf.append("'");
486       }
487     else
488       {
489         sbuf.append("code:");
490         sbuf.append(code & 0xFFFF);
491       }
492     int mods = (code >> 16) & 0xff;
493     if (mods != 0)
494       {
495         sbuf.append(" mods:");
496         sbuf.append(mods);
497       }
498     if ((code & (RELEASED << 16)) != 0)
499       sbuf.append(" release");
500     sbuf.append(']');
501     return sbuf.toString();
502   }
503
504   public Object JavaDoc
505   lookupKey(Sequence keys, boolean acceptDefaults)
506   {
507     int nKeys = keys.size();
508     int[] prefixKeys = new int[nKeys];
509     java.util.Enumeration JavaDoc enumKeys = keys.elements();
510     for (int i = 0; enumKeys.hasMoreElements(); i++)
511       {
512         prefixKeys[i] = asKeyStroke(enumKeys.nextElement());
513       }
514     return lookupKey(prefixKeys, nKeys, 0, acceptDefaults);
515   }
516
517   public Object JavaDoc lookupKey(int[] prefixKeys, int nPrefix,
518               int key, boolean acceptDefaults)
519   {
520     EKeymap keymap = this;
521     int nKeys = nPrefix + (key != 0 ? 1 : 0);
522     boolean pendingMeta = false;
523     if (nKeys == 0)
524      throw new Error JavaDoc("no keys");
525     for (int i = 0; ; )
526       {
527         int key_i = i == nPrefix ? key : prefixKeys[i];
528
529         if (pendingMeta)
530           key_i |= ((META_MASK|PRESSED) << 16);
531         Object JavaDoc command = keymap.get(key_i, false);
532         Object JavaDoc metaCommand;
533         if (command == null
534             && (getModifiers(key_i) & META_MASK) != 0
535             && (metaCommand = keymap.get(metaKey, false)) instanceof EKeymap)
536           {
537             command = ((EKeymap) metaCommand).get(stripMeta(key_i), false);
538           }
539         i++;
540         if (command == null)
541           {
542             if (ignorable(key))
543             {
544               return EToolkit.getInstance().getIgnoreAction();
545             }
546             else
547           return keymap.getDefaultBinding();
548           }
549         if (i == nKeys)
550           return command;
551     Object JavaDoc comm;
552     if (command instanceof String JavaDoc || command instanceof Symbol)
553       command = Command.resolveSymbol(command);
554     if (command instanceof EKeymap)
555       keymap = (EKeymap) command;
556     else
557       return null;
558       }
559   }
560
561   /*
562    * For debugging
563    */

564   public static String JavaDoc show(int binary)
565   {
566     StringBuffer JavaDoc sb = new StringBuffer JavaDoc(Integer.toBinaryString(binary));
567     for (int i = 32 - sb.length() - 1; i >= 0; i--)
568       sb.insert(0, '0');
569     return sb.toString();
570   }
571 }
572
573
Popular Tags