3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Jean-Marc Lasgouttes
11 * Full author contact details are available in file CREDITS.
18 #include "KeySequence.h"
19 #include "LyXAction.h"
22 #include "support/debug.h"
23 #include "support/docstream.h"
24 #include "support/FileName.h"
25 #include "support/filetools.h"
26 #include "support/gettext.h"
27 #include "support/lstrings.h"
29 #include "frontends/alert.h"
36 using namespace lyx::support
;
41 string
const KeyMap::printKeySym(KeySymbol
const & key
, KeyModifier mod
)
45 string
const s
= key
.getSymbolName();
47 if (mod
& ControlModifier
)
49 if (mod
& AltModifier
)
51 if (mod
& ShiftModifier
)
59 size_t KeyMap::bind(string
const & seq
, FuncRequest
const & func
)
61 LYXERR(Debug::KBMAP
, "BIND: Sequence `" << seq
<< "' Action `"
62 << func
.action
<< '\'');
66 string::size_type
const res
= k
.parse(seq
);
67 if (res
== string::npos
) {
70 LYXERR(Debug::KBMAP
, "Parse error at position " << res
71 << " in key sequence '" << seq
<< "'.");
74 return res
== string::npos
? 0 : res
;
78 size_t KeyMap::unbind(string
const & seq
, FuncRequest
const & func
)
82 string::size_type
const res
= k
.parse(seq
);
83 if (res
== string::npos
)
86 LYXERR(Debug::KBMAP
, "Parse error at position " << res
87 << " in key sequence '" << seq
<< "'.");
88 return res
== string::npos
? 0 : res
;
92 void KeyMap::bind(KeySequence
* seq
, FuncRequest
const & func
, unsigned int r
)
94 KeySymbol code
= seq
->sequence
[r
];
98 KeyModifier
const mod1
= seq
->modifiers
[r
].first
;
99 KeyModifier
const mod2
= seq
->modifiers
[r
].second
;
101 // check if key is already there
102 Table::iterator end
= table
.end();
103 for (Table::iterator it
= table
.begin(); it
!= end
; ++it
) {
105 && mod1
== it
->mod
.first
106 && mod2
== it
->mod
.second
) {
108 if (r
+ 1 == seq
->length()) {
109 LYXERR(Debug::KBMAP
, "Warning: New binding for '"
110 << to_utf8(seq
->print(KeySequence::Portable
))
111 << "' is overriding old binding...");
112 if (it
->prefixes
.get()) {
113 it
->prefixes
.reset();
116 it
->func
.origin
= FuncRequest::KEYBOARD
;
118 } else if (!it
->prefixes
.get()) {
119 lyxerr
<< "Error: New binding for '"
120 << to_utf8(seq
->print(KeySequence::Portable
))
121 << "' is overriding old binding..."
125 it
->prefixes
->bind(seq
, func
, r
+ 1);
131 Table::iterator newone
= table
.insert(table
.end(), Key());
133 newone
->mod
= seq
->modifiers
[r
];
134 if (r
+ 1 == seq
->length()) {
136 newone
->func
.origin
= FuncRequest::KEYBOARD
;
137 newone
->prefixes
.reset();
139 newone
->prefixes
.reset(new KeyMap
);
140 newone
->prefixes
->bind(seq
, func
, r
+ 1);
145 void KeyMap::unbind(KeySequence
* seq
, FuncRequest
const & func
, unsigned int r
)
147 KeySymbol code
= seq
->sequence
[r
];
151 KeyModifier
const mod1
= seq
->modifiers
[r
].first
;
152 KeyModifier
const mod2
= seq
->modifiers
[r
].second
;
154 // check if key is already there
155 Table::iterator end
= table
.end();
156 Table::iterator remove
= end
;
157 for (Table::iterator it
= table
.begin(); it
!= end
; ++it
) {
159 && mod1
== it
->mod
.first
160 && mod2
== it
->mod
.second
) {
162 if (r
+ 1 == seq
->length()) {
163 if (it
->func
== func
) {
165 if (it
->prefixes
.get())
166 it
->prefixes
.reset();
168 } else if (it
->prefixes
.get()) {
169 it
->prefixes
->unbind(seq
, func
, r
+ 1);
170 if (it
->prefixes
->empty())
181 FuncRequest
KeyMap::getBinding(KeySequence
const & seq
, unsigned int r
)
183 KeySymbol code
= seq
.sequence
[r
];
185 return FuncRequest::unknown
;
187 KeyModifier
const mod1
= seq
.modifiers
[r
].first
;
188 KeyModifier
const mod2
= seq
.modifiers
[r
].second
;
190 // check if key is already there
191 Table::iterator end
= table
.end();
192 for (Table::iterator it
= table
.begin(); it
!= end
; ++it
) {
194 && mod1
== it
->mod
.first
195 && mod2
== it
->mod
.second
) {
196 if (r
+ 1 == seq
.length())
198 else if (it
->prefixes
.get())
199 return it
->prefixes
->getBinding(seq
, r
+ 1);
202 return FuncRequest::unknown
;
212 bool KeyMap::read(string
const & bind_file
, KeyMap
* unbind_map
, BindReadType rt
)
214 FileName bf
= i18nLibFileSearch("bind", bind_file
, "bind");
219 lyxerr
<< "Could not find bind file: " << bind_file
;
221 frontend::Alert::warning(_("Could not find bind file"),
222 bformat(_("Unable to find the bind file\n%1$s.\n"
223 "Please check your installation."), from_utf8(bind_file
)));
227 static string
const defaultBindfile
= "cua";
228 if (bind_file
== defaultBindfile
) {
229 frontend::Alert::warning(_("Could not find cua bind file"),
230 _("Unable to find the default bind file `cua'.\n"
231 "Please check your installation."));
235 // Try it with the default file.
236 frontend::Alert::warning(_("Could not find bind file"),
237 bformat(_("Unable to find the bind file\n%1$s.\n"
238 "Falling back to default."), from_utf8(bind_file
)));
239 return read(defaultBindfile
, unbind_map
);
241 return read(bf
, unbind_map
);
245 bool KeyMap::read(FileName
const & bind_file
, KeyMap
* unbind_map
)
253 LexerKeyword bindTags
[] = {
254 { "\\bind", BN_BIND
},
255 { "\\bind_file", BN_BINDFILE
},
256 { "\\unbind", BN_UNBIND
},
259 Lexer
lexrc(bindTags
);
260 if (lyxerr
.debugging(Debug::PARSER
))
261 lexrc
.printTable(lyxerr
);
263 lexrc
.setFile(bind_file
);
265 LYXERR0("KeyMap::read: cannot open bind file:" << bind_file
.absFilename());
269 LYXERR(Debug::KBMAP
, "Reading bind file:" << bind_file
.absFilename());
272 while (lexrc
.isOK()) {
273 switch (lexrc
.lex()) {
275 case Lexer::LEX_UNDEF
:
276 lexrc
.printError("Unknown tag `$$Token'");
280 case Lexer::LEX_FEOF
:
285 lexrc
.printError("BN_BIND: Missing key sequence");
289 string seq
= lexrc
.getString();
291 if (!lexrc
.next(true)) {
292 lexrc
.printError("BN_BIND: missing command");
296 string cmd
= lexrc
.getString();
298 FuncRequest func
= lyxaction
.lookupFunc(cmd
);
299 if (func
.action
== LFUN_UNKNOWN_ACTION
) {
300 lexrc
.printError("BN_BIND: Unknown LyX function `$$Token'");
311 lexrc
.printError("BN_UNBIND: Missing key sequence");
315 string seq
= lexrc
.getString();
317 if (!lexrc
.next(true)) {
318 lexrc
.printError("BN_UNBIND: missing command");
322 string cmd
= lexrc
.getString();
324 FuncRequest func
= lyxaction
.lookupFunc(cmd
);
325 if (func
.action
== LFUN_UNKNOWN_ACTION
) {
326 lexrc
.printError("BN_UNBIND: Unknown LyX"
327 " function `$$Token'");
333 unbind_map
->bind(seq
, func
);
341 lexrc
.printError("BN_BINDFILE: Missing file name");
345 string
const tmp
= lexrc
.getString();
346 error
|= !read(tmp
, unbind_map
);
352 LYXERR0("KeyMap::read: error while reading bind file:" << bind_file
.absFilename());
357 void KeyMap::write(string
const & bind_file
, bool append
, bool unbind
) const
359 ofstream
os(bind_file
.c_str(),
360 append
? (ios::app
| ios::out
) : ios::out
);
363 os
<< "## This file is automatically generated by lyx\n"
364 << "## All modifications will be lost\n\n";
366 string tag
= unbind
? "\\unbind" : "\\bind";
367 BindingList
const list
= listBindings(false);
368 BindingList::const_iterator it
= list
.begin();
369 BindingList::const_iterator it_end
= list
.end();
370 for (; it
!= it_end
; ++it
) {
371 FuncCode action
= it
->request
.action
;
372 string arg
= to_utf8(it
->request
.argument());
375 << to_utf8(it
->sequence
.print(KeySequence::BindFile
))
377 << lyxaction
.getActionName(action
)
378 << (arg
.empty() ? "" : " ") << arg
386 FuncRequest
const & KeyMap::lookup(KeySymbol
const &key
,
387 KeyModifier mod
, KeySequence
* seq
) const
391 return FuncRequest::unknown
;
394 Table::const_iterator end
= table
.end();
395 for (Table::const_iterator cit
= table
.begin(); cit
!= end
; ++cit
) {
396 KeyModifier mask
= cit
->mod
.second
;
397 KeyModifier check
= static_cast<KeyModifier
>(mod
& ~mask
);
399 if (cit
->code
== key
&& cit
->mod
.first
== check
) {
401 if (cit
->prefixes
.get()) {
402 // this is a prefix key - set new map
403 seq
->curmap
= cit
->prefixes
.get();
404 static FuncRequest
prefix(LFUN_COMMAND_PREFIX
);
407 // final key - reset map
414 // error - key not found:
417 return FuncRequest::unknown
;
421 docstring
const KeyMap::print(bool forgui
) const
424 Table::const_iterator end
= table
.end();
425 for (Table::const_iterator cit
= table
.begin(); cit
!= end
; ++cit
) {
426 buf
+= cit
->code
.print(cit
->mod
.first
, forgui
);
433 docstring
KeyMap::printBindings(FuncRequest
const & func
,
434 KeySequence::outputFormat format
) const
436 Bindings bindings
= findBindings(func
);
437 if (bindings
.empty())
440 odocstringstream res
;
441 Bindings::const_iterator cit
= bindings
.begin();
442 Bindings::const_iterator cit_end
= bindings
.end();
443 // print the first item
444 res
<< cit
->print(format
);
445 // more than one shortcuts?
446 for (++cit
; cit
!= cit_end
; ++cit
)
447 res
<< ", " << cit
->print(format
);
452 KeyMap::Bindings
KeyMap::findBindings(FuncRequest
const & func
) const
454 return findBindings(func
, KeySequence(0, 0));
458 KeyMap::Bindings
KeyMap::findBindings(FuncRequest
const & func
,
459 KeySequence
const & prefix
) const
465 Table::const_iterator end
= table
.end();
466 for (Table::const_iterator cit
= table
.begin(); cit
!= end
; ++cit
) {
467 if (cit
->prefixes
.get()) {
468 KeySequence seq
= prefix
;
469 seq
.addkey(cit
->code
, cit
->mod
.first
);
470 Bindings res2
= cit
->prefixes
->findBindings(func
, seq
);
471 res
.insert(res
.end(), res2
.begin(), res2
.end());
472 } else if (cit
->func
== func
) {
473 KeySequence seq
= prefix
;
474 seq
.addkey(cit
->code
, cit
->mod
.first
);
483 KeyMap::BindingList
KeyMap::listBindings(bool unbound
, KeyMap::ItemType tag
) const
486 listBindings(list
, KeySequence(0, 0), tag
);
488 LyXAction::const_func_iterator fit
= lyxaction
.func_begin();
489 LyXAction::const_func_iterator fit_end
= lyxaction
.func_end();
490 for (; fit
!= fit_end
; ++fit
) {
491 FuncCode action
= fit
->second
;
492 bool has_action
= false;
493 BindingList::const_iterator it
= list
.begin();
494 BindingList::const_iterator it_end
= list
.end();
495 for (; it
!= it_end
; ++it
)
496 if (it
->request
.action
== action
) {
501 list
.push_back(Binding(FuncRequest(action
), KeySequence(0, 0), tag
));
508 void KeyMap::listBindings(BindingList
& list
,
509 KeySequence
const & prefix
, KeyMap::ItemType tag
) const
511 Table::const_iterator it
= table
.begin();
512 Table::const_iterator it_end
= table
.end();
513 for (; it
!= it_end
; ++it
) {
514 // a LFUN_COMMAND_PREFIX
515 if (it
->prefixes
.get()) {
516 KeySequence seq
= prefix
;
517 seq
.addkey(it
->code
, it
->mod
.first
);
518 it
->prefixes
->listBindings(list
, seq
, tag
);
520 KeySequence seq
= prefix
;
521 seq
.addkey(it
->code
, it
->mod
.first
);
522 list
.push_back(Binding(it
->func
, seq
, tag
));