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)
137 unsigned int mod = 0, nmod = 0;
139 if(s[i] && (s[i]) <= ' ') ++i;
142 if(s[i + 1] == '-') { // is implicit that s[i] == true
159 } else if(s[i] == '~' && s[i + 1] && s[i + 2] == '-') {
179 for(; s[j] && s[j] > ' '; ++j)
180 tbuf += s[j]; // (!!!check bounds :-)
182 KeySym key = XStringToKeysym(tbuf.c_str());
183 if(key == NoSymbol) {
185 << "kbmap.C: No such keysym: "
191 addkey(key, mod, nmod);
200 /* ---F+------------------------------------------------------------------ *\
201 Function : kb_sequence::print
203 Purpose : print the currently defined sequence into a string
204 Parameters: buf - string where the result goes
205 maxlen - length of string (including '\0')
206 when_defined - only print when sequence is real: length > 0.
207 Returns : 0, if ok, -1 if string too long
208 \* ---F------------------------------------------------------------------- */
210 int kb_sequence::print(string & buf, bool when_defined) const
215 if ( l < 0 && !when_defined ) l = -l;
217 for(int i = 0; i < l; ++i) {
219 mod = modifiers[i] & 0xffff;
221 printKeysym(key, mod, buf); // RVDK_PATCH_5
223 if(i + 1 < l) { // append a blank
231 /* ---F+------------------------------------------------------------------ *\
232 Function : kb_sequence::printOptions
234 Purpose : print the available key options from the current state in the
235 sequence. RVDK_PATCH_5
236 Parameters: buf - string where the result goes
237 maxlen - length of string (including '\0')
238 Returns : 0, if ok, -1 if string too long
239 \* ---F------------------------------------------------------------------- */
241 int kb_sequence::printOptions(string & buf) const
245 if (!curmap) return -1;
246 buf += _(" options: ");
252 /* ---F+------------------------------------------------------------------ *\
253 Function : kb_sequence::delseq
255 Purpose : mark the sequence as deleted
258 \* ---F------------------------------------------------------------------- */
260 void kb_sequence::delseq()
262 // negative length marks sequence as deleted, but we can still
263 // print() it or retrieve the last char using getiso()
268 /* ---F+------------------------------------------------------------------ *\
269 Function : kb_sequence::getsym
270 Called by : [user], getiso
271 Purpose : get the keysym of the last key in sequence
274 \* ---F------------------------------------------------------------------- */
276 KeySym kb_sequence::getsym()
279 if(l == 0) return NoSymbol;
281 return sequence[l - 1];
285 /* ---F+------------------------------------------------------------------ *\
286 Function : kb_sequence::getiso
288 Purpose : return iso character code of last key, if any
290 Returns : iso code or 0 if none
291 \* ---F------------------------------------------------------------------- */
293 char kb_sequence::getiso()
303 /* ---F+------------------------------------------------------------------ *\
304 Function : kb_sequence::reset
306 Purpose : reset sequence to initial state. RVDK_PATCH_5
309 \* ---F------------------------------------------------------------------- */
311 void kb_sequence::reset()
315 if (length > 0) length = -length;
319 // === kb_keymap methods ==================================================
321 // This binds a key to an action
322 int kb_keymap::bind(char const * seq, int action)
326 int res = k.parse(seq);
330 lyxerr[Debug::KBMAP] << "Parse error at position " << res
331 << " in key sequence '" << seq << "'."
337 /* ---F+------------------------------------------------------------------ *\
338 Function : kb_keymap::lookup
339 Called by : [user], kb_sequence::add()
340 Purpose : look up a key press in a given keymap
341 Parameters: key - the keysym of the key press
342 mod - the modifier mask of the keypress
343 seq - the key-sequence retrieved so far
344 Returns : user defined action; 0 for prefix key, -1 if key not found
345 \* ---F------------------------------------------------------------------- */
347 int kb_keymap::lookup(KeySym key, unsigned int mod, kb_sequence * seq)
350 unsigned int hashval;
352 unsigned int ksym, msk1, msk0;
355 //suppress modifier bits we do not handle
359 // error - no keymap defined:
360 seq->curmap = seq->stdmap;
366 if(size < 0) { // --- if hash table ---
367 hashval = ((key & 0xff) ^ ((key >> 8) & 0xff)) % KB_HASHSIZE;
368 tab = htable[hashval];
370 seq->curmap = seq->stdmap;
374 } else // --- else: linear list ---
378 // --- now search the list of keys ---
380 for(; (tab->code & 0xffff) != NoSymbol; ++tab) {
382 msk1 = tab->mod & 0xffff;
383 msk0 = (tab->mod >> 16) & 0xffff;
385 if(ksym == key && (mod & ~msk0) == msk1) {
388 // this is a prefix key - set new map
389 seq->curmap = tab->table;
392 // final key - reset map
393 seq->curmap = seq->stdmap;
395 return tab->action; // ... and return action
400 // error - key not found:
401 seq->curmap = seq->stdmap;
407 /* ---F+------------------------------------------------------------------ *\
408 Function : kb_keymap::print
410 Purpose : Prints all the available keysyms. RVDK_PATCH_5
411 Parameters: buf - string where output goes.
412 maxLen - available length in string, including `\0'.
413 Returns : updated maxLen.
414 \* ---F------------------------------------------------------------------- */
416 void kb_keymap::print(string & buf) const
418 // Return when keymap has no table.
421 // Process each of its slots recursively and return.
423 if ( size < 0 ) { // Hash table
424 for ( int ix = 0; ix < KB_HASHSIZE; ++ix ) {
426 printKeyTab(htable[ix], buf);
429 } else // Normal table
431 printKeyTab(table, buf);
435 /* ---F+------------------------------------------------------------------ *\
436 Function : kb_keymap::defkey
438 Purpose : define an action for a key sequence
439 Parameters: seq - the key sequence
440 action - the action to be defined
441 idx - recursion depth
443 \* ---F------------------------------------------------------------------- */
445 int kb_keymap::defkey(kb_sequence * seq, int action, int idx /*= 0*/)
447 unsigned int code = seq->sequence[idx];
448 if(code == NoSymbol) return -1;
450 unsigned int modmsk = seq->modifiers[idx];
451 kb_key * tab, ** ptab;
452 // --- get list------------------------------------------------------
454 // If we don't have any yet, make an empty one
455 table = new kb_key[KB_PREALLOC];
456 table[0].code = NoSymbol;
461 } else if(size < 0) {
463 int hashval = code & 0xffff;
464 hashval = ((hashval & 0xff) ^ ((hashval >> 8) & 0xff)) % KB_HASHSIZE;
465 tab = htable[hashval];
466 ptab = htable+hashval;
468 tab = new kb_key[KB_PREALLOC];
469 tab[0].code = NoSymbol;
478 // --- check if key is already there --------------------------------
482 for(t = tab, tsize = 1; t->code != NoSymbol; ++t, ++tsize) {
483 if(code == t->code && modmsk == t->mod) { // -- overwrite binding ---
484 if(idx + 1 == seq->length) {
486 seq->print(buf, true);
488 << "Warning: New binding for '"
490 << "' is overriding old binding..."
499 } else if (!t->table) {
501 seq->print(buf, true);
502 lyxerr << "Error: New binding for '" << buf
503 << "' is overriding old binding..."
507 return t->table->defkey(seq, action, idx + 1);
511 // --- extend list if necessary -------------------------------------
513 if(tsize % KB_PREALLOC == 0) {
514 kb_key * nt = new kb_key[tsize + KB_PREALLOC];
515 // Set to 0 as table is used uninitialised later (thornley)
517 memcpy(nt, tab, tsize * sizeof(kb_key));
521 if(size >= 0) size = tsize + KB_PREALLOC;
524 // --- add action ---------------------------------------------------
526 tab[tsize--].code = NoSymbol;
527 tab[tsize].code = code;
528 tab[tsize].mod = modmsk;
529 kb_key * newone = &tab[tsize];
531 // --- convert list to hash table if necessary ----------------------
534 if(size >= 0 && tsize >= 32) {
535 kb_key * oldtab = tab;
536 kb_key ** nht = new kb_key*[KB_HASHSIZE];
537 for(int i = 0; i < KB_HASHSIZE; ++i)
542 // --- copy old keys to new hash table ---
544 for(kb_key * tu = oldtab; tu->code != NoSymbol; ++tu) {
545 // copy values from oldtab to htable
546 hashval = (tu->code & 0xffff);
547 hashval = ((hashval & 0xff) ^ ((hashval>>8) & 0xff)) % KB_HASHSIZE;
548 tab = htable[hashval];
551 htable[hashval] = tab = new kb_key[KB_PREALLOC];
552 tab->code = NoSymbol;
555 for(kb_key * tt = tab; tt->code != NoSymbol; ++tt)
557 if(ts % KB_PREALLOC == 0){
559 kb_key * nt = new kb_key[ts+KB_PREALLOC];
560 memcpy(nt, tab, ts * sizeof(kb_key));
561 htable[hashval] = nt;
565 tab[ts--].code = NoSymbol;
566 tab[ts].code = tu->code;
567 tab[ts].mod = tu->mod;
568 tab[ts].action = tu->action;
569 tab[ts].table = tu->table;
577 // --- define rest of sequence --------------------------------------
579 if(idx+1 == seq->length) {
580 newone->action = action;
584 newone->table = new kb_keymap;
585 int res = newone->table->defkey(seq, action, idx+1);
591 /* ---F+------------------------------------------------------------------ *\
592 Function : kb_keymap::~kb_keymap
593 Called by : [destructor]
594 Purpose : free keymap and its descendents
597 \* ---F------------------------------------------------------------------- */
599 kb_keymap::~kb_keymap()
604 for(int i = 0; i < KB_HASHSIZE; ++i) {
606 for(kb_key * t = htable[i];
607 t->code != NoSymbol; ++t)
616 for(kb_key * t = table; t->code != NoSymbol; ++t)
626 string keyname(kb_key k)
629 printKeysym(k.code, k.mod, buf);
634 // Finds a key for a keyaction, if possible
635 string kb_keymap::findbinding(int act) const
638 if (!table) return res;
642 for(int i = 0; i < KB_HASHSIZE; ++i) {
644 for(kb_key * t = htable[i];
645 t->code != NoSymbol; ++t) {
647 string suffix = t->table->findbinding(act);
648 suffix = strip(suffix, ' ');
649 suffix = strip(suffix, ']');
650 suffix = frontStrip(suffix, '[');
651 if (!suffix.empty()) {
652 res += "[" + keyname(*t) + " " + suffix + "] ";
654 } else if (t->action == act) {
655 res += "[" + keyname(*t) + "] ";
662 for(kb_key * t = table; t->code != NoSymbol; ++t) {
664 string suffix = t->table->findbinding(act);
665 suffix = strip(suffix, ' ');
666 suffix = strip(suffix, ']');
667 suffix = frontStrip(suffix, '[');
668 if (!suffix.empty()) {
669 res += "[" + keyname(*t) + " " + suffix + "] ";
671 } else if (t->action == act) {
672 res += "[" + keyname(*t) + "] ";
682 /* === End of File: kbmap.C ============================================== */