]> git.lyx.org Git - lyx.git/blob - src/kbmap.C
fix some of the bugs reported, should hopefully be a bit better now.
[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 <X11/Xlib.h>
14
15 #include "support/lstrings.h"
16 #include "gettext.h"
17
18 #ifdef __GNUG__
19 #pragma implementation
20 #endif
21
22 #include "kbmap.h"
23 #include "debug.h"
24
25 // The only modifiers that we handle. We want to throw away things
26 // like NumLock. 
27 enum { ModsMask = ShiftMask | ControlMask | Mod1Mask};
28
29
30 // === static functions =================================================== 
31
32
33 /* ---F+------------------------------------------------------------------ *\
34    Function  : printKeysym
35    Called by : kb_sequence::print and printKeyMap. RVDK_PATCH_5
36    Purpose   : prints a keysym, including modifiers.
37    Parameters: key    - keysym
38                mod    - modifiers
39                buf    - string where the result goes
40                maxlen - length of string (including '\0')
41    Returns   : length of printed string if ok, 0 otherwise.
42 \* ---F------------------------------------------------------------------- */
43 static
44 void printKeysym(unsigned long key, unsigned int mod, string & buf)
45 {
46         mod &= ModsMask;
47
48         char * s = XKeysymToString(key);
49         
50         if (mod & ShiftMask) buf += "S-";
51         if (mod & ControlMask) buf += "C-";
52         if (mod & Mod1Mask) buf += "M-";
53         if (s) buf += s;
54 }
55
56
57 /* ---F+------------------------------------------------------------------ *\
58    Function  : printKeyTab
59    Called by : kb_keymap::print
60    Purpose   : print the keysyms found in the given key table. RVDK_PATCH_5
61    Parameters: tabPt  - keytable pointer
62                buf    - string where the result goes
63                maxLen - length of string (including '\0')
64    Returns   : length of printed string.
65 \* ---F------------------------------------------------------------------- */
66
67 static
68 void printKeyTab(kb_key * tabPt, string & buf)
69 {
70         unsigned int ksym, mod;
71         
72         /* -------> Print each of the slots into buf. */
73         for( ; (tabPt->code & 0xffff) != NoSymbol; ++tabPt) {
74                 ksym =  tabPt->code;
75                 mod  =  tabPt->mod & 0xffff;
76                 
77                 printKeysym(ksym, mod, buf);
78                 buf += ' ';
79         }
80 }
81
82
83 // === kb_sequence methods ================================================ 
84
85 /* ---F+------------------------------------------------------------------ *\
86     Function  : kb_sequence::addkey
87     Called by : [user]
88     Purpose   : add a key to the sequence, look up in map and return action
89     Parameters: key  - keysym of key
90                 mod  - modifier mask
91                 nmod - modifier veto mask (unused now)
92     Returns   : action or -1 if error (no map defined or key not found)
93 \* ---F------------------------------------------------------------------- */
94
95 int kb_sequence::addkey(unsigned long key,
96                         unsigned int mod, unsigned int nmod /*= 0*/)
97 {
98         if(length < 0) length = 0;
99
100         if(length + 1 >= size) {
101                 unsigned int * nseq = new unsigned int[size + KB_PREALLOC];
102                 size += KB_PREALLOC;
103                 memcpy(nseq, sequence, length * sizeof(unsigned int));
104                 if(sequence != staticseq) delete sequence;
105                 sequence = nseq;
106                 nseq = new unsigned int[size];
107                 memcpy(nseq, modifiers, length * sizeof(unsigned int));
108                 if(modifiers != staticmod) delete modifiers;
109                 modifiers = nseq;
110         }
111
112         modifiers[length]  = mod + (nmod << 16);
113         sequence[length++] = key;
114    
115         if(curmap)
116                 return curmap->lookup(key, mod, this);
117         
118         return -1;
119 }
120
121
122 /* ---F+------------------------------------------------------------------ *\
123     Function  : kb_sequence::parse
124     Called by : [user]
125     Purpose   : parse a string that holds a key sequence and add the keys
126     Parameters: s - string holding the key sequence
127     Returns   : 0 - if ok, error pos if error
128     Note      : Keys must be separated with whitespace;
129                 Use the keysym names used by XStringToKeysym
130                 Prefixes are S-, C-, M- for shift, control, meta
131 \* ---F------------------------------------------------------------------- */
132
133 int kb_sequence::parse(char const * s)
134 {
135         if(!s[0]) return 1;
136
137         int i = 0;
138         unsigned int mod = 0, nmod = 0;
139         while(s[i]) {
140                 if(s[i] && (s[i]) <= ' ') ++i;
141                 if(!s[i]) break;
142                 
143                 if(s[i + 1] == '-')     { // is implicit that s[i] == true
144                         switch(s[i]) {
145                         case 's': case 'S':
146                                 mod |= ShiftMask;
147                                 i += 2;
148                                 continue;
149                         case 'c': case 'C':
150                                 mod |= ControlMask;
151                                 i += 2;
152                                 continue;
153                         case 'm': case 'M':
154                                 mod |= Mod1Mask;
155                                 i += 2;
156                                 continue;
157                         default:
158                                 return i + 1;
159                         }
160                 } else if(s[i] == '~' && s[i + 1] && s[i + 2] == '-') {
161                         switch(s[i + 1]) {
162                         case 's': case 'S':
163                                 nmod |= ShiftMask;
164                                 i += 3;
165                                 continue;
166                         case 'c': case 'C':
167                                 nmod |= ControlMask;
168                                 i += 3;
169                                 continue;
170                         case 'm': case 'M':
171                                 nmod |= Mod1Mask;
172                                 i += 3;
173                                 continue;
174                         default:
175                                 return i + 2;
176                         }
177                 } else {
178                         string tbuf;
179                         int j = i;
180                         for(; s[j] && s[j] > ' '; ++j)
181                                 tbuf += s[j];    // (!!!check bounds :-)
182                         
183                         KeySym key = XStringToKeysym(tbuf.c_str());
184                         if(key == NoSymbol) {
185                                 lyxerr[Debug::KBMAP]
186                                         << "kbmap.C: No such keysym: "
187                                         << tbuf << endl;
188                                 return j;
189                         }
190                         i = j;
191                         
192                         addkey(key, mod, nmod);
193                         mod = 0;
194                         nmod = 0;
195                 }
196         }
197         return 0;
198 }
199
200
201 /* ---F+------------------------------------------------------------------ *\
202     Function  : kb_sequence::print
203     Called by : [user]
204     Purpose   : print the currently defined sequence into a string
205     Parameters: buf           - string where the result goes
206                 maxlen        - length of string (including '\0')
207                 when_defined  - only  print when sequence is real: length > 0.
208     Returns   : 0, if ok, -1 if string too long
209 \* ---F------------------------------------------------------------------- */
210
211 int kb_sequence::print(string & buf, bool when_defined) const
212 {
213         KeySym key;
214         unsigned int mod;
215         int l = length;
216         if ( l < 0 && !when_defined ) l = -l;
217         
218         for(int i = 0; i < l; ++i) {
219                 key = sequence[i];
220                 mod = modifiers[i] & 0xffff;
221
222                 printKeysym(key, mod, buf);  // RVDK_PATCH_5
223
224                 if(i + 1 < l) {  // append a blank
225                         buf += ' ';
226                 }
227         }
228         return 0;
229 }
230
231
232 /* ---F+------------------------------------------------------------------ *\
233     Function  : kb_sequence::printOptions
234     Called by : [user]
235     Purpose   : print the available key options from the current state in the
236                 sequence. RVDK_PATCH_5
237     Parameters: buf    - string where the result goes
238                 maxlen - length of string (including '\0')
239     Returns   : 0, if ok, -1 if string too long
240 \* ---F------------------------------------------------------------------- */
241
242 int kb_sequence::printOptions(string & buf) const
243 {
244         print(buf, true);
245         
246         if (!curmap) return -1;
247         buf += _("   options: ");
248         curmap->print(buf);
249         return 0;
250 }
251
252
253 /* ---F+------------------------------------------------------------------ *\
254     Function  : kb_sequence::delseq
255     Called by : [user]
256     Purpose   : mark the sequence as deleted
257     Parameters: none
258     Returns   : nothing
259 \* ---F------------------------------------------------------------------- */
260
261 void kb_sequence::delseq()
262 {
263         // negative length marks sequence as deleted, but we can still
264         // print() it or retrieve the last char using getiso()
265         length = -length;
266 }
267
268
269 /* ---F+------------------------------------------------------------------ *\
270    Function  : kb_sequence::getsym
271    Called by : [user], getiso
272    Purpose   : get the keysym of the last key in sequence
273    Parameters: none
274    Returns   : keysym
275 \* ---F------------------------------------------------------------------- */
276
277 KeySym kb_sequence::getsym()
278 {
279         int l = length;
280         if(l == 0) return NoSymbol;
281         if(l < 0) l = -l;
282         return sequence[l - 1];
283 }
284
285
286 /* ---F+------------------------------------------------------------------ *\
287     Function  : kb_sequence::getiso
288     Called by : [user]
289     Purpose   : return iso character code of last key, if any
290     Parameters: none
291     Returns   : iso code or 0 if none
292 \* ---F------------------------------------------------------------------- */
293
294 char kb_sequence::getiso()
295 {
296         int c = getsym();
297         
298         if(c > 0xff)
299                 return '\0';
300         return c;
301 }
302
303
304 /* ---F+------------------------------------------------------------------ *\
305     Function  : kb_sequence::reset
306     Called by : [user]
307     Purpose   : reset sequence to initial state. RVDK_PATCH_5
308     Parameters: none
309     Returns   : void
310 \* ---F------------------------------------------------------------------- */
311
312 void kb_sequence::reset()
313 {
314         delseq();
315         curmap = stdmap;
316         if (length > 0) length = -length;
317 }
318
319
320 // === kb_keymap methods ================================================== 
321
322 // This binds a key to an action
323 int kb_keymap::bind(char const * seq, int action)
324 {
325         kb_sequence k;
326
327         int res = k.parse(seq);
328         if (!res) {
329                 defkey(&k, action);
330         } else
331                 lyxerr[Debug::KBMAP] << "Parse error at position " << res
332                                      << " in key sequence '" << seq << "'."
333                                      << endl;
334         return res;
335 }
336
337
338 /* ---F+------------------------------------------------------------------ *\
339     Function  : kb_keymap::lookup
340     Called by : [user], kb_sequence::add()
341     Purpose   : look up a key press in a given keymap
342     Parameters: key - the keysym of the key press
343                 mod - the modifier mask of the keypress
344                 seq - the key-sequence retrieved so far
345     Returns   : user defined action; 0 for prefix key, -1 if key not found
346 \* ---F------------------------------------------------------------------- */
347
348 int kb_keymap::lookup(unsigned long key, unsigned int mod, kb_sequence * seq)
349 {
350         unsigned int ksym, msk1, msk0;
351         kb_key * tab;
352
353         //suppress modifier bits we do not handle
354         mod &= ModsMask;
355
356         if(!table) {
357                 // error - no keymap defined:
358                 seq->curmap = seq->stdmap;
359                 seq->delseq();
360                 return -1;
361         }
362
363         tab = table;
364
365         // --- now search the list of keys ---
366
367         for(; (tab->code & 0xffff) != NoSymbol; ++tab) {
368                 ksym =  tab->code;
369                 msk1 =  tab->mod      & 0xffff;
370                 msk0 = (tab->mod >> 16) & 0xffff;
371
372                 if(ksym == key && (mod & ~msk0) == msk1) {
373                         // match found:
374                         if(tab->table) {
375                                 // this is a prefix key - set new map
376                                 seq->curmap = tab->table;
377                                 return 0;
378                         } else {
379                                 // final key - reset map
380                                 seq->curmap = seq->stdmap;
381                                 seq->delseq();
382                                 return tab->action; // ... and return action
383                         }
384                 }
385         }
386         
387         // error - key not found:
388         seq->curmap = seq->stdmap;
389         seq->delseq();
390         return -1;
391 }
392
393
394 /* ---F+------------------------------------------------------------------ *\
395     Function  : kb_keymap::print
396     Called by : [user]
397     Purpose   : Prints all the available keysyms. RVDK_PATCH_5
398     Parameters: buf    - string where output goes.
399                maxLen - available length in string, including `\0'.
400     Returns   : updated maxLen.
401 \* ---F------------------------------------------------------------------- */
402
403 void kb_keymap::print(string & buf) const
404 {
405         // Return when keymap has no table.
406         if (!table) return;
407    
408         // Process each of its slots recursively and return.
409         printKeyTab(table, buf);
410 }
411
412
413 /* ---F+------------------------------------------------------------------ *\
414     Function  : kb_keymap::defkey
415     Called by : [user]
416     Purpose   : define an action for a key sequence
417     Parameters: seq    - the key sequence
418                 action - the action to be defined
419                 idx    - recursion depth
420     Returns   : 0 if ok.
421 \* ---F------------------------------------------------------------------- */
422
423 int kb_keymap::defkey(kb_sequence * seq, int action, int idx /*= 0*/)
424 {
425         unsigned int code = seq->sequence[idx];
426         if(code == NoSymbol) return -1;
427
428         unsigned int modmsk = seq->modifiers[idx];
429         kb_key  * tab, ** ptab;
430         // --- get list------------------------------------------------------
431         if(!table) {
432                 // If we don't have any yet, make an empty one
433                 table = new kb_key[KB_PREALLOC];
434                 table[0].code = NoSymbol;
435                 tab   =  table;
436                 ptab  = &table;
437                 size  = KB_PREALLOC;
438         } else {
439                 tab  =  table;
440                 ptab = &table;
441         }
442
443         // --- check if key is already there --------------------------------
444
445         kb_key * t;
446         int tsize;
447         for(t = tab, tsize = 1; t->code != NoSymbol; ++t, ++tsize) {
448                 if(code == t->code && modmsk == t->mod) { // -- overwrite binding ---
449                         if(idx + 1 == seq->length) {
450                                 string buf;
451                                 seq->print(buf, true);
452                                 lyxerr[Debug::KEY]
453                                         << "Warning: New binding for '"
454                                         << buf 
455                                         << "' is overriding old binding..."
456                                         << endl;
457
458                                 if(t->table) {
459                                         delete t->table;
460                                         t->table = 0;
461                                 }
462                                 t->action = action;
463                                 return 0;
464                         } else if (!t->table) {
465                                 string buf;
466                                 seq->print(buf, true);
467                                 lyxerr << "Error: New binding for '" << buf
468                                        << "' is overriding old binding..."
469                                        << endl;
470                                 return -1;
471                         } else
472                                 return t->table->defkey(seq, action, idx + 1);
473                 }
474         }
475
476         // --- extend list if necessary -------------------------------------
477
478         if(tsize % KB_PREALLOC == 0) {
479                 kb_key * nt = new kb_key[tsize + KB_PREALLOC];
480                 // Set to 0 as table is used uninitialised later (thornley)
481                 nt[tsize].table = 0;
482                 memcpy(nt, tab, tsize * sizeof(kb_key));
483                 *ptab = nt;
484                 delete[] tab;
485                 tab = nt;
486                 if(size >= 0) size = tsize + KB_PREALLOC;
487         }
488
489         // --- add action ---------------------------------------------------
490
491         tab[tsize--].code = NoSymbol;
492         tab[tsize].code = code;
493         tab[tsize].mod  = modmsk;
494         kb_key * newone = &tab[tsize];
495         
496         // --- define rest of sequence --------------------------------------
497
498         if(idx + 1 == seq->length) {
499                 newone->action = action;
500                 newone->table  = 0;
501                 return 0;
502         } else {
503                 newone->table = new kb_keymap;
504                 int res = newone->table->defkey(seq, action, idx + 1);
505                 return res;
506         }
507 }
508
509
510 /* ---F+------------------------------------------------------------------ *\
511     Function  : kb_keymap::~kb_keymap
512     Called by : [destructor]
513     Purpose   : free keymap and its descendents
514     Parameters: none
515     Returns   : nothing
516 \* ---F------------------------------------------------------------------- */
517
518 kb_keymap::~kb_keymap()
519 {
520         if(!table) return;
521         for(kb_key * t = table; t->code != NoSymbol; ++t)
522                 if(t->table)
523                         delete t->table;
524         delete table;
525 }
526
527
528 string keyname(kb_key k)
529 {
530         string buf;
531         printKeysym(k.code, k.mod, buf);
532         return buf;
533 }
534
535
536 // Finds a key for a keyaction, if possible
537 string kb_keymap::findbinding(int act) const
538 {
539         string res;
540         if (!table) return res;
541
542         for(kb_key * t = table; t->code != NoSymbol; ++t) {
543                 if(t->table) {
544                         string suffix = t->table->findbinding(act);
545                         suffix = strip(suffix, ' ');
546                         suffix = strip(suffix, ']');
547                         suffix = frontStrip(suffix, '[');
548                         if (!suffix.empty()) {
549                                 res += "[" + keyname(*t) + " " + suffix + "] ";
550                         }
551                 } else if (t->action == act) {
552                         res += "[" + keyname(*t) + "] ";
553                 }
554         }
555         return res;
556 }
557
558
559 /* === End of File: kbmap.C ============================================== */