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