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