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