]> git.lyx.org Git - lyx.git/blob - src/kbmap.C
9808f34dd05afbebd96457b430720e0af4c37c2f
[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 "debug.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]
223                                         << "kbmap.C: No such keysym: "
224                                         << tbuf << endl;
225                                 return j;
226                         }
227                         i = j;
228                         
229                         addkey(key, mod, nmod);
230                         mod = 0;
231                         nmod = 0;
232                 }
233         }
234         return 0;
235 }
236
237
238 /* ---F+------------------------------------------------------------------ *\
239     Function  : kb_sequence::print
240     Called by : [user]
241     Purpose   : print the currently defined sequence into a string
242     Parameters: buf           - string where the result goes
243                 maxlen        - length of string (including '\0')
244                 when_defined  - only  print when sequence is real: length > 0.
245     Returns   : 0, if ok, -1 if string too long
246 \* ---F------------------------------------------------------------------- */
247
248 int kb_sequence::print(char *buf, int maxlen, bool when_defined) const
249 {
250         KeySym key;
251         unsigned int mod;
252         int len;
253         int l = length;
254         if ( l<0 && !when_defined ) l = -l;
255         
256         for(int i = 0; i < l; i++) {
257                 key = sequence[i];
258                 mod = modifiers[i] & 0xffff;
259
260                 len = printKeysym( key, mod, buf, maxlen );  // RVDK_PATCH_5
261                 buf += len;
262                 maxlen -= len;
263                 
264                 if ( len == 0 ) {
265                         *buf = '\0';
266                         return -1;
267                 }
268
269                 if(i+1<l && maxlen>1) {  // append a blank
270                         *buf++ = ' ';
271                         maxlen--;
272                 }
273         }
274         *buf = '\0';
275         return 0;
276 }
277
278
279 /* ---F+------------------------------------------------------------------ *\
280     Function  : kb_sequence::printOptions
281     Called by : [user]
282     Purpose   : print the available key options from the current state in the
283                 sequence. RVDK_PATCH_5
284     Parameters: buf    - string where the result goes
285                 maxlen - length of string (including '\0')
286     Returns   : 0, if ok, -1 if string too long
287 \* ---F------------------------------------------------------------------- */
288
289 int kb_sequence::printOptions(char *buf, int maxlen) const
290 {
291         int len;
292
293         print( buf, maxlen, true );
294         len = strlen( buf );
295         maxlen -= len;
296         buf    += len;
297         
298         if ( maxlen < 20 || !curmap ) return -1;
299 #ifdef WITH_WARNINGS
300 #warning reimplement kb_sequence using string
301 #endif
302         char s[20];
303         strcpy(s,_("   options: "));
304         strcpy( buf, s);
305         buf += strlen(s);
306         maxlen -= strlen(s);
307
308         curmap->print(buf, maxlen);
309         return 0;
310 }
311
312
313 /* ---F+------------------------------------------------------------------ *\
314     Function  : kb_sequence::delseq
315     Called by : [user]
316     Purpose   : mark the sequence as deleted
317     Parameters: none
318     Returns   : nothing
319 \* ---F------------------------------------------------------------------- */
320
321 void kb_sequence::delseq()
322 {
323         // negative length marks sequence as deleted, but we can still
324         // print() it or retrieve the last char using getiso()
325         length = -length;
326 }
327
328
329 /* ---F+------------------------------------------------------------------ *\
330    Function  : kb_sequence::getsym
331    Called by : [user], getiso
332    Purpose   : get the keysym of the last key in sequence
333    Parameters: none
334    Returns   : keysym
335 \* ---F------------------------------------------------------------------- */
336
337 KeySym kb_sequence::getsym()
338 {
339         int l = length;
340         if(l==0) return NoSymbol;
341         if(l<0) l = -l;
342         return sequence[l-1];
343 }
344
345
346 /* ---F+------------------------------------------------------------------ *\
347     Function  : kb_sequence::getiso
348     Called by : [user]
349     Purpose   : return iso character code of last key, if any
350     Parameters: none
351     Returns   : iso code or 0 if none
352 \* ---F------------------------------------------------------------------- */
353
354 char kb_sequence::getiso()
355 {
356         int c = getsym();
357         
358         if(c > 0xff)
359                 return '\0';
360         return (char)c;
361 }
362
363
364 /* ---F+------------------------------------------------------------------ *\
365     Function  : kb_sequence::reset
366     Called by : [user]
367     Purpose   : reset sequence to initial state. RVDK_PATCH_5
368     Parameters: none
369     Returns   : void
370 \* ---F------------------------------------------------------------------- */
371
372 void kb_sequence::reset()
373 {
374         delseq();
375         curmap = stdmap;
376         if ( length > 0 ) length = -length;
377 }
378
379
380 // === kb_keymap methods ==================================================
381
382 // This binds a key to an action
383 int kb_keymap::bind(char const *seq, int action)
384 {
385         kb_sequence k;
386
387         int res = k.parse(seq);
388         if (!res) {
389                 defkey(&k, action);
390         } else
391                 lyxerr[Debug::KBMAP] << "Parse error at position " << res
392                                      << " in key sequence '" << seq << "'."
393                                      << endl;
394         return res;
395 }
396
397
398 /* ---F+------------------------------------------------------------------ *\
399     Function  : kb_keymap::lookup
400     Called by : [user], kb_sequence::add()
401     Purpose   : look up a key press in a given keymap
402     Parameters: key - the keysym of the key press
403                 mod - the modifier mask of the keypress
404                 seq - the key-sequence retrieved so far
405     Returns   : user defined action; 0 for prefix key, -1 if key not found
406 \* ---F------------------------------------------------------------------- */
407
408 int kb_keymap::lookup(KeySym key, unsigned int mod, kb_sequence *seq)
409 {
410         unsigned int hashval, ksym, msk1, msk0;
411         kb_key *tab;
412
413         //suppress modifier bits we do not handle
414         mod &= ModsMask;
415
416         if(!table) {
417                 // error - no keymap defined:
418                 seq->curmap = seq->stdmap;
419                 seq->delseq();
420                 return -1;
421         }
422
423         if(size < 0) {               // --- if hash table ---
424                 hashval = ((key&0xff) ^ ((key>>8)&0xff)) % KB_HASHSIZE;
425                 tab = htable[hashval];
426                 if(!tab) {
427                         seq->curmap = seq->stdmap;
428                         seq->delseq();
429                         return -1;
430                 }
431         } else                       // --- else: linear list ---
432                 tab = table;
433
434         // --- now search the list of keys ---
435
436         for( ; (tab->code & 0xffff) != NoSymbol; tab++) {
437                 ksym =  tab->code;
438                 msk1 =  tab->mod      & 0xffff;
439                 msk0 = (tab->mod>>16) & 0xffff;
440
441                 if(ksym == key && (mod&~msk0) == msk1) {
442                         // match found:
443                         if(tab->table) {
444                                  // this is a prefix key - set new map
445                                 seq->curmap = tab->table;
446                                 return 0;
447                         } else {
448                                   // final key - reset map
449                                 seq->curmap = seq->stdmap;
450                                 seq->delseq();
451                                 return tab->action; // ... and return action
452                         }
453                 }
454         }
455         
456         // error - key not found:
457         seq->curmap = seq->stdmap;
458         seq->delseq();
459         return -1;
460 }
461
462
463 /* ---F+------------------------------------------------------------------ *\
464     Function  : kb_keymap::print
465     Called by : [user]
466     Purpose   : Prints all the available keysyms. RVDK_PATCH_5
467     Parameters: buf    - string where output goes.
468                maxLen - available length in string, including `\0'.
469     Returns   : updated maxLen.
470 \* ---F------------------------------------------------------------------- */
471
472 int kb_keymap::print(char *buf, int maxLen) const
473 {
474         int                    len;
475         
476  /* -----> Return when running out of string space or when keymap has no table.
477      Else, place a terminating newline in case no other output is generated. */
478
479         if ( maxLen <= 3 || !buf ) return maxLen;
480         if ( !table ) return maxLen;
481         *buf   = '\0';
482    
483  /* -------> Hash table. Process each of its slots recursively and return. */
484         if ( size < 0 ) {   
485                 for ( int ix = 0; (ix < KB_HASHSIZE) && (maxLen > 1); ix++ ) {
486                         if ( htable[ix] ) {
487                                 len = printKeyTab( htable[ix], buf, maxLen );
488                                 maxLen -= len;
489                                 buf    += len;
490                         }
491                 }
492         } else {
493                 /* -------> Normal table. */
494                 len = printKeyTab( table, buf, maxLen ); 
495                 maxLen -= len;
496                 buf    += len;
497         }
498         return maxLen;
499 }
500
501
502 /* ---F+------------------------------------------------------------------ *\
503     Function  : kb_keymap::defkey
504     Called by : [user]
505     Purpose   : define an action for a key sequence
506     Parameters: seq    - the key sequence
507                 action - the action to be defined
508                 idx    - recursion depth
509     Returns   : 0 if ok.
510 \* ---F------------------------------------------------------------------- */
511
512 int kb_keymap::defkey(kb_sequence *seq, int action, int idx /*=0*/)
513 {
514         int      tsize;
515         unsigned int code, modmsk;
516         kb_key  *tab, **ptab;
517
518         code = seq->sequence[idx];
519         modmsk = seq->modifiers[idx];
520         if(code == NoSymbol) return -1;
521
522         // --- get list------------------------------------------------------
523         if(!table) {
524                 // If we don't have any yet, make an empty one
525                 table = new kb_key[KB_PREALLOC];
526                 table[0].code = NoSymbol;
527                 tab   =  table;
528                 ptab  = &table;
529                 size  = KB_PREALLOC;
530         } else if(size<0) {
531                 // Hash table.
532                 int hashval = (code&0xffff);
533                 hashval = ((hashval&0xff) ^ ((hashval>>8)&0xff)) % KB_HASHSIZE;
534                 tab  = htable[hashval];
535                 ptab = htable+hashval;
536                 if(!tab) {
537                         tab = new kb_key[KB_PREALLOC];
538                         tab[0].code = NoSymbol;
539                         *ptab = tab;
540                 }
541         } else {
542                 tab  =  table;
543                 ptab = &table;
544         }
545
546         // --- check if key is already there --------------------------------
547
548         kb_key *t;
549         for(t = tab, tsize=1; t->code != NoSymbol; t++, tsize++) {
550                 if(code == t->code && modmsk == t->mod) { // -- overwrite binding ---
551                         if(idx+1 == seq->length) {
552                                 char buf[20]; buf[0] = 0;
553                                 seq->print(buf, 20, true);
554                                 lyxerr[Debug::KEY]
555                                         << "Warning: New binding for '"
556                                         << buf 
557                                         << "' is overriding old binding..."
558                                         << endl;
559
560                                 if(t->table) {
561                                         delete t->table;
562                                         t->table = 0;
563                                 }
564                                 t->action = action;
565                                 return 0;
566                         } else if (!t->table) {
567                                 char buf[20]; buf[0] = 0;
568                                 seq->print(buf, 20, true);
569                                 lyxerr << "Error: New binding for '" << buf
570                                        << "' is overriding old binding..."
571                                        << endl;
572                                 return -1;
573                         } else
574                                 return t->table->defkey(seq, action, idx+1);
575                 }
576         }
577
578         // --- extend list if necessary -------------------------------------
579
580         if(tsize % KB_PREALLOC == 0) {
581                 kb_key *nt = new kb_key[tsize+KB_PREALLOC];
582                 // Set to 0 as table is used uninitialised later (thornley)
583                 nt[tsize].table = 0;
584                 memcpy(nt, tab, tsize*sizeof(kb_key));
585                 *ptab = nt;
586                 delete[] tab;
587                 tab = nt;
588                 if(size>=0) size = tsize+KB_PREALLOC;
589         }
590
591         // --- add action ---------------------------------------------------
592
593         tab[tsize--].code = NoSymbol;
594         tab[tsize].code = code;
595         tab[tsize].mod  = modmsk;
596         kb_key *newone = &tab[tsize];
597         
598         // --- convert list to hash table if necessary ----------------------
599
600         if(size>=0 && tsize>=32) {
601                 kb_key *oldtab = tab;
602                 kb_key **nht = new kb_key*[KB_HASHSIZE];
603                 for(int i = 0; i < KB_HASHSIZE; i++)
604                         nht[i] = 0;
605                 htable = nht;
606                 size   = -KB_HASHSIZE;
607                 
608                 // --- copy old keys to new hash table ---
609                 int hashval;
610                 for(kb_key *tu = oldtab; tu->code != NoSymbol; tu++){
611                         // copy values from oldtab to htable
612                         hashval = (tu->code&0xffff);
613                         hashval = ((hashval&0xff) ^ ((hashval>>8)&0xff)) % KB_HASHSIZE;
614                         tab  = htable[hashval];
615                         
616                         if(!tab){
617                                 htable[hashval] = tab = new kb_key[KB_PREALLOC];
618                                 tab->code = NoSymbol;
619                         }
620                         int ts = 1;
621                         for(kb_key *tt = tab; tt->code != NoSymbol; tt++)
622                                 ts++;
623                         if(ts % KB_PREALLOC == 0){
624                                 // extend table
625                                 kb_key *nt = new kb_key[ts+KB_PREALLOC];
626                                 memcpy(nt, tab, ts*sizeof(kb_key));
627                                 htable[hashval] = nt;
628                                 delete[] tab;
629                                 tab = nt;
630                         }
631                         tab[ts--].code = NoSymbol;
632                         tab[ts].code   = tu->code;
633                         tab[ts].mod    = tu->mod;
634                         tab[ts].action = tu->action;
635                         tab[ts].table  = tu->table;
636                         
637                         if(tu == newone)
638                                 newone = &tab[ts];
639                 }
640                 delete[] oldtab;
641         }
642         
643         // --- define rest of sequence --------------------------------------
644
645         if(idx+1 == seq->length) {
646                 newone->action = action;
647                 newone->table  = 0;
648                 return 0;
649         } else {
650                 newone->table = new kb_keymap;
651                 int res = newone->table->defkey(seq, action, idx+1);
652                 return res;
653         }
654 }
655
656
657 /* ---F+------------------------------------------------------------------ *\
658     Function  : kb_keymap::~kb_keymap
659     Called by : [destructor]
660     Purpose   : free keymap and its descendents
661     Parameters: none
662     Returns   : nothing
663 \* ---F------------------------------------------------------------------- */
664
665 kb_keymap::~kb_keymap()
666 {
667         if(!table) return;
668         if(size<0) {
669                 for(int i=0; i < KB_HASHSIZE; i++) {
670                         if(htable[i]) {
671                                 for(kb_key *t = htable[i]; t->code != NoSymbol; t++)
672                                         if(t->table)
673                                                 delete t->table;
674                                 delete htable[i];
675                         }
676                 }
677                 delete htable;
678         } else {
679                 for(kb_key *t = table; t->code != NoSymbol; t++)
680                         if(t->table)
681                                 delete t->table;
682                 delete table;
683         }
684 }
685
686 string keyname(kb_key k) {
687         char buf[100];
688         printKeysym(k.code, k.mod, buf, 100);
689         return buf;
690 }
691
692 // Finds a key for a keyaction, if possible
693 string kb_keymap::findbinding(int act) const {
694         string res;
695         if (!table)
696                 return res;
697
698         if (size<0) {
699                 for(int i=0; i < KB_HASHSIZE; i++) {
700                         if(htable[i]) {
701                                 for(kb_key *t = htable[i]; t->code != NoSymbol; t++) {
702                                         if(t->table) {
703                                                 string suffix = t->table->findbinding(act);
704                                                 suffix = strip(suffix, ' ');
705                                                 suffix = strip(suffix, ']');
706                                                 suffix = frontStrip(suffix, '[');
707                                                 if (!suffix.empty()) {
708                                                         res += "[" + keyname(*t) + " " + suffix + "] ";
709                                                 }
710                                         } else if (t->action == act) {
711                                                 res += "[" + keyname(*t) + "] ";
712                                         }
713                                 }
714                         }
715                 }
716         } else {
717                 for(kb_key *t = table; t->code != NoSymbol; t++) {
718                         if(t->table) {
719                                 string suffix = t->table->findbinding(act);
720                                 suffix = strip(suffix, ' ');
721                                 suffix = strip(suffix, ']');
722                                 suffix = frontStrip(suffix, '[');
723                                 if (!suffix.empty()) {
724                                         res += "[" + keyname(*t) + " " + suffix + "] ";
725                                 }
726                         } else if (t->action == act) {
727                                 res += "[" + keyname(*t) + "] ";
728                         }
729                 }
730         }
731         return res;
732 }
733
734
735 /* === End of File: kbmap.C ============================================== */