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