]> git.lyx.org Git - lyx.git/blob - src/kbmap.C
b744f9d88f1771fa187fd2bf154f09c9b4ce8705
[lyx.git] / src / kbmap.C
1 /**
2  * \file kbmap.C
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 "kbmap.h"
17
18 #include "debug.h"
19 #include "kbsequence.h"
20 #include "LyXAction.h"
21 #include "lyxlex.h"
22
23 #include "frontends/LyXKeySym.h"
24
25 #include "support/filetools.h"
26
27 #include <sstream>
28
29
30 namespace lyx {
31
32 using support::FileName;
33 using support::i18nLibFileSearch;
34
35 using std::endl;
36 using std::string;
37
38
39 string const kb_keymap::printKeySym(LyXKeySym const & key,
40                                     key_modifier::state mod)
41 {
42         string buf;
43
44         string const s = key.getSymbolName();
45
46         if (mod & key_modifier::shift)
47                 buf += "S-";
48         if (mod & key_modifier::ctrl)
49                 buf += "C-";
50         if (mod & key_modifier::alt)
51                 buf += "M-";
52
53         buf += s;
54         return buf;
55 }
56
57
58 string const kb_keymap::printKey(kb_key const & key) const
59 {
60         return key.code->print(key.mod.first);
61 }
62
63
64 string::size_type kb_keymap::bind(string const & seq, FuncRequest const & func)
65 {
66         if (lyxerr.debugging(Debug::KBMAP)) {
67                 lyxerr << "BIND: Sequence `"
68                        << seq << "' Action `"
69                        << func.action << '\'' << endl;
70         }
71
72         kb_sequence k(0, 0);
73
74         string::size_type const res = k.parse(seq);
75         if (res == string::npos) {
76                 defkey(&k, func);
77         } else {
78                 lyxerr[Debug::KBMAP] << "Parse error at position " << res
79                                      << " in key sequence '" << seq << "'."
80                                      << endl;
81         }
82
83         return res;
84 }
85
86
87 namespace {
88
89 enum BindTags {
90         BN_BIND,
91         BN_BINDFILE
92 };
93
94 keyword_item bindTags[] = {
95         { "\\bind", BN_BIND },
96         { "\\bind_file", BN_BINDFILE }
97 };
98
99 }
100
101
102 bool kb_keymap::read(string const & bind_file)
103 {
104         const int bindCount = sizeof(bindTags) / sizeof(keyword_item);
105
106         LyXLex lexrc(bindTags, bindCount);
107         if (lyxerr.debugging(Debug::PARSER))
108                 lexrc.printTable(lyxerr);
109
110         FileName const tmp(i18nLibFileSearch("bind", bind_file, "bind"));
111         lexrc.setFile(tmp);
112         if (!lexrc.isOK()) {
113                 lyxerr << "kb_keymap::read: cannot open bind file:"
114                        << tmp << endl;
115                 return false;
116         }
117
118         lyxerr[Debug::KBMAP] << "Reading bind file:" << tmp << endl;
119
120         bool error = false;
121         while (lexrc.isOK()) {
122                 switch (lexrc.lex()) {
123                 case LyXLex::LEX_UNDEF:
124                         lexrc.printError("Unknown tag `$$Token'");
125                         error = true;
126                         continue;
127                 case LyXLex::LEX_FEOF:
128                         continue;
129                 case BN_BIND:
130                 {
131                         string seq, cmd;
132
133                         if (lexrc.next()) {
134                                 seq = lexrc.getString();
135                         } else {
136                                 lexrc.printError("BN_BIND: Missing key sequence");
137                                 error = true;
138                                 break;
139                         }
140
141                         if (lexrc.next(true)) {
142                                 cmd = lexrc.getString();
143                         } else {
144                                 lexrc.printError("BN_BIND: missing command");
145                                 error = true;
146                                 break;
147                         }
148
149                         FuncRequest func = lyxaction.lookupFunc(cmd);
150                         if (func. action == LFUN_UNKNOWN_ACTION) {
151                                 lexrc.printError("BN_BIND: Unknown LyX"
152                                                  " function `$$Token'");
153                                 error = true;
154                                 break;
155                         }
156
157                         bind(seq, func);
158                         break;
159                 }
160                 case BN_BINDFILE:
161                         if (lexrc.next()) {
162                                 string const tmp(lexrc.getString());
163                                 error |= !read(tmp);
164                         } else {
165                                 lexrc.printError("BN_BINDFILE: Missing file name");
166                                 error = true;
167                                 break;
168
169                         }
170                         break;
171                 }
172         }
173
174         if (error)
175                 lyxerr << "kb_keymap::read: error while reading bind file:"
176                        << tmp << endl;
177         return !error;
178 }
179
180
181 FuncRequest const &
182 kb_keymap::lookup(LyXKeySymPtr key,
183                   key_modifier::state mod, kb_sequence * seq) const
184 {
185         static FuncRequest const unknown(LFUN_UNKNOWN_ACTION);
186
187         if (table.empty()) {
188                 seq->curmap = seq->stdmap;
189                 seq->mark_deleted();
190                 return unknown;
191         }
192
193         Table::const_iterator end = table.end();
194         for (Table::const_iterator cit = table.begin(); cit != end; ++cit) {
195                 key_modifier::state mask(cit->mod.second);
196                 key_modifier::state check =
197                         static_cast<key_modifier::state>(mod & ~mask);
198
199                 if (*(cit->code) == *key && cit->mod.first == check) {
200                         // match found
201                         if (cit->table.get()) {
202                                 // this is a prefix key - set new map
203                                 seq->curmap = cit->table.get();
204                                 static FuncRequest prefix(LFUN_COMMAND_PREFIX);
205                                 return prefix;
206                         } else {
207                                 // final key - reset map
208                                 seq->curmap = seq->stdmap;
209                                 seq->mark_deleted();
210                                 return cit->func;
211                         }
212                 }
213         }
214
215         // error - key not found:
216         seq->curmap = seq->stdmap;
217         seq->mark_deleted();
218
219         return unknown;
220 }
221
222
223 string const kb_keymap::print() const
224 {
225         string buf;
226         Table::const_iterator end = table.end();
227         for (Table::const_iterator cit = table.begin(); cit != end; ++cit) {
228                 buf += printKey((*cit));
229                 buf += ' ';
230         }
231         return buf;
232 }
233
234
235 void kb_keymap::defkey(kb_sequence * seq,
236                        FuncRequest const & func, unsigned int r)
237 {
238         LyXKeySymPtr code = seq->sequence[r];
239         if (!code->isOK())
240                 return;
241
242         key_modifier::state const mod1 = seq->modifiers[r].first;
243         key_modifier::state const mod2 = seq->modifiers[r].second;
244
245         // check if key is already there
246         Table::iterator end = table.end();
247         for (Table::iterator it = table.begin(); it != end; ++it) {
248                 if (*(code) == *(it->code)
249                     && mod1 == it->mod.first
250                     && mod2 == it->mod.second) {
251                         // overwrite binding
252                         if (r + 1 == seq->length()) {
253                                 lyxerr[Debug::KBMAP]
254                                         << "Warning: New binding for '"
255                                         << seq->print()
256                                         << "' is overriding old binding..."
257                                         << endl;
258                                 if (it->table.get()) {
259                                         it->table.reset();
260                                 }
261                                 it->func = func;
262                                 it->func.origin = FuncRequest::KEYBOARD;
263                                 return;
264                         } else if (!it->table.get()) {
265                                 lyxerr << "Error: New binding for '" << seq->print()
266                                        << "' is overriding old binding..."
267                                                << endl;
268                                 return;
269                         } else {
270                                 it->table->defkey(seq, func, r + 1);
271                                 return;
272                         }
273                 }
274         }
275
276         Table::iterator newone = table.insert(table.end(), kb_key());
277         newone->code = code;
278         newone->mod = seq->modifiers[r];
279         if (r + 1 == seq->length()) {
280                 newone->func = func;
281                 newone->func.origin = FuncRequest::KEYBOARD;
282                 newone->table.reset();
283                 return;
284         } else {
285                 newone->table.reset(new kb_keymap);
286                 newone->table->defkey(seq, func, r + 1);
287                 return;
288         }
289 }
290
291
292 string const kb_keymap::printbindings(FuncRequest const & func) const
293 {
294         std::ostringstream res;
295         Bindings bindings = findbindings(func);
296         for (Bindings::const_iterator cit = bindings.begin();
297              cit != bindings.end() ; ++cit)
298                 res << '[' << cit->print() << ']';
299         return res.str();
300 }
301
302
303 kb_keymap::Bindings
304 kb_keymap::findbindings(FuncRequest const & func) const
305 {
306         return findbindings(func, kb_sequence(0, 0));
307 }
308
309
310 kb_keymap::Bindings
311 kb_keymap::findbindings(FuncRequest const & func,
312                         kb_sequence const & prefix) const
313 {
314         Bindings res;
315         if (table.empty()) return res;
316
317         Table::const_iterator end = table.end();
318         for (Table::const_iterator cit = table.begin();
319             cit != end; ++cit) {
320                 if (cit->table.get()) {
321                         kb_sequence seq = prefix;
322                         seq.addkey(cit->code, cit->mod.first);
323                         Bindings res2 =
324                                 cit->table->findbindings(func, seq);
325                         res.insert(res.end(), res2.begin(), res2.end());
326                 } else if (cit->func == func) {
327                         kb_sequence seq = prefix;
328                         seq.addkey(cit->code, cit->mod.first);
329                         res.push_back(seq);
330                 }
331         }
332
333         return res;
334 }
335
336
337 std::pair<LyXKeySym const *, key_modifier::state>
338 kb_keymap::find1keybinding(FuncRequest const & func) const
339 {
340         Table::const_iterator end = table.end();
341         for (Table::const_iterator cit = table.begin();
342             cit != end; ++cit) {
343                 if (!cit->table.get() && cit->func == func)
344                         return std::make_pair(cit->code.get(), cit->mod.first);
345         }
346
347         return std::make_pair<LyXKeySym const *, key_modifier::state>(0, key_modifier::none);
348 }
349
350
351 } // namespace lyx