X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FKeyMap.cpp;h=af5f5b8223eaf1c685bb7654c6672b606a010123;hb=68045958ee470f9caa57d49265ae3ffc7456dcda;hp=bef688fe54eb3d383119304944a05cd4ec62e89a;hpb=5265a8c02c7223f72dda771fd776c104bc729487;p=lyx.git diff --git a/src/KeyMap.cpp b/src/KeyMap.cpp index bef688fe54..af5f5b8223 100644 --- a/src/KeyMap.cpp +++ b/src/KeyMap.cpp @@ -3,10 +3,10 @@ * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * - * \author Lars Gullik Bjønnes + * \author Lars Gullik Bjønnes * \author Jean-Marc Lasgouttes * \author John Levon - * \author André Pönitz + * \author André Pönitz * * Full author contact details are available in file CREDITS. */ @@ -15,40 +15,48 @@ #include "KeyMap.h" -#include "debug.h" #include "KeySequence.h" #include "LyXAction.h" #include "Lexer.h" -#include "frontends/LyXKeySym.h" - +#include "support/debug.h" +#include "support/docstream.h" +#include "support/FileName.h" #include "support/filetools.h" +#include "support/gettext.h" +#include "support/lstrings.h" +#include "support/TempFile.h" + +#include "frontends/alert.h" +#include #include +#include +using namespace std; +using namespace lyx::support; namespace lyx { -using support::FileName; -using support::i18nLibFileSearch; - -using std::endl; -using std::string; - - -string const KeyMap::printKeySym(LyXKeySym const & key, - key_modifier::state mod) +string const KeyMap::printKeySym(KeySymbol const & key, KeyModifier mod) { string buf; string const s = key.getSymbolName(); - if (mod & key_modifier::shift) - buf += "S-"; - if (mod & key_modifier::ctrl) + if (mod & ControlModifier) buf += "C-"; - if (mod & key_modifier::alt) +#if defined(USE_MACOSX_PACKAGING) || defined(USE_META_KEYBINDING) + if (mod & MetaModifier) buf += "M-"; + if (mod & AltModifier) + buf += "A-"; +#else + if (mod & AltModifier) + buf += "M-"; +#endif + if (mod & ShiftModifier) + buf += "S-"; buf += s; return buf; @@ -57,91 +65,276 @@ string const KeyMap::printKeySym(LyXKeySym const & key, size_t KeyMap::bind(string const & seq, FuncRequest const & func) { - LYXERR(Debug::KBMAP) << "BIND: Sequence `" - << seq << "' Action `" - << func.action << '\'' << endl; + LYXERR(Debug::KBMAP, "BIND: Sequence `" << seq << "' Action `" + << func.action() << '\''); KeySequence k(0, 0); string::size_type const res = k.parse(seq); if (res == string::npos) { - defkey(&k, func); + bind(&k, func); } else { - LYXERR(Debug::KBMAP) << "Parse error at position " << res - << " in key sequence '" << seq << "'." - << endl; + LYXERR(Debug::KBMAP, "Parse error at position " << res + << " in key sequence '" << seq << "'."); } - return res; + return res == string::npos ? 0 : res; } -namespace { +size_t KeyMap::unbind(string const & seq, FuncRequest const & func) +{ + KeySequence k(0, 0); + + string::size_type const res = k.parse(seq); + if (res == string::npos) + unbind(&k, func); + else + LYXERR(Debug::KBMAP, "Parse error at position " << res + << " in key sequence '" << seq << "'."); + return res == string::npos ? 0 : res; +} + + +void KeyMap::bind(KeySequence * seq, FuncRequest const & func, unsigned int r) +{ + KeySymbol code = seq->sequence[r]; + if (!code.isOK()) + return; + + KeyModifier const mod1 = seq->modifiers[r].first; + KeyModifier const mod2 = seq->modifiers[r].second; + + // check if key is already there + // FIXME perf: Profiling shows that this is responsible of 99% of the + // cost of GuiPrefs::applyView() + Table::iterator end = table.end(); + for (Table::iterator it = table.begin(); it != end; ++it) { + if (code == it->code + && mod1 == it->mod.first + && mod2 == it->mod.second) { + // overwrite binding + if (r + 1 == seq->length()) { + LYXERR(Debug::KBMAP, "Warning: New binding for '" + << to_utf8(seq->print(KeySequence::Portable)) + << "' is overriding old binding..."); + if (it->prefixes) + it->prefixes.reset(); + it->func = func; + it->func.setOrigin(FuncRequest::KEYBOARD); + return; + } else if (!it->prefixes) { + lyxerr << "Error: New binding for '" + << to_utf8(seq->print(KeySequence::Portable)) + << "' is overriding old binding..." + << endl; + return; + } else { + it->prefixes->bind(seq, func, r + 1); + return; + } + } + } -enum BindTags { - BN_BIND, - BN_BINDFILE -}; + Table::iterator newone = table.insert(table.end(), Key()); + newone->code = code; + newone->mod = seq->modifiers[r]; + if (r + 1 == seq->length()) { + newone->func = func; + newone->func.setOrigin(FuncRequest::KEYBOARD); + newone->prefixes.reset(); + } else { + newone->prefixes.reset(new KeyMap); + newone->prefixes->bind(seq, func, r + 1); + } +} + + +void KeyMap::unbind(KeySequence * seq, FuncRequest const & func, unsigned int r) +{ + KeySymbol code = seq->sequence[r]; + if (!code.isOK()) + return; + + KeyModifier const mod1 = seq->modifiers[r].first; + KeyModifier const mod2 = seq->modifiers[r].second; + + // check if key is already there + Table::iterator end = table.end(); + Table::iterator remove = end; + for (Table::iterator it = table.begin(); it != end; ++it) { + if (code == it->code + && mod1 == it->mod.first + && mod2 == it->mod.second) { + // remove + if (r + 1 == seq->length()) { + if (it->func == func) { + remove = it; + if (it->prefixes) + it->prefixes.reset(); + } + } else if (it->prefixes) { + it->prefixes->unbind(seq, func, r + 1); + if (it->prefixes->empty()) + remove = it; + return; + } + } + } + if (remove != end) + table.erase(remove); +} + + +FuncRequest KeyMap::getBinding(KeySequence const & seq, unsigned int r) +{ + KeySymbol code = seq.sequence[r]; + if (!code.isOK()) + return FuncRequest::unknown; + + KeyModifier const mod1 = seq.modifiers[r].first; + KeyModifier const mod2 = seq.modifiers[r].second; + + // check if key is already there + Table::iterator end = table.end(); + for (Table::iterator it = table.begin(); it != end; ++it) { + if (code == it->code + && mod1 == it->mod.first + && mod2 == it->mod.second) { + if (r + 1 == seq.length()) + return it->func; + else if (it->prefixes) + return it->prefixes->getBinding(seq, r + 1); + } + } + return FuncRequest::unknown; +} + + +void KeyMap::clear() +{ + table.clear(); +} + + +bool KeyMap::read(string const & bind_file, KeyMap * unbind_map, BindReadType rt) +{ + FileName bf = i18nLibFileSearch("bind", bind_file, "bind"); + if (bf.empty()) { + if (rt == MissingOK) + return true; + + lyxerr << "Could not find bind file: " << bind_file; + if (rt == Default) { + frontend::Alert::warning(_("Could not find bind file"), + bformat(_("Unable to find the bind file\n%1$s.\n" + "Please check your installation."), from_utf8(bind_file))); + return false; + } -keyword_item bindTags[] = { - { "\\bind", BN_BIND }, - { "\\bind_file", BN_BINDFILE } -}; + static string const defaultBindfile = "cua"; + if (bind_file == defaultBindfile) { + frontend::Alert::warning(_("Could not find `cua.bind' file"), + _("Unable to find the default bind file `cua.bind'.\n" + "Please check your installation.")); + return false; + } + // Try it with the default file. + frontend::Alert::warning(_("Could not find bind file"), + bformat(_("Unable to find the bind file\n%1$s.\n" + "Falling back to default."), from_utf8(bind_file))); + return read(defaultBindfile, unbind_map); + } + return read(bf, unbind_map); } -bool KeyMap::read(string const & bind_file) +bool KeyMap::read(FileName const & bind_file, KeyMap * unbind_map) { - const int bindCount = sizeof(bindTags) / sizeof(keyword_item); + ReturnValues retval = readWithoutConv(bind_file, unbind_map); + if (retval != FormatMismatch) + return retval == ReadOK; + + LYXERR(Debug::FILES, "Converting bind file to " << LFUN_FORMAT); + TempFile tmp("convert_bind"); + FileName const tempfile = tmp.name(); + bool const success = prefs2prefs(bind_file, tempfile, true); + if (!success) { + LYXERR0 ("Unable to convert " << bind_file << + " to format " << LFUN_FORMAT); + return false; + } + retval = readWithoutConv(tempfile, unbind_map); + return retval == ReadOK; +} + - Lexer lexrc(bindTags, bindCount); +KeyMap::ReturnValues KeyMap::readWithoutConv(FileName const & bind_file, KeyMap * unbind_map) +{ + enum { + BN_BIND, + BN_BINDFILE, + BN_FORMAT, + BN_UNBIND + }; + + LexerKeyword bindTags[] = { + { "\\bind", BN_BIND }, + { "\\bind_file", BN_BINDFILE }, + { "\\unbind", BN_UNBIND }, + { "format", BN_FORMAT } + }; + + Lexer lexrc(bindTags); if (lyxerr.debugging(Debug::PARSER)) lexrc.printTable(lyxerr); - FileName const tmp(i18nLibFileSearch("bind", bind_file, "bind")); - lexrc.setFile(tmp); + lexrc.setFile(bind_file); if (!lexrc.isOK()) { - lyxerr << "KeyMap::read: cannot open bind file:" - << tmp << endl; - return false; + LYXERR0("KeyMap::read: cannot open bind file:" << bind_file.absFileName()); + return FileError; } - LYXERR(Debug::KBMAP) << "Reading bind file:" << tmp << endl; + LYXERR(Debug::KBMAP, "Reading bind file:" << bind_file.absFileName()); + // format of pre-2.0 bind files, before this tag was introduced. + unsigned int format = 0; bool error = false; while (lexrc.isOK()) { switch (lexrc.lex()) { + case Lexer::LEX_UNDEF: lexrc.printError("Unknown tag `$$Token'"); error = true; continue; + case Lexer::LEX_FEOF: continue; - case BN_BIND: - { - string seq, cmd; - if (lexrc.next()) { - seq = lexrc.getString(); - } else { + case BN_FORMAT: + if (lexrc.next()) + format = lexrc.getInteger(); + break; + + case BN_BIND: { + if (!lexrc.next()) { lexrc.printError("BN_BIND: Missing key sequence"); error = true; break; } + string seq = lexrc.getString(); - if (lexrc.next(true)) { - cmd = lexrc.getString(); - } else { + if (!lexrc.next(true)) { lexrc.printError("BN_BIND: missing command"); error = true; break; } + string cmd = lexrc.getString(); FuncRequest func = lyxaction.lookupFunc(cmd); - if (func. action == LFUN_UNKNOWN_ACTION) { - lexrc.printError("BN_BIND: Unknown LyX" - " function `$$Token'"); + if (func.action() == LFUN_UNKNOWN_ACTION) { + lexrc.printError("BN_BIND: Unknown LyX function `$$Token'"); error = true; break; } @@ -149,66 +342,124 @@ bool KeyMap::read(string const & bind_file) bind(seq, func); break; } + + case BN_UNBIND: { + if (!lexrc.next()) { + lexrc.printError("BN_UNBIND: Missing key sequence"); + error = true; + break; + } + string seq = lexrc.getString(); + + if (!lexrc.next(true)) { + lexrc.printError("BN_UNBIND: missing command"); + error = true; + break; + } + string cmd = lexrc.getString(); + + FuncRequest func = lyxaction.lookupFunc(cmd); + if (func.action() == LFUN_UNKNOWN_ACTION) { + lexrc.printError("BN_UNBIND: Unknown LyX" + " function `$$Token'"); + error = true; + break; + } + + if (unbind_map) + unbind_map->bind(seq, func); + else + unbind(seq, func); + break; + } + case BN_BINDFILE: - if (lexrc.next()) { - string const tmp(lexrc.getString()); - error |= !read(tmp); - } else { + if (!lexrc.next()) { lexrc.printError("BN_BINDFILE: Missing file name"); error = true; break; - } + string const tmp = lexrc.getString(); + error |= !read(tmp, unbind_map); break; } + + // This is triggered the first time through the loop unless + // we hit a format tag. + if (format != LFUN_FORMAT) + return FormatMismatch; } - if (error) - lyxerr << "KeyMap::read: error while reading bind file:" - << tmp << endl; - return !error; + if (error) { + LYXERR0("KeyMap::read: error while reading bind file:" << bind_file.absFileName()); + return ReadError; + } + return ReadOK; } -FuncRequest const & -KeyMap::lookup(LyXKeySymPtr key, - key_modifier::state mod, KeySequence * seq) const +void KeyMap::write(string const & bind_file, bool append, bool unbind) const { - static FuncRequest const unknown(LFUN_UNKNOWN_ACTION); + ofstream os(bind_file.c_str(), + append ? (ios::app | ios::out) : ios::out); + + if (!append) + os << "## This file is automatically generated by lyx\n" + << "## All modifications will be lost\n\n" + << "Format " << LFUN_FORMAT << "\n\n"; + + string tag = unbind ? "\\unbind" : "\\bind"; + BindingList const list = listBindings(false); + BindingList::const_iterator it = list.begin(); + BindingList::const_iterator it_end = list.end(); + for (; it != it_end; ++it) { + FuncCode action = it->request.action(); + string arg = to_utf8(it->request.argument()); + + string const cmd = lyxaction.getActionName(action) + + (arg.empty() ? string() : " " + arg) ; + os << tag << " \"" + << to_utf8(it->sequence.print(KeySequence::BindFile)) + << "\" " << Lexer::quoteString(cmd) + << "\n"; + } + os << "\n"; + os.close(); +} + +FuncRequest const & KeyMap::lookup(KeySymbol const &key, + KeyModifier mod, KeySequence * seq) const +{ if (table.empty()) { - seq->curmap = seq->stdmap; - seq->mark_deleted(); - return unknown; + seq->reset(); + return FuncRequest::unknown; } Table::const_iterator end = table.end(); for (Table::const_iterator cit = table.begin(); cit != end; ++cit) { - key_modifier::state mask(cit->mod.second); - key_modifier::state check = - static_cast(mod & ~mask); + KeyModifier mask = cit->mod.second; + KeyModifier check = static_cast(mod & ~mask); - if (*(cit->code) == *key && cit->mod.first == check) { + if (cit->code == key && cit->mod.first == check) { // match found - if (cit->table.get()) { + if (cit->prefixes) { // this is a prefix key - set new map - seq->curmap = cit->table.get(); - static FuncRequest prefix(LFUN_COMMAND_PREFIX); + seq->curmap = cit->prefixes.get(); + static const FuncRequest prefix(LFUN_COMMAND_PREFIX); return prefix; } else { // final key - reset map - seq->curmap = seq->stdmap; - seq->mark_deleted(); + seq->reset(); return cit->func; } } } // error - key not found: - seq->curmap = seq->stdmap; - seq->mark_deleted(); + seq->reset(); - return unknown; + return FuncRequest::unknown; } @@ -217,99 +468,51 @@ docstring const KeyMap::print(bool forgui) const docstring buf; Table::const_iterator end = table.end(); for (Table::const_iterator cit = table.begin(); cit != end; ++cit) { - buf += cit->code->print(cit->mod.first, forgui); + buf += cit->code.print(cit->mod.first, forgui); buf += ' '; } return buf; } -void KeyMap::defkey(KeySequence * seq, FuncRequest const & func, unsigned int r) +docstring KeyMap::printBindings(FuncRequest const & func, + KeySequence::outputFormat format) const { - LyXKeySymPtr code = seq->sequence[r]; - if (!code->isOK()) - return; + Bindings bindings = findBindings(func); + if (bindings.empty()) + return docstring(); - key_modifier::state const mod1 = seq->modifiers[r].first; - key_modifier::state const mod2 = seq->modifiers[r].second; - - // check if key is already there - Table::iterator end = table.end(); - for (Table::iterator it = table.begin(); it != end; ++it) { - if (*(code) == *(it->code) - && mod1 == it->mod.first - && mod2 == it->mod.second) { - // overwrite binding - if (r + 1 == seq->length()) { - LYXERR(Debug::KBMAP) - << "Warning: New binding for '" - << to_utf8(seq->print(false)) - << "' is overriding old binding..." - << endl; - if (it->table.get()) { - it->table.reset(); - } - it->func = func; - it->func.origin = FuncRequest::KEYBOARD; - return; - } else if (!it->table.get()) { - lyxerr << "Error: New binding for '" - << to_utf8(seq->print(false)) - << "' is overriding old binding..." - << endl; - return; - } else { - it->table->defkey(seq, func, r + 1); - return; - } - } - } - - Table::iterator newone = table.insert(table.end(), Key()); - newone->code = code; - newone->mod = seq->modifiers[r]; - if (r + 1 == seq->length()) { - newone->func = func; - newone->func.origin = FuncRequest::KEYBOARD; - newone->table.reset(); - } else { - newone->table.reset(new KeyMap); - newone->table->defkey(seq, func, r + 1); - } -} - - -docstring const KeyMap::printbindings(FuncRequest const & func) const -{ odocstringstream res; - Bindings bindings = findbindings(func); - for (Bindings::const_iterator cit = bindings.begin(); - cit != bindings.end() ; ++cit) - res << '[' << cit->print(true) << ']'; + Bindings::const_iterator cit = bindings.begin(); + Bindings::const_iterator cit_end = bindings.end(); + // print the first item + res << cit->print(format); + // more than one shortcuts? + for (++cit; cit != cit_end; ++cit) + res << ", " << cit->print(format); return res.str(); } -KeyMap::Bindings KeyMap::findbindings(FuncRequest const & func) const +KeyMap::Bindings KeyMap::findBindings(FuncRequest const & func) const { - return findbindings(func, KeySequence(0, 0)); + return findBindings(func, KeySequence(0, 0)); } -KeyMap::Bindings KeyMap::findbindings(FuncRequest const & func, +KeyMap::Bindings KeyMap::findBindings(FuncRequest const & func, KeySequence const & prefix) const { Bindings res; - if (table.empty()) return res; + if (table.empty()) + return res; Table::const_iterator end = table.end(); - for (Table::const_iterator cit = table.begin(); - cit != end; ++cit) { - if (cit->table.get()) { + for (Table::const_iterator cit = table.begin(); cit != end; ++cit) { + if (cit->prefixes) { KeySequence seq = prefix; seq.addkey(cit->code, cit->mod.first); - Bindings res2 = - cit->table->findbindings(func, seq); + Bindings res2 = cit->prefixes->findBindings(func, seq); res.insert(res.end(), res2.begin(), res2.end()); } else if (cit->func == func) { KeySequence seq = prefix; @@ -322,17 +525,48 @@ KeyMap::Bindings KeyMap::findbindings(FuncRequest const & func, } -std::pair -KeyMap::find1keybinding(FuncRequest const & func) const +KeyMap::BindingList KeyMap::listBindings(bool unbound, KeyMap::ItemType tag) const { - Table::const_iterator end = table.end(); - for (Table::const_iterator cit = table.begin(); - cit != end; ++cit) { - if (!cit->table.get() && cit->func == func) - return std::make_pair(cit->code.get(), cit->mod.first); + BindingList list; + listBindings(list, KeySequence(0, 0), tag); + if (unbound) { + LyXAction::const_iterator fit = lyxaction.func_begin(); + LyXAction::const_iterator const fen = lyxaction.func_end(); + for (; fit != fen; ++fit) { + FuncCode action = fit->second; + bool has_action = false; + BindingList::const_iterator bit = list.begin(); + BindingList::const_iterator const ben = list.end(); + for (; bit != ben; ++bit) + if (bit->request.action() == action) { + has_action = true; + break; + } + if (!has_action) + list.push_back(Binding(FuncRequest(action), KeySequence(0, 0), tag)); + } } + return list; +} + - return std::make_pair(0, key_modifier::none); +void KeyMap::listBindings(BindingList & list, + KeySequence const & prefix, KeyMap::ItemType tag) const +{ + Table::const_iterator it = table.begin(); + Table::const_iterator it_end = table.end(); + for (; it != it_end; ++it) { + // a LFUN_COMMAND_PREFIX + if (it->prefixes) { + KeySequence seq = prefix; + seq.addkey(it->code, it->mod.first); + it->prefixes->listBindings(list, seq, tag); + } else { + KeySequence seq = prefix; + seq.addkey(it->code, it->mod.first); + list.push_back(Binding(it->func, seq, tag)); + } + } }