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