1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-1999 The LyX Team.
9 * ====================================================== */
14 #include "support/lstrings.h"
18 #pragma implementation
24 // The only modifiers that we handle. We want to throw away things
26 enum { ModsMask = ShiftMask | ControlMask | Mod1Mask};
29 // === static functions ===================================================
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
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------------------------------------------------------------------- */
43 void printKeysym( KeySym key, unsigned int mod, string & buf)
47 char * s = XKeysymToString(key);
49 if (mod & ShiftMask) buf += "S-";
50 if (mod & ControlMask) buf += "C-";
51 if (mod & Mod1Mask) buf += "M-";
56 /* ---F+------------------------------------------------------------------ *\
57 Function : printKeyTab
58 Called by : kb_keymap::print
59 Purpose : print the keysyms found in the given key table. RVDK_PATCH_5
60 Parameters: tabPt - keytable pointer
61 buf - string where the result goes
62 maxLen - length of string (including '\0')
63 Returns : length of printed string.
64 \* ---F------------------------------------------------------------------- */
67 void printKeyTab( kb_key * tabPt, string & buf)
69 unsigned int ksym, mod;
71 /* -------> Print each of the slots into buf. */
72 for( ; (tabPt->code & 0xffff) != NoSymbol; ++tabPt) {
74 mod = tabPt->mod & 0xffff;
76 printKeysym(ksym, mod, buf);
82 // === kb_sequence methods ================================================
84 /* ---F+------------------------------------------------------------------ *\
85 Function : kb_sequence::addkey
87 Purpose : add a key to the sequence, look up in map and return action
88 Parameters: key - keysym of key
90 nmod - modifier veto mask (unused now)
91 Returns : action or -1 if error (no map defined or key not found)
92 \* ---F------------------------------------------------------------------- */
94 int kb_sequence::addkey(KeySym key,
95 unsigned int mod, unsigned int nmod /*= 0*/)
97 if(length < 0) length = 0;
99 if(length + 1 >= size) {
100 unsigned int * nseq = new unsigned int[size + KB_PREALLOC];
102 memcpy(nseq, sequence, length * sizeof(unsigned int));
103 if(sequence != staticseq) delete sequence;
105 nseq = new unsigned int[size];
106 memcpy(nseq, modifiers, length * sizeof(unsigned int));
107 if(modifiers != staticmod) delete modifiers;
111 modifiers[length] = mod + (nmod << 16);
112 sequence[length++] = key;
115 return curmap->lookup(key, mod, this);
121 /* ---F+------------------------------------------------------------------ *\
122 Function : kb_sequence::parse
124 Purpose : parse a string that holds a key sequence and add the keys
125 Parameters: s - string holding the key sequence
126 Returns : 0 - if ok, error pos if error
127 Note : Keys must be separated with whitespace;
128 Use the keysym names used by XStringToKeysym
129 Prefixes are S-, C-, M- for shift, control, meta
130 \* ---F------------------------------------------------------------------- */
132 int kb_sequence::parse(char const * s)
135 unsigned int mod = 0, nmod = 0;
142 if(s[i] && (s[i]) <= ' ') ++i;
145 if(s[i+1] == '-') { // is implicit that s[i] == true
162 } else if(s[i] == '~' && s[i+1] && s[i+2] == '-') {
181 for(j = i; s[j] && s[j] > ' '; ++j)
182 tbuf[j-i] = s[j]; // (!!!check bounds :-)
186 key = XStringToKeysym(tbuf);
187 if(key == NoSymbol) {
189 << "kbmap.C: No such keysym: "
195 addkey(key, mod, nmod);
204 /* ---F+------------------------------------------------------------------ *\
205 Function : kb_sequence::print
207 Purpose : print the currently defined sequence into a string
208 Parameters: buf - string where the result goes
209 maxlen - length of string (including '\0')
210 when_defined - only print when sequence is real: length > 0.
211 Returns : 0, if ok, -1 if string too long
212 \* ---F------------------------------------------------------------------- */
214 int kb_sequence::print(string & buf, bool when_defined) const
219 if ( l < 0 && !when_defined ) l = -l;
221 for(int i = 0; i < l; ++i) {
223 mod = modifiers[i] & 0xffff;
225 printKeysym(key, mod, buf); // RVDK_PATCH_5
227 if(i + 1 < l) { // append a blank
235 /* ---F+------------------------------------------------------------------ *\
236 Function : kb_sequence::printOptions
238 Purpose : print the available key options from the current state in the
239 sequence. RVDK_PATCH_5
240 Parameters: buf - string where the result goes
241 maxlen - length of string (including '\0')
242 Returns : 0, if ok, -1 if string too long
243 \* ---F------------------------------------------------------------------- */
245 int kb_sequence::printOptions(string & buf) const
249 if (!curmap) return -1;
250 buf += _(" options: ");
256 /* ---F+------------------------------------------------------------------ *\
257 Function : kb_sequence::delseq
259 Purpose : mark the sequence as deleted
262 \* ---F------------------------------------------------------------------- */
264 void kb_sequence::delseq()
266 // negative length marks sequence as deleted, but we can still
267 // print() it or retrieve the last char using getiso()
272 /* ---F+------------------------------------------------------------------ *\
273 Function : kb_sequence::getsym
274 Called by : [user], getiso
275 Purpose : get the keysym of the last key in sequence
278 \* ---F------------------------------------------------------------------- */
280 KeySym kb_sequence::getsym()
283 if(l == 0) return NoSymbol;
285 return sequence[l-1];
289 /* ---F+------------------------------------------------------------------ *\
290 Function : kb_sequence::getiso
292 Purpose : return iso character code of last key, if any
294 Returns : iso code or 0 if none
295 \* ---F------------------------------------------------------------------- */
297 char kb_sequence::getiso()
307 /* ---F+------------------------------------------------------------------ *\
308 Function : kb_sequence::reset
310 Purpose : reset sequence to initial state. RVDK_PATCH_5
313 \* ---F------------------------------------------------------------------- */
315 void kb_sequence::reset()
319 if ( length > 0 ) length = -length;
323 // === kb_keymap methods ==================================================
325 // This binds a key to an action
326 int kb_keymap::bind(char const * seq, int action)
330 int res = k.parse(seq);
334 lyxerr[Debug::KBMAP] << "Parse error at position " << res
335 << " in key sequence '" << seq << "'."
341 /* ---F+------------------------------------------------------------------ *\
342 Function : kb_keymap::lookup
343 Called by : [user], kb_sequence::add()
344 Purpose : look up a key press in a given keymap
345 Parameters: key - the keysym of the key press
346 mod - the modifier mask of the keypress
347 seq - the key-sequence retrieved so far
348 Returns : user defined action; 0 for prefix key, -1 if key not found
349 \* ---F------------------------------------------------------------------- */
351 int kb_keymap::lookup(KeySym key, unsigned int mod, kb_sequence * seq)
354 unsigned int hashval;
356 unsigned int ksym, msk1, msk0;
359 //suppress modifier bits we do not handle
363 // error - no keymap defined:
364 seq->curmap = seq->stdmap;
370 if(size < 0) { // --- if hash table ---
371 hashval = ((key&0xff) ^ ((key>>8)&0xff)) % KB_HASHSIZE;
372 tab = htable[hashval];
374 seq->curmap = seq->stdmap;
378 } else // --- else: linear list ---
382 // --- now search the list of keys ---
384 for( ; (tab->code & 0xffff) != NoSymbol; ++tab) {
386 msk1 = tab->mod & 0xffff;
387 msk0 = (tab->mod >> 16) & 0xffff;
389 if(ksym == key && (mod & ~msk0) == msk1) {
392 // this is a prefix key - set new map
393 seq->curmap = tab->table;
396 // final key - reset map
397 seq->curmap = seq->stdmap;
399 return tab->action; // ... and return action
404 // error - key not found:
405 seq->curmap = seq->stdmap;
411 /* ---F+------------------------------------------------------------------ *\
412 Function : kb_keymap::print
414 Purpose : Prints all the available keysyms. RVDK_PATCH_5
415 Parameters: buf - string where output goes.
416 maxLen - available length in string, including `\0'.
417 Returns : updated maxLen.
418 \* ---F------------------------------------------------------------------- */
420 void kb_keymap::print(string & buf) const
422 // Return when keymap has no table.
425 // Process each of its slots recursively and return.
427 if ( size < 0 ) { // Hash table
428 for ( int ix = 0; ix < KB_HASHSIZE; ++ix ) {
430 printKeyTab(htable[ix], buf);
433 } else // Normal table
435 printKeyTab(table, buf);
439 /* ---F+------------------------------------------------------------------ *\
440 Function : kb_keymap::defkey
442 Purpose : define an action for a key sequence
443 Parameters: seq - the key sequence
444 action - the action to be defined
445 idx - recursion depth
447 \* ---F------------------------------------------------------------------- */
449 int kb_keymap::defkey(kb_sequence * seq, int action, int idx /*= 0*/)
451 unsigned int code = seq->sequence[idx];
452 if(code == NoSymbol) return -1;
454 unsigned int modmsk = seq->modifiers[idx];
455 kb_key * tab, ** ptab;
456 // --- get list------------------------------------------------------
458 // If we don't have any yet, make an empty one
459 table = new kb_key[KB_PREALLOC];
460 table[0].code = NoSymbol;
465 } else if(size < 0) {
467 int hashval = code & 0xffff;
468 hashval = ((hashval & 0xff) ^ ((hashval >> 8) & 0xff)) % KB_HASHSIZE;
469 tab = htable[hashval];
470 ptab = htable+hashval;
472 tab = new kb_key[KB_PREALLOC];
473 tab[0].code = NoSymbol;
482 // --- check if key is already there --------------------------------
486 for(t = tab, tsize = 1; t->code != NoSymbol; ++t, ++tsize) {
487 if(code == t->code && modmsk == t->mod) { // -- overwrite binding ---
488 if(idx+1 == seq->length) {
490 seq->print(buf, true);
492 << "Warning: New binding for '"
494 << "' is overriding old binding..."
503 } else if (!t->table) {
505 seq->print(buf, true);
506 lyxerr << "Error: New binding for '" << buf
507 << "' is overriding old binding..."
511 return t->table->defkey(seq, action, idx + 1);
515 // --- extend list if necessary -------------------------------------
517 if(tsize % KB_PREALLOC == 0) {
518 kb_key * nt = new kb_key[tsize + KB_PREALLOC];
519 // Set to 0 as table is used uninitialised later (thornley)
521 memcpy(nt, tab, tsize * sizeof(kb_key));
525 if(size >= 0) size = tsize + KB_PREALLOC;
528 // --- add action ---------------------------------------------------
530 tab[tsize--].code = NoSymbol;
531 tab[tsize].code = code;
532 tab[tsize].mod = modmsk;
533 kb_key * newone = &tab[tsize];
535 // --- convert list to hash table if necessary ----------------------
538 if(size >= 0 && tsize >= 32) {
539 kb_key * oldtab = tab;
540 kb_key ** nht = new kb_key*[KB_HASHSIZE];
541 for(int i = 0; i < KB_HASHSIZE; ++i)
546 // --- copy old keys to new hash table ---
548 for(kb_key * tu = oldtab; tu->code != NoSymbol; ++tu) {
549 // copy values from oldtab to htable
550 hashval = (tu->code & 0xffff);
551 hashval = ((hashval & 0xff) ^ ((hashval>>8) & 0xff)) % KB_HASHSIZE;
552 tab = htable[hashval];
555 htable[hashval] = tab = new kb_key[KB_PREALLOC];
556 tab->code = NoSymbol;
559 for(kb_key * tt = tab; tt->code != NoSymbol; ++tt)
561 if(ts % KB_PREALLOC == 0){
563 kb_key * nt = new kb_key[ts+KB_PREALLOC];
564 memcpy(nt, tab, ts * sizeof(kb_key));
565 htable[hashval] = nt;
569 tab[ts--].code = NoSymbol;
570 tab[ts].code = tu->code;
571 tab[ts].mod = tu->mod;
572 tab[ts].action = tu->action;
573 tab[ts].table = tu->table;
581 // --- define rest of sequence --------------------------------------
583 if(idx+1 == seq->length) {
584 newone->action = action;
588 newone->table = new kb_keymap;
589 int res = newone->table->defkey(seq, action, idx+1);
595 /* ---F+------------------------------------------------------------------ *\
596 Function : kb_keymap::~kb_keymap
597 Called by : [destructor]
598 Purpose : free keymap and its descendents
601 \* ---F------------------------------------------------------------------- */
603 kb_keymap::~kb_keymap()
608 for(int i = 0; i < KB_HASHSIZE; ++i) {
610 for(kb_key * t = htable[i];
611 t->code != NoSymbol; ++t)
620 for(kb_key * t = table; t->code != NoSymbol; ++t)
630 string keyname(kb_key k)
633 printKeysym(k.code, k.mod, buf);
638 // Finds a key for a keyaction, if possible
639 string kb_keymap::findbinding(int act) const
642 if (!table) return res;
646 for(int i = 0; i < KB_HASHSIZE; ++i) {
648 for(kb_key * t = htable[i];
649 t->code != NoSymbol; ++t) {
651 string suffix = t->table->findbinding(act);
652 suffix = strip(suffix, ' ');
653 suffix = strip(suffix, ']');
654 suffix = frontStrip(suffix, '[');
655 if (!suffix.empty()) {
656 res += "[" + keyname(*t) + " " + suffix + "] ";
658 } else if (t->action == act) {
659 res += "[" + keyname(*t) + "] ";
666 for(kb_key * t = table; t->code != NoSymbol; ++t) {
668 string suffix = t->table->findbinding(act);
669 suffix = strip(suffix, ' ');
670 suffix = strip(suffix, ']');
671 suffix = frontStrip(suffix, '[');
672 if (!suffix.empty()) {
673 res += "[" + keyname(*t) + " " + suffix + "] ";
675 } else if (t->action == act) {
676 res += "[" + keyname(*t) + "] ";
686 /* === End of File: kbmap.C ============================================== */