rgheck wrote:
Wrong title. And the previous patch wasn't quite right, either.
And it still wasn't right. Newer one attached....
Anyway, the attached patch allows you to bind multiple LFUNs to a
single key. LyX will use the first one that is enabled. It does work,
in the sense that you can have both completion-accept and cell-forward
bound to Tab, and this works as it should in tables.
The problem is that it is entirely unclear how to integrate this with
the shortcut editing UI. Ideas are welcome.
Richard
Index: src/KeyMap.h
===================================================================
--- src/KeyMap.h (revision 26504)
+++ src/KeyMap.h (working copy)
@@ -29,6 +29,7 @@
/// Defines key maps and actions for key sequences
class KeyMap {
public:
+ /// Enum to indicate source of binding
enum ItemType {
System, //< loaded from a bind file
UserBind, //< \bind loaded from user.bind
@@ -37,21 +38,28 @@
UserExtraUnbind //< \unbind loaded from user.bind, without
//< corresponding entry in system bind file.
};
+ /// Enum to indicate how to handle new binding
+ enum BindType {
+ Replace, //< Replace existing binding
+ Append //< Append to list of bindings
+ };
/**
* Bind/Unbind a key sequence to an action.
+ * @param append if true, will not overwrite but append as an additional binding
* @return 0 on success, or position in string seq where error
* occurs.
* See KeySequence::parse for the syntax of the seq string
*/
- size_t bind(std::string const & seq, FuncRequest const & func);
+ size_t bind(std::string const & seq, FuncRequest const & func, BindType type);
size_t unbind(std::string const & seq, FuncRequest const & func);
/**
* Define/Undefine an action for a key sequence.
* @param r internal recursion level
+ * @param append if true, will not overwrite but append as an additional binding
*/
void bind(KeySequence * seq, FuncRequest const & func,
- unsigned int r = 0);
+ BindType type, unsigned int r = 0);
void unbind(KeySequence * seq, FuncRequest const & func,
unsigned int r = 0);
@@ -143,6 +151,16 @@
///
struct Key {
+ /// is this function somewhere in the list of bindings?
+ bool hasBinding(FuncRequest const &) const;
+ /// overwrites all existing bindings
+ void rebind(FuncRequest const &);
+ /// adds to the end of the list of bindings
+ void appendBinding(FuncRequest const &);
+ /// removes from the list of bindings
+ bool removeBinding(FuncRequest const &);
+ /// are we bound to at least one thing?
+ bool empty() const { return lfuns.empty(); }
/// Keysym
KeySymbol code;
/// Modifier masks
@@ -150,7 +168,7 @@
/// Keymap for prefix keys
boost::shared_ptr<KeyMap> table;
/// Action for !prefix keys
- FuncRequest func;
+ std::vector<FuncRequest> lfuns;
};
/**
Index: src/KeyMap.cpp
===================================================================
--- src/KeyMap.cpp (revision 26504)
+++ src/KeyMap.cpp (working copy)
@@ -15,6 +15,7 @@
#include "KeyMap.h"
+#include "FuncStatus.h"
#include "KeySequence.h"
#include "LyXAction.h"
#include "Lexer.h"
@@ -33,7 +34,44 @@
namespace lyx {
+extern FuncStatus getStatus(FuncRequest const & action);
+bool KeyMap::Key::hasBinding(FuncRequest const & func) const
+{
+ return find(lfuns.begin(), lfuns.end(), func) != lfuns.end();
+}
+
+
+void KeyMap::Key::rebind(FuncRequest const & func)
+{
+ lfuns.clear();
+ FuncRequest newfunc(func);
+ newfunc.origin = FuncRequest::KEYBOARD;
+ lfuns.push_back(newfunc);
+}
+
+
+void KeyMap::Key::appendBinding(FuncRequest const & func)
+{
+ FuncRequest newfunc(func);
+ newfunc.origin = FuncRequest::KEYBOARD;
+ lfuns.push_back(newfunc);
+}
+
+
+bool KeyMap::Key::removeBinding(FuncRequest const & func)
+{
+ vector<FuncRequest>::iterator it =
+ find(lfuns.begin(), lfuns.end(), func);
+ if (it == lfuns.end())
+ return false;
+ lfuns.erase(it);
+ if (lfuns.empty() && table.get())
+ table.reset();
+ return true;
+}
+
+
string const KeyMap::printKeySym(KeySymbol const & key, KeyModifier mod)
{
string buf;
@@ -52,7 +90,8 @@
}
-size_t KeyMap::bind(string const & seq, FuncRequest const & func)
+size_t KeyMap::bind(string const & seq, FuncRequest const & func,
+ KeyMap::BindType bt)
{
LYXERR(Debug::KBMAP, "BIND: Sequence `" << seq << "' Action `"
<< func.action << '\'');
@@ -61,7 +100,7 @@
string::size_type const res = k.parse(seq);
if (res == string::npos) {
- bind(&k, func);
+ bind(&k, func, bt);
} else {
LYXERR(Debug::KBMAP, "Parse error at position " << res
<< " in key sequence '" << seq << "'.");
@@ -102,7 +141,7 @@
&& mod1 == it->mod.first
&& mod2 == it->mod.second) {
if (r + 1 == seq.length())
- return it->func == func;
+ return it->hasBinding(func);
else if (it->table.get())
return it->table->hasBinding(seq, func, r + 1);
}
@@ -117,15 +156,48 @@
}
+namespace {
+ struct BindInfo
+ {
+ string seq;
+ FuncRequest func;
+ };
+
+ bool readBindInfo(Lexer & lexrc, BindInfo & bi)
+ {
+ if (!lexrc.next()) {
+ lexrc.printError("readBindInfo: Missing key sequence");
+ return false;
+ }
+ bi.seq = lexrc.getString();
+
+ if (!lexrc.next(true)) {
+ lexrc.printError("readBindInfo: missing command");
+ return false;
+ }
+ string cmd = lexrc.getString();
+
+ bi.func = lyxaction.lookupFunc(cmd);
+ if (bi.func.action == LFUN_UNKNOWN_ACTION) {
+ lexrc.printError("readBindInfo: Unknown LyX function `$$Token'");
+ return false;
+ }
+ return true;
+ }
+} // anonymous namespace
+
+
bool KeyMap::read(string const & bind_file, KeyMap * unbind_map)
{
enum {
+ BN_ADDBIND,
BN_BIND,
BN_BINDFILE,
BN_UNBIND,
};
LexerKeyword bindTags[] = {
+ { "\\addbind", BN_ADDBIND },
{ "\\bind", BN_BIND },
{ "\\bind_file", BN_BINDFILE },
{ "\\unbind", BN_UNBIND },
@@ -157,61 +229,33 @@
continue;
case BN_BIND: {
- if (!lexrc.next()) {
- lexrc.printError("BN_BIND: Missing key sequence");
- error = true;
- break;
- }
- string seq = lexrc.getString();
-
- 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'");
- error = true;
- break;
- }
-
- bind(seq, func);
+ BindInfo bi;
+ error = !readBindInfo(lexrc, bi);
+ if (!error)
+ bind(bi.seq, bi.func, Replace);
break;
}
case BN_UNBIND: {
- if (!lexrc.next()) {
- lexrc.printError("BN_UNBIND: Missing key sequence");
- error = true;
+ BindInfo bi;
+ error = !readBindInfo(lexrc, bi);
+ if (error)
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);
+ unbind_map->bind(bi.seq, bi.func, Replace);
else
- unbind(seq, func);
+ unbind(bi.seq, bi.func);
break;
}
+ case BN_ADDBIND: {
+ BindInfo bi;
+ error = !readBindInfo(lexrc, bi);
+ if (!error)
+ bind(bi.seq, bi.func, Append);
+ break;
+ }
+
case BN_BINDFILE:
if (!lexrc.next()) {
lexrc.printError("BN_BINDFILE: Missing file name");
@@ -262,12 +306,12 @@
FuncRequest const & KeyMap::lookup(KeySymbol const &key,
KeyModifier mod, KeySequence * seq) const
{
- static FuncRequest const unknown(LFUN_UNKNOWN_ACTION);
+ static FuncRequest const theUnknownLFUN(LFUN_UNKNOWN_ACTION);
if (table.empty()) {
seq->curmap = seq->stdmap;
seq->mark_deleted();
- return unknown;
+ return theUnknownLFUN;
}
Table::const_iterator end = table.end();
@@ -286,7 +330,17 @@
// final key - reset map
seq->curmap = seq->stdmap;
seq->mark_deleted();
- return cit->func;
+ // We now try to find the first lfun in the list that is enabled.
+ vector<FuncRequest>::const_iterator it = cit->lfuns.begin();
+ vector<FuncRequest>::const_iterator en = cit->lfuns.end();
+ for (; it != en; ++it) {
+ FuncStatus const status = lyx::getStatus(*it);
+ if (status.enabled())
+ return *it;
+ }
+ // None of the associated functions were enabled, so just return the first
+ // if it's there, otherwise theUnknownLFUN
+ return cit->lfuns.empty() ? theUnknownLFUN : cit->lfuns.front();
}
}
}
@@ -295,7 +349,7 @@
seq->curmap = seq->stdmap;
seq->mark_deleted();
- return unknown;
+ return theUnknownLFUN;
}
@@ -311,7 +365,8 @@
}
-void KeyMap::bind(KeySequence * seq, FuncRequest const & func, unsigned int r)
+void KeyMap::bind(KeySequence * seq, FuncRequest const & func, KeyMap::BindType bt,
+ unsigned int r)
{
KeySymbol code = seq->sequence[r];
if (!code.isOK())
@@ -326,6 +381,13 @@
if (code == it->code
&& mod1 == it->mod.first
&& mod2 == it->mod.second) {
+ // found the key
+ if (bt == Append) {
+ LYXERR(Debug::KBMAP, "Appending new binding for `"
+ << to_utf8(seq->print(KeySequence::Portable)) << "'");
+ it->appendBinding(func);
+ return;
+ }
// overwrite binding
if (r + 1 == seq->length()) {
LYXERR(Debug::KBMAP, "Warning: New binding for '"
@@ -334,32 +396,36 @@
if (it->table.get()) {
it->table.reset();
}
- it->func = func;
- it->func.origin = FuncRequest::KEYBOARD;
+ it->rebind(func);
return;
} else if (!it->table.get()) {
- lyxerr << "Error: New binding for '"
+ LYXERR0("Error: New binding for '"
<< to_utf8(seq->print(KeySequence::Portable))
- << "' is overriding old binding..."
- << endl;
+ << "' is overriding old binding...");
return;
} else {
- it->table->bind(seq, func, r + 1);
+ it->table->bind(seq, func, bt, r + 1);
return;
}
}
}
+ // key was not found
+ if (bt == Append)
+ LYXERR0("Error: Creating new binding of '"
+ << to_utf8(seq->print(KeySequence::Portable))
+ << "' to `" << func <<
+ "' since no old binding exists!");
+
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->rebind(func);
newone->table.reset();
} else {
newone->table.reset(new KeyMap);
- newone->table->bind(seq, func, r + 1);
+ newone->table->bind(seq, func, bt, r + 1);
}
}
@@ -382,10 +448,9 @@
&& mod2 == it->mod.second) {
// remove
if (r + 1 == seq->length()) {
- if (it->func == func) {
- remove = it;
- if (it->table.get())
- it->table.reset();
+ if (it->removeBinding(func)) {
+ if (it->empty())
+ remove = it;
}
} else if (it->table.get()) {
it->table->unbind(seq, func, r + 1);
@@ -438,7 +503,7 @@
seq.addkey(cit->code, cit->mod.first);
Bindings res2 = cit->table->findBindings(func, seq);
res.insert(res.end(), res2.begin(), res2.end());
- } else if (cit->func == func) {
+ } else if (cit->hasBinding(func)) {
KeySequence seq = prefix;
seq.addkey(cit->code, cit->mod.first);
res.push_back(seq);
@@ -488,7 +553,10 @@
} else {
KeySequence seq = prefix;
seq.addkey(it->code, it->mod.first);
- list.push_back(Binding(it->func, seq, tag));
+ FuncRequest bnd;
+ if (!it->lfuns.empty())
+ bnd = it->lfuns.front();
+ list.push_back(Binding(bnd, seq, tag));
}
}
}
Index: src/frontends/qt4/GuiPrefs.cpp
===================================================================
--- src/frontends/qt4/GuiPrefs.cpp (revision 26504)
+++ src/frontends/qt4/GuiPrefs.cpp (working copy)
@@ -2306,7 +2306,7 @@
case KeyMap::System: {
// for system bind, we do not touch the item
// but add an user unbind item
- user_unbind_.bind(shortcut, func);
+ user_unbind_.bind(shortcut, func, KeyMap::Replace);
setItemType(items[i], KeyMap::UserUnbind);
removePB->setText(qt_("Res&tore"));
break;
@@ -2435,7 +2435,7 @@
QTreeWidgetItem * item = insertShortcutItem(func, k, KeyMap::UserBind);
if (item) {
- user_bind_.bind(&k, func);
+ user_bind_.bind(&k, func, KeyMap::Replace);
shortcutsTW->sortItems(0, Qt::AscendingOrder);
shortcutsTW->setItemExpanded(item->parent(), true);
shortcutsTW->scrollToItem(item);
Index: lib/bind/site.bind
===================================================================
--- lib/bind/site.bind (revision 26501)
+++ lib/bind/site.bind (working copy)
@@ -25,7 +25,8 @@
\bind "Up" "up"
\bind "Down" "down"
-\bind "Tab" "cell-forward"
+\bind "Tab" "completion-accept"
+\addbind "Tab" "cell-forward"
\bind "C-Tab" "cell-split"
\bind "~S-ISO_Left_Tab" "cell-backward"
\bind "~S-BackTab" "cell-backward"