]> git.lyx.org Git - lyx.git/blob - src/KeyMap.cpp
Move Dialog.{h,cpp} and ButtonPolicy.{h,cpp} to frontends/qt4/
[lyx.git] / src / KeyMap.cpp
1 /**
2  * \file KeyMap.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author John Levon
9  * \author André Pönitz
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "KeyMap.h"
17
18 #include "debug.h"
19 #include "KeySequence.h"
20 #include "LyXAction.h"
21 #include "Lexer.h"
22
23 #include "support/docstream.h"
24 #include "support/filetools.h"
25
26 #include <fstream>
27 #include <sstream>
28 #include <utility>
29
30 using std::endl;
31 using std::ios;
32 using std::ofstream;
33 using std::string;
34
35
36 namespace lyx {
37
38 using support::FileName;
39 using support::i18nLibFileSearch;
40
41
42 string const KeyMap::printKeySym(KeySymbol const & key, KeyModifier mod)
43 {
44         string buf;
45
46         string const s = key.getSymbolName();
47
48         if (mod & ControlModifier)
49                 buf += "C-";
50         if (mod & AltModifier)
51                 buf += "M-";
52         if (mod & ShiftModifier)
53                 buf += "S-";
54
55         buf += s;
56         return buf;
57 }
58
59
60 size_t KeyMap::bind(string const & seq, FuncRequest const & func)
61 {
62         LYXERR(Debug::KBMAP, "BIND: Sequence `" << seq << "' Action `"
63                << func.action << '\'');
64
65         KeySequence k(0, 0);
66
67         string::size_type const res = k.parse(seq);
68         if (res == string::npos) {
69                 bind(&k, func);
70         } else {
71                 LYXERR(Debug::KBMAP, "Parse error at position " << res
72                                      << " in key sequence '" << seq << "'.");
73         }
74
75         return res == string::npos ? 0 : res;
76 }
77
78
79 size_t KeyMap::unbind(string const & seq, FuncRequest const & func)
80 {
81         KeySequence k(0, 0);
82
83         string::size_type const res = k.parse(seq);
84         if (res == string::npos)
85                 unbind(&k, func);
86         else
87                 LYXERR(Debug::KBMAP, "Parse error at position " << res
88                                      << " in key sequence '" << seq << "'.");
89         return res == string::npos ? 0 : res;
90 }
91
92
93 bool KeyMap::hasBinding(KeySequence const & seq, FuncRequest const & func,
94                 unsigned int r)
95 {
96         KeySymbol code = seq.sequence[r];
97         if (!code.isOK())
98                 return false;
99
100         KeyModifier const mod1 = seq.modifiers[r].first;
101         KeyModifier const mod2 = seq.modifiers[r].second;
102
103         // check if key is already there
104         Table::iterator end = table.end();
105         for (Table::iterator it = table.begin(); it != end; ++it) {
106                 if (code == it->code
107                     && mod1 == it->mod.first
108                     && mod2 == it->mod.second) {
109                         if (r + 1 == seq.length())
110                                 return it->func == func;
111                         else if (it->table.get())
112                                 return it->table->hasBinding(seq, func, r + 1);
113                 }
114         }
115         return false;
116 }
117
118
119 void KeyMap::clear()
120 {
121         table.clear();
122 }
123
124
125 namespace {
126
127 enum BindTags {
128         BN_BIND,
129         BN_BINDFILE,
130         BN_UNBIND,
131 };
132
133 keyword_item bindTags[] = {
134         { "\\bind", BN_BIND },
135         { "\\bind_file", BN_BINDFILE },
136         { "\\unbind", BN_UNBIND },
137 };
138
139 }
140
141
142 bool KeyMap::read(string const & bind_file, KeyMap * unbind_map)
143 {
144         const int bindCount = sizeof(bindTags) / sizeof(keyword_item);
145
146         Lexer lexrc(bindTags, bindCount);
147         if (lyxerr.debugging(Debug::PARSER))
148                 lexrc.printTable(lyxerr);
149
150         FileName const tmp(i18nLibFileSearch("bind", bind_file, "bind"));
151         lexrc.setFile(tmp);
152         if (!lexrc.isOK()) {
153                 lyxerr << "KeyMap::read: cannot open bind file:"
154                        << tmp << endl;
155                 return false;
156         }
157
158         LYXERR(Debug::KBMAP, "Reading bind file:" << tmp);
159
160         bool error = false;
161         while (lexrc.isOK()) {
162                 switch (lexrc.lex()) {
163                 case Lexer::LEX_UNDEF:
164                         lexrc.printError("Unknown tag `$$Token'");
165                         error = true;
166                         continue;
167                 case Lexer::LEX_FEOF:
168                         continue;
169                 case BN_BIND:
170                 {
171                         string seq, cmd;
172
173                         if (lexrc.next()) {
174                                 seq = lexrc.getString();
175                         } else {
176                                 lexrc.printError("BN_BIND: Missing key sequence");
177                                 error = true;
178                                 break;
179                         }
180
181                         if (lexrc.next(true)) {
182                                 cmd = lexrc.getString();
183                         } else {
184                                 lexrc.printError("BN_BIND: missing command");
185                                 error = true;
186                                 break;
187                         }
188
189                         FuncRequest func = lyxaction.lookupFunc(cmd);
190                         if (func. action == LFUN_UNKNOWN_ACTION) {
191                                 lexrc.printError("BN_BIND: Unknown LyX"
192                                                  " function `$$Token'");
193                                 error = true;
194                                 break;
195                         }
196
197                         bind(seq, func);
198                         break;
199                 }
200                 case BN_UNBIND:
201                 {
202                         string seq, cmd;
203
204                         if (lexrc.next()) {
205                                 seq = lexrc.getString();
206                         } else {
207                                 lexrc.printError("BN_UNBIND: Missing key sequence");
208                                 error = true;
209                                 break;
210                         }
211
212                         if (lexrc.next(true)) {
213                                 cmd = lexrc.getString();
214                         } else {
215                                 lexrc.printError("BN_UNBIND: missing command");
216                                 error = true;
217                                 break;
218                         }
219
220                         FuncRequest func = lyxaction.lookupFunc(cmd);
221                         if (func. action == LFUN_UNKNOWN_ACTION) {
222                                 lexrc.printError("BN_UNBIND: Unknown LyX"
223                                                  " function `$$Token'");
224                                 error = true;
225                                 break;
226                         }
227                         
228                         if (unbind_map)
229                                 unbind_map->bind(seq, func);
230                         else
231                                 unbind(seq, func);
232                         break;
233                 }
234                 case BN_BINDFILE:
235                         if (lexrc.next()) {
236                                 string const tmp(lexrc.getString());
237                                 error |= !read(tmp, unbind_map);
238                         } else {
239                                 lexrc.printError("BN_BINDFILE: Missing file name");
240                                 error = true;
241                                 break;
242
243                         }
244                         break;
245                 }
246         }
247
248         if (error)
249                 lyxerr << "KeyMap::read: error while reading bind file:"
250                        << tmp << endl;
251         return !error;
252 }
253
254
255 void KeyMap::write(string const & bind_file, bool append, bool unbind) const
256 {
257         ofstream os(bind_file.c_str(), 
258                 append ? (ios::app | ios:: out): ios::out);
259
260         if (!append)
261                 os << "## This file is automatically generated by lyx\n"
262                    << "## All modifications will be lost\n\n";
263         
264         string tag = unbind ? "\\unbind" : "\\bind";
265         BindingList const list = listBindings(false);
266         BindingList::const_iterator it = list.begin();
267         BindingList::const_iterator it_end = list.end();
268         for (; it != it_end; ++it) {
269                 kb_action action = it->request.action;
270                 string arg = to_utf8(it->request.argument());
271
272                 os << tag << " \""
273                                 << to_utf8(it->sequence.print(KeySequence::BindFile))
274                                 << "\" \""
275                                 << lyxaction.getActionName(action)
276                                 << (arg.empty() ? "" : " ") << arg
277                                 << "\"\n";
278         }
279         os << "\n";
280         os.close();
281 }
282
283
284 FuncRequest const & KeyMap::lookup(KeySymbol const &key,
285                   KeyModifier mod, KeySequence * seq) const
286 {
287         static FuncRequest const unknown(LFUN_UNKNOWN_ACTION);
288
289         if (table.empty()) {
290                 seq->curmap = seq->stdmap;
291                 seq->mark_deleted();
292                 return unknown;
293         }
294
295         Table::const_iterator end = table.end();
296         for (Table::const_iterator cit = table.begin(); cit != end; ++cit) {
297                 KeyModifier mask = cit->mod.second;
298                 KeyModifier check = static_cast<KeyModifier>(mod & ~mask);
299
300                 if (cit->code == key && cit->mod.first == check) {
301                         // match found
302                         if (cit->table.get()) {
303                                 // this is a prefix key - set new map
304                                 seq->curmap = cit->table.get();
305                                 static FuncRequest prefix(LFUN_COMMAND_PREFIX);
306                                 return prefix;
307                         } else {
308                                 // final key - reset map
309                                 seq->curmap = seq->stdmap;
310                                 seq->mark_deleted();
311                                 return cit->func;
312                         }
313                 }
314         }
315
316         // error - key not found:
317         seq->curmap = seq->stdmap;
318         seq->mark_deleted();
319
320         return unknown;
321 }
322
323
324 docstring const KeyMap::print(bool forgui) const
325 {
326         docstring buf;
327         Table::const_iterator end = table.end();
328         for (Table::const_iterator cit = table.begin(); cit != end; ++cit) {
329                 buf += cit->code.print(cit->mod.first, forgui);
330                 buf += ' ';
331         }
332         return buf;
333 }
334
335
336 void KeyMap::bind(KeySequence * seq, FuncRequest const & func, unsigned int r)
337 {
338         KeySymbol code = seq->sequence[r];
339         if (!code.isOK())
340                 return;
341
342         KeyModifier const mod1 = seq->modifiers[r].first;
343         KeyModifier const mod2 = seq->modifiers[r].second;
344
345         // check if key is already there
346         Table::iterator end = table.end();
347         for (Table::iterator it = table.begin(); it != end; ++it) {
348                 if (code == it->code
349                     && mod1 == it->mod.first
350                     && mod2 == it->mod.second) {
351                         // overwrite binding
352                         if (r + 1 == seq->length()) {
353                                 LYXERR(Debug::KBMAP, "Warning: New binding for '"
354                                         << to_utf8(seq->print(KeySequence::Portable))
355                                         << "' is overriding old binding...");
356                                 if (it->table.get()) {
357                                         it->table.reset();
358                                 }
359                                 it->func = func;
360                                 it->func.origin = FuncRequest::KEYBOARD;
361                                 return;
362                         } else if (!it->table.get()) {
363                                 lyxerr << "Error: New binding for '"
364                                        << to_utf8(seq->print(KeySequence::Portable))
365                                        << "' is overriding old binding..."
366                                                << endl;
367                                 return;
368                         } else {
369                                 it->table->bind(seq, func, r + 1);
370                                 return;
371                         }
372                 }
373         }
374
375         Table::iterator newone = table.insert(table.end(), Key());
376         newone->code = code;
377         newone->mod = seq->modifiers[r];
378         if (r + 1 == seq->length()) {
379                 newone->func = func;
380                 newone->func.origin = FuncRequest::KEYBOARD;
381                 newone->table.reset();
382         } else {
383                 newone->table.reset(new KeyMap);
384                 newone->table->bind(seq, func, r + 1);
385         }
386 }
387
388
389 void KeyMap::unbind(KeySequence * seq, FuncRequest const & func, unsigned int r)
390 {
391         KeySymbol code = seq->sequence[r];
392         if (!code.isOK())
393                 return;
394
395         KeyModifier const mod1 = seq->modifiers[r].first;
396         KeyModifier const mod2 = seq->modifiers[r].second;
397
398         // check if key is already there
399         Table::iterator end = table.end();
400         Table::iterator remove = end;
401         for (Table::iterator it = table.begin(); it != end; ++it) {
402                 if (code == it->code
403                     && mod1 == it->mod.first
404                     && mod2 == it->mod.second) {
405                         // remove
406                         if (r + 1 == seq->length()) {
407                                 if (it->func == func) {
408                                         remove = it;
409                                         if (it->table.get())
410                                                 it->table.reset();
411                                         }
412                         } else if (it->table.get()) {
413                                 it->table->unbind(seq, func, r + 1);
414                                 if (it->table->empty())
415                                         remove = it;
416                                 return;
417                         }
418                 }
419         }
420         if (remove != end) {
421                 table.erase(remove);
422                 return;
423         }
424 }
425
426
427 docstring KeyMap::printBindings(FuncRequest const & func) const
428 {
429         Bindings bindings = findBindings(func);
430         if (bindings.empty())
431                 return docstring();
432         
433         odocstringstream res;
434         Bindings::const_iterator cit = bindings.begin();
435         Bindings::const_iterator cit_end = bindings.end();
436         // prin the first item
437         res << cit->print(KeySequence::ForGui);
438         // more than one shortcuts?
439         for (++cit; cit != cit_end; ++cit)
440                 res << ", " << cit->print(KeySequence::ForGui);
441         return res.str();
442 }
443
444
445 KeyMap::Bindings KeyMap::findBindings(FuncRequest const & func) const
446 {
447         return findBindings(func, KeySequence(0, 0));
448 }
449
450
451 KeyMap::Bindings KeyMap::findBindings(FuncRequest const & func,
452                         KeySequence const & prefix) const
453 {
454         Bindings res;
455         if (table.empty())
456                 return res;
457
458         Table::const_iterator end = table.end();
459         for (Table::const_iterator cit = table.begin(); cit != end; ++cit) {
460                 if (cit->table.get()) {
461                         KeySequence seq = prefix;
462                         seq.addkey(cit->code, cit->mod.first);
463                         Bindings res2 = cit->table->findBindings(func, seq);
464                         res.insert(res.end(), res2.begin(), res2.end());
465                 } else if (cit->func == func) {
466                         KeySequence seq = prefix;
467                         seq.addkey(cit->code, cit->mod.first);
468                         res.push_back(seq);
469                 }
470         }
471
472         return res;
473 }
474
475
476 KeyMap::BindingList KeyMap::listBindings(bool unbound, int tag) const
477 {
478         BindingList list;
479         listBindings(list, KeySequence(0, 0), tag);
480         if (unbound) {
481                 LyXAction::const_func_iterator fit = lyxaction.func_begin();
482                 LyXAction::const_func_iterator fit_end = lyxaction.func_end();
483                 for (; fit != fit_end; ++fit) {
484                         kb_action action = fit->second;
485                         bool has_action = false;
486                         BindingList::const_iterator it = list.begin();
487                         BindingList::const_iterator it_end = list.end();
488                         for (; it != it_end; ++it)
489                                 if (it->request.action == action) {
490                                         has_action = true;
491                                         break;
492                                 }
493                         if (!has_action)
494                                 list.push_back(Binding(FuncRequest(action), KeySequence(0, 0), tag));
495                 }       
496         }
497         return list;
498 }
499
500
501 void KeyMap::listBindings(BindingList & list,
502         KeySequence const & prefix, int tag) const
503 {
504         Table::const_iterator it = table.begin();
505         Table::const_iterator it_end = table.end();
506         for (; it != it_end; ++it) {
507                 // a LFUN_COMMAND_PREFIX
508                 if (it->table.get()) {
509                         KeySequence seq = prefix;
510                         seq.addkey(it->code, it->mod.first);
511                         it->table->listBindings(list, seq, tag);
512                 } else {
513                         KeySequence seq = prefix;
514                         seq.addkey(it->code, it->mod.first);
515                         list.push_back(Binding(it->func, seq, tag));
516                 }
517         }
518 }
519
520
521 } // namespace lyx