]> git.lyx.org Git - lyx.git/blob - src/kbmap.C
020623df01c1e5b17fc4b3f0d3db090a7741b8a6
[lyx.git] / src / kbmap.C
1 /* This file is part of
2  * ======================================================
3  * 
4  *          LyX, The Document Processor
5  *       
6  *          Copyright 1995 Matthias Ettrich
7  *          Copyright 1995-1999 The LyX Team.
8  *
9  * ======================================================*/
10
11 #include <config.h>
12 #include <cstring>
13 #include <cstdio>
14 #include "support/lstrings.h"
15 #include "gettext.h"
16
17 #ifdef __GNUG__
18 #pragma implementation
19 #endif
20
21 #include "kbmap.h"
22 #include "error.h"
23
24 // The only modifiers that we handle. We want to throw away things
25 // like NumLock. 
26 enum { ModsMask = ShiftMask | ControlMask | Mod1Mask};
27
28
29 // === static functions ===================================================
30
31
32 /* ---F+------------------------------------------------------------------ *\
33    Function  : printKeysym
34    Called by : kb_sequence::print and printKeyMap. RVDK_PATCH_5
35    Purpose   : prints a keysym, including modifiers.
36    Parameters: key    - keysym
37                mod    - modifiers
38                buf    - string where the result goes
39                maxlen - length of string (including '\0')
40    Returns   : length of printed string if ok, 0 otherwise.
41 \* ---F------------------------------------------------------------------- */
42
43 static
44 int printKeysym( KeySym key, unsigned int mod, char *buf, int maxlen )
45 {
46         int len;
47         char *s;
48         
49         mod &= ModsMask;
50
51         // calc required length;
52         len = 0;
53         if ( mod & ShiftMask )   len += 2;
54         if ( mod & ControlMask ) len += 2;
55         if ( mod & Mod1Mask )    len += 2;
56         
57         s = XKeysymToString( key );
58         if ( s ) len += strlen( s );
59         if ( len < maxlen ) {
60                 if ( mod & ShiftMask ) {
61                         *buf++ = 'S'; *buf++ = '-'; }
62                 if ( mod & ControlMask ) {
63                         *buf++ = 'C'; *buf++ = '-'; }
64                 if ( mod & Mod1Mask ) {
65                         *buf++ = 'M'; *buf++ = '-'; }
66                 if ( s ) strcpy( buf, s );
67                 return len;
68         } else 
69                 return 0;
70 }
71
72
73 /* ---F+------------------------------------------------------------------ *\
74    Function  : printKeyTab
75    Called by : kb_keymap::print
76    Purpose   : print the keysyms found in the given key table. RVDK_PATCH_5
77    Parameters: tabPt  - keytable pointer
78                buf    - string where the result goes
79                maxLen - length of string (including '\0')
80    Returns   : length of printed string.
81 \* ---F------------------------------------------------------------------- */
82
83 static
84 int printKeyTab( kb_key *tabPt, char *buf, int maxLen )
85 {
86         int len, doneLen = 0;
87         unsigned int ksym, mod;
88         
89         /* -------> Print each of the slots into buf. */
90         for( ; (tabPt->code & 0xffff) != NoSymbol; tabPt++) {
91                 if ( maxLen <= 0 ) break;
92                 
93                 ksym =  tabPt->code;
94                 mod  =  tabPt->mod & 0xffff;
95                 
96                 len = printKeysym( ksym, mod, buf, maxLen );
97                 if ( len <= 0 ) break;
98                 buf     += len;
99                 maxLen  -= len;
100                 doneLen += len;
101                 
102                 /* -------> Add space when possible. */
103                 if ( maxLen > 0 ) {
104                         *buf++ = ' ';
105                         *buf = '\0';
106                         maxLen--;
107                         doneLen++;
108                 }
109         }
110         return doneLen;
111 }
112
113
114
115 // === kb_sequence methods ================================================
116
117
118
119 /* ---F+------------------------------------------------------------------ *\
120     Function  : kb_sequence::addkey
121     Called by : [user]
122     Purpose   : add a key to the sequence, look up in map and return action
123     Parameters: key  - keysym of key
124                 mod  - modifier mask
125                 nmod - modifier veto mask (unused now)
126     Returns   : action or -1 if error (no map defined or key not found)
127 \* ---F------------------------------------------------------------------- */
128
129 int kb_sequence::addkey(KeySym key, unsigned int mod, unsigned int nmod /*=0*/)
130 {
131         if(length<0) length=0;
132
133         if(length+1 >= size) {
134                 unsigned int *nseq = new unsigned int[size+KB_PREALLOC];
135                 size += KB_PREALLOC;
136                 memcpy(nseq, sequence, length*sizeof(unsigned int));
137                 if(sequence != staticseq) delete sequence;
138                 sequence = nseq;
139                 nseq = new unsigned int[size];
140                 memcpy(nseq, modifiers, length*sizeof(unsigned int));
141                 if(modifiers != staticmod) delete modifiers;
142                 modifiers = nseq;
143         }
144
145         modifiers[length]  = mod + (nmod<<16);
146         sequence[length++] = key;
147    
148         if(curmap)
149                 return curmap->lookup(key, mod, this);
150         
151         return -1;
152 }
153
154
155 /* ---F+------------------------------------------------------------------ *\
156     Function  : kb_sequence::parse
157     Called by : [user]
158     Purpose   : parse a string that holds a key sequence and add the keys
159     Parameters: s - string holding the key sequence
160     Returns   : 0 - if ok, error pos if error
161     Note      : Keys must be separated with whitespace;
162                 Use the keysym names used by XStringToKeysym
163                 Prefixes are S-, C-, M- for shift, control, meta
164 \* ---F------------------------------------------------------------------- */
165
166 int kb_sequence::parse(char const*s)
167 {
168         int i = 0;
169         unsigned int mod = 0, nmod = 0;
170         KeySym key = 0;
171         char tbuf[100];
172         
173         if(!s[0]) return 1;
174         
175         while(s[i]) {
176                 if(s[i] && ((unsigned char) s[i]) <= ' ') i++;
177                 if(!s[i]) break;
178                 
179                 if(s[i+1]=='-') { // is implicit that s[i]==true
180                         switch(s[i]) {
181                         case 's': case 'S':
182                                 mod |= ShiftMask;
183                                 i+=2;
184                                 continue;
185                         case 'c': case 'C':
186                                 mod |= ControlMask;
187                                 i+=2;
188                                 continue;
189                         case 'm': case 'M':
190                                 mod |= Mod1Mask;
191                                 i+=2;
192                                 continue;
193                         default:
194                                 return i+1;
195                         }
196                 } else if(s[i]=='~' && s[i+1] && s[i+2]=='-') {
197                         switch(s[i+1]) {
198                         case 's': case 'S':
199                                 nmod |= ShiftMask;
200                                 i+=3;
201                                 continue;
202                         case 'c': case 'C':
203                                 nmod |= ControlMask;
204                                 i+=3;
205                                 continue;
206                         case 'm': case 'M':
207                                 nmod |= Mod1Mask;
208                                 i+=3;
209                                 continue;
210                         default:
211                                 return i+2;
212                         }
213                 } else {
214                         int j = 0;
215                         for(j = i; s[j] && ((unsigned char)s[j])>' '; j++)
216                                 tbuf[j-i] = s[j];    // (!!!check bounds :-)
217                         
218                         tbuf[j-i] = '\0';
219          
220                         key = XStringToKeysym(tbuf);
221                         if(key == NoSymbol) {
222                                 lyxerr.debug("kbmap.C: No such keysym: "
223                                              + string(tbuf),Error::KBMAP);
224                                 return j;
225                         }
226                         i = j;
227                         
228                         addkey(key, mod, nmod);
229                         mod = 0;
230                         nmod = 0;
231                 }
232         }
233         return 0;
234 }
235
236
237 /* ---F+------------------------------------------------------------------ *\
238     Function  : kb_sequence::print
239     Called by : [user]
240     Purpose   : print the currently defined sequence into a string
241     Parameters: buf           - string where the result goes
242                 maxlen        - length of string (including '\0')
243                 when_defined  - only  print when sequence is real: length > 0.
244     Returns   : 0, if ok, -1 if string too long
245 \* ---F------------------------------------------------------------------- */
246
247 int kb_sequence::print(char *buf, int maxlen, bool when_defined) const
248 {
249         KeySym key;
250         unsigned int mod;
251         int len;
252         int l = length;
253         if ( l<0 && !when_defined ) l = -l;
254         
255         for(int i = 0; i < l; i++) {
256                 key = sequence[i];
257                 mod = modifiers[i] & 0xffff;
258
259                 len = printKeysym( key, mod, buf, maxlen );  // RVDK_PATCH_5
260                 buf += len;
261                 maxlen -= len;
262                 
263                 if ( len == 0 ) {
264                         *buf = '\0';
265                         return -1;
266                 }
267
268                 if(i+1<l && maxlen>1) {  // append a blank
269                         *buf++ = ' ';
270                         maxlen--;
271                 }
272         }
273         *buf = '\0';
274         return 0;
275 }
276
277
278 /* ---F+------------------------------------------------------------------ *\
279     Function  : kb_sequence::printOptions
280     Called by : [user]
281     Purpose   : print the available key options from the current state in the
282                 sequence. RVDK_PATCH_5
283     Parameters: buf    - string where the result goes
284                 maxlen - length of string (including '\0')
285     Returns   : 0, if ok, -1 if string too long
286 \* ---F------------------------------------------------------------------- */
287
288 int kb_sequence::printOptions(char *buf, int maxlen) const
289 {
290         int len;
291
292         print( buf, maxlen, true );
293         len = strlen( buf );
294         maxlen -= len;
295         buf    += len;
296         
297         if ( maxlen < 20 || !curmap ) return -1;
298 #ifdef WITH_WARNINGS
299 #warning reimplement kb_sequence using string
300 #endif
301         char s[20];
302         strcpy(s,_("   options: "));
303         strcpy( buf, s);
304         buf += strlen(s);
305         maxlen -= strlen(s);
306
307         curmap->print(buf, maxlen);
308         return 0;
309 }
310
311
312 /* ---F+------------------------------------------------------------------ *\
313     Function  : kb_sequence::delseq
314     Called by : [user]
315     Purpose   : mark the sequence as deleted
316     Parameters: none
317     Returns   : nothing
318 \* ---F------------------------------------------------------------------- */
319
320 void kb_sequence::delseq()
321 {
322         // negative length marks sequence as deleted, but we can still
323         // print() it or retrieve the last char using getiso()
324         length = -length;
325 }
326
327
328 /* ---F+------------------------------------------------------------------ *\
329    Function  : kb_sequence::getsym
330    Called by : [user], getiso
331    Purpose   : get the keysym of the last key in sequence
332    Parameters: none
333    Returns   : keysym
334 \* ---F------------------------------------------------------------------- */
335
336 KeySym kb_sequence::getsym()
337 {
338         int l = length;
339         if(l==0) return NoSymbol;
340         if(l<0) l = -l;
341         return sequence[l-1];
342 }
343
344
345 /* ---F+------------------------------------------------------------------ *\
346     Function  : kb_sequence::getiso
347     Called by : [user]
348     Purpose   : return iso character code of last key, if any
349     Parameters: none
350     Returns   : iso code or 0 if none
351 \* ---F------------------------------------------------------------------- */
352
353 char kb_sequence::getiso()
354 {
355         int c = getsym();
356         
357         if(c > 0xff)
358                 return '\0';
359         return (char)c;
360 }
361
362
363 /* ---F+------------------------------------------------------------------ *\
364     Function  : kb_sequence::reset
365     Called by : [user]
366     Purpose   : reset sequence to initial state. RVDK_PATCH_5
367     Parameters: none
368     Returns   : void
369 \* ---F------------------------------------------------------------------- */
370
371 void kb_sequence::reset()
372 {
373         delseq();
374         curmap = stdmap;
375         if ( length > 0 ) length = -length;
376 }
377
378
379 // === kb_keymap methods ==================================================
380
381 // This binds a key to an action
382 int kb_keymap::bind(char const *seq, int action)
383 {
384         kb_sequence k;
385
386         int res = k.parse(seq);
387         if (!res) {
388                 defkey(&k, action);
389         } else
390                 lyxerr.debug(string("Parse error at position ") + tostr(res) +
391                              " in key sequence '" + seq + "'.", Error::KBMAP);
392         return res;
393 }
394
395
396 /* ---F+------------------------------------------------------------------ *\
397     Function  : kb_keymap::lookup
398     Called by : [user], kb_sequence::add()
399     Purpose   : look up a key press in a given keymap
400     Parameters: key - the keysym of the key press
401                 mod - the modifier mask of the keypress
402                 seq - the key-sequence retrieved so far
403     Returns   : user defined action; 0 for prefix key, -1 if key not found
404 \* ---F------------------------------------------------------------------- */
405
406 int kb_keymap::lookup(KeySym key, unsigned int mod, kb_sequence *seq)
407 {
408         unsigned int hashval, ksym, msk1, msk0;
409         kb_key *tab;
410
411         //suppress modifier bits we do not handle
412         mod &= ModsMask;
413
414         if(!table) {
415                 // error - no keymap defined:
416                 seq->curmap = seq->stdmap;
417                 seq->delseq();
418                 return -1;
419         }
420
421         if(size < 0) {               // --- if hash table ---
422                 hashval = ((key&0xff) ^ ((key>>8)&0xff)) % KB_HASHSIZE;
423                 tab = htable[hashval];
424                 if(!tab) {
425                         seq->curmap = seq->stdmap;
426                         seq->delseq();
427                         return -1;
428                 }
429         } else                       // --- else: linear list ---
430                 tab = table;
431
432         // --- now search the list of keys ---
433
434         for( ; (tab->code & 0xffff) != NoSymbol; tab++) {
435                 ksym =  tab->code;
436                 msk1 =  tab->mod      & 0xffff;
437                 msk0 = (tab->mod>>16) & 0xffff;
438
439                 if(ksym == key && (mod&~msk0) == msk1) {
440                         // match found:
441                         if(tab->table) {
442                                  // this is a prefix key - set new map
443                                 seq->curmap = tab->table;
444                                 return 0;
445                         } else {
446                                   // final key - reset map
447                                 seq->curmap = seq->stdmap;
448                                 seq->delseq();
449                                 return tab->action; // ... and return action
450                         }
451                 }
452         }
453         
454         // error - key not found:
455         seq->curmap = seq->stdmap;
456         seq->delseq();
457         return -1;
458 }
459
460
461 /* ---F+------------------------------------------------------------------ *\
462     Function  : kb_keymap::print
463     Called by : [user]
464     Purpose   : Prints all the available keysyms. RVDK_PATCH_5
465     Parameters: buf    - string where output goes.
466                maxLen - available length in string, including `\0'.
467     Returns   : updated maxLen.
468 \* ---F------------------------------------------------------------------- */
469
470 int kb_keymap::print(char *buf, int maxLen) const
471 {
472         int                    len;
473         
474  /* -----> Return when running out of string space or when keymap has no table.
475      Else, place a terminating newline in case no other output is generated. */
476
477         if ( maxLen <= 3 || !buf ) return maxLen;
478         if ( !table ) return maxLen;
479         *buf   = '\0';
480    
481  /* -------> Hash table. Process each of its slots recursively and return. */
482         if ( size < 0 ) {   
483                 for ( int ix = 0; (ix < KB_HASHSIZE) && (maxLen > 1); ix++ ) {
484                         if ( htable[ix] ) {
485                                 len = printKeyTab( htable[ix], buf, maxLen );
486                                 maxLen -= len;
487                                 buf    += len;
488                         }
489                 }
490         } else {
491                 /* -------> Normal table. */
492                 len = printKeyTab( table, buf, maxLen ); 
493                 maxLen -= len;
494                 buf    += len;
495         }
496         return maxLen;
497 }
498
499
500 /* ---F+------------------------------------------------------------------ *\
501     Function  : kb_keymap::defkey
502     Called by : [user]
503     Purpose   : define an action for a key sequence
504     Parameters: seq    - the key sequence
505                 action - the action to be defined
506                 idx    - recursion depth
507     Returns   : 0 if ok.
508 \* ---F------------------------------------------------------------------- */
509
510 int kb_keymap::defkey(kb_sequence *seq, int action, int idx /*=0*/)
511 {
512         int      tsize;
513         unsigned int code, modmsk;
514         kb_key  *tab, **ptab;
515
516         code = seq->sequence[idx];
517         modmsk = seq->modifiers[idx];
518         if(code == NoSymbol) return -1;
519
520         // --- get list------------------------------------------------------
521         if(!table) {
522                 // If we don't have any yet, make an empty one
523                 table = new kb_key[KB_PREALLOC];
524                 table[0].code = NoSymbol;
525                 tab   =  table;
526                 ptab  = &table;
527                 size  = KB_PREALLOC;
528         } else if(size<0) {
529                 // Hash table.
530                 int hashval = (code&0xffff);
531                 hashval = ((hashval&0xff) ^ ((hashval>>8)&0xff)) % KB_HASHSIZE;
532                 tab  = htable[hashval];
533                 ptab = htable+hashval;
534                 if(!tab) {
535                         tab = new kb_key[KB_PREALLOC];
536                         tab[0].code = NoSymbol;
537                         *ptab = tab;
538                 }
539         } else {
540                 tab  =  table;
541                 ptab = &table;
542         }
543
544         // --- check if key is already there --------------------------------
545
546         kb_key *t;
547         for(t = tab, tsize=1; t->code != NoSymbol; t++, tsize++) {
548                 if(code == t->code && modmsk == t->mod) { // -- overwrite binding ---
549                         if(idx+1 == seq->length) {
550                                 char buf[20]; buf[0] = 0;
551                                 seq->print(buf, 20, true);
552                                 lyxerr.debug(string("Warning: New binding for '") + buf + 
553                                              "' is overriding old binding...", Error::KEY);
554
555                                 if(t->table) {
556                                         delete t->table;
557                                         t->table = 0;
558                                 }
559                                 t->action = action;
560                                 return 0;
561                         } else if (!t->table) {
562                                 char buf[20]; buf[0] = 0;
563                                 seq->print(buf, 20, true);
564                                 lyxerr.print(string("Error: New binding for '") + buf + 
565                                              "' is overriding old binding...");
566                                 return -1;
567                         } else
568                                 return t->table->defkey(seq, action, idx+1);
569                 }
570         }
571
572         // --- extend list if necessary -------------------------------------
573
574         if(tsize % KB_PREALLOC == 0) {
575                 kb_key *nt = new kb_key[tsize+KB_PREALLOC];
576                 // Set to 0 as table is used uninitialised later (thornley)
577                 nt[tsize].table = 0;
578                 memcpy(nt, tab, tsize*sizeof(kb_key));
579                 *ptab = nt;
580                 delete[] tab;
581                 tab = nt;
582                 if(size>=0) size = tsize+KB_PREALLOC;
583         }
584
585         // --- add action ---------------------------------------------------
586
587         tab[tsize--].code = NoSymbol;
588         tab[tsize].code = code;
589         tab[tsize].mod  = modmsk;
590         kb_key *newone = &tab[tsize];
591         
592         // --- convert list to hash table if necessary ----------------------
593
594         if(size>=0 && tsize>=32) {
595                 kb_key *oldtab = tab;
596                 kb_key **nht = new kb_key*[KB_HASHSIZE];
597                 for(int i = 0; i < KB_HASHSIZE; i++)
598                         nht[i] = 0;
599                 htable = nht;
600                 size   = -KB_HASHSIZE;
601                 
602                 // --- copy old keys to new hash table ---
603                 int hashval;
604                 for(kb_key *tu = oldtab; tu->code != NoSymbol; tu++){
605                         // copy values from oldtab to htable
606                         hashval = (tu->code&0xffff);
607                         hashval = ((hashval&0xff) ^ ((hashval>>8)&0xff)) % KB_HASHSIZE;
608                         tab  = htable[hashval];
609                         
610                         if(!tab){
611                                 htable[hashval] = tab = new kb_key[KB_PREALLOC];
612                                 tab->code = NoSymbol;
613                         }
614                         int ts = 1;
615                         for(kb_key *tt = tab; tt->code != NoSymbol; tt++)
616                                 ts++;
617                         if(ts % KB_PREALLOC == 0){
618                                 // extend table
619                                 kb_key *nt = new kb_key[ts+KB_PREALLOC];
620                                 memcpy(nt, tab, ts*sizeof(kb_key));
621                                 htable[hashval] = nt;
622                                 delete[] tab;
623                                 tab = nt;
624                         }
625                         tab[ts--].code = NoSymbol;
626                         tab[ts].code   = tu->code;
627                         tab[ts].mod    = tu->mod;
628                         tab[ts].action = tu->action;
629                         tab[ts].table  = tu->table;
630                         
631                         if(tu == newone)
632                                 newone = &tab[ts];
633                 }
634                 delete[] oldtab;
635         }
636         
637         // --- define rest of sequence --------------------------------------
638
639         if(idx+1 == seq->length) {
640                 newone->action = action;
641                 newone->table  = 0;
642                 return 0;
643         } else {
644                 newone->table = new kb_keymap;
645                 int res = newone->table->defkey(seq, action, idx+1);
646                 return res;
647         }
648 }
649
650
651 /* ---F+------------------------------------------------------------------ *\
652     Function  : kb_keymap::~kb_keymap
653     Called by : [destructor]
654     Purpose   : free keymap and its descendents
655     Parameters: none
656     Returns   : nothing
657 \* ---F------------------------------------------------------------------- */
658
659 kb_keymap::~kb_keymap()
660 {
661         if(!table) return;
662         if(size<0) {
663                 for(int i=0; i < KB_HASHSIZE; i++) {
664                         if(htable[i]) {
665                                 for(kb_key *t = htable[i]; t->code != NoSymbol; t++)
666                                         if(t->table)
667                                                 delete t->table;
668                                 delete htable[i];
669                         }
670                 }
671                 delete htable;
672         } else {
673                 for(kb_key *t = table; t->code != NoSymbol; t++)
674                         if(t->table)
675                                 delete t->table;
676                 delete table;
677         }
678 }
679
680 string keyname(kb_key k) {
681         char buf[100];
682         printKeysym(k.code, k.mod, buf, 100);
683         return buf;
684 }
685
686 // Finds a key for a keyaction, if possible
687 string kb_keymap::findbinding(int act) const {
688         string res;
689         if (!table)
690                 return res;
691
692         if (size<0) {
693                 for(int i=0; i < KB_HASHSIZE; i++) {
694                         if(htable[i]) {
695                                 for(kb_key *t = htable[i]; t->code != NoSymbol; t++) {
696                                         if(t->table) {
697                                                 string suffix = t->table->findbinding(act);
698                                                 suffix = strip(suffix, ' ');
699                                                 suffix = strip(suffix, ']');
700                                                 suffix = frontStrip(suffix, '[');
701                                                 if (!suffix.empty()) {
702                                                         res += "[" + keyname(*t) + " " + suffix + "] ";
703                                                 }
704                                         } else if (t->action == act) {
705                                                 res += "[" + keyname(*t) + "] ";
706                                         }
707                                 }
708                         }
709                 }
710         } else {
711                 for(kb_key *t = table; t->code != NoSymbol; t++) {
712                         if(t->table) {
713                                 string suffix = t->table->findbinding(act);
714                                 suffix = strip(suffix, ' ');
715                                 suffix = strip(suffix, ']');
716                                 suffix = frontStrip(suffix, '[');
717                                 if (!suffix.empty()) {
718                                         res += "[" + keyname(*t) + " " + suffix + "] ";
719                                 }
720                         } else if (t->action == act) {
721                                 res += "[" + keyname(*t) + "] ";
722                         }
723                 }
724         }
725         return res;
726 }
727
728
729 /* === End of File: kbmap.C ============================================== */