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