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