The attached patch is a start on this. It does work, in the sense that you can have both completion-accept and cell-forward bound to Tab, and this works 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.cpp
===================================================================
--- src/KeyMap.cpp	(revision 26501)
+++ 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,
+                    bool append)
 {
 	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, append);
 	} 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);
 		}
@@ -123,9 +162,11 @@
 		BN_BIND,
 		BN_BINDFILE,
 		BN_UNBIND,
+		BN_ADDBIND
 	};
 
 	LexerKeyword bindTags[] = {
+		{ "\\addbind",   BN_ADDBIND },
 		{ "\\bind",      BN_BIND },
 		{ "\\bind_file", BN_BINDFILE },
 		{ "\\unbind",    BN_UNBIND },
@@ -212,6 +253,32 @@
 			break;
 		}
 
+		case BN_ADDBIND: {
+			if (!lexrc.next()) {
+				lexrc.printError("BN_ADDBIND: Missing key sequence");
+				error = true;
+				break;
+			}
+			string seq = lexrc.getString();
+
+			if (!lexrc.next(true)) {
+				lexrc.printError("BN_ADDBIND: missing command");
+				error = true;
+				break;
+			}
+			string cmd = lexrc.getString();
+
+			FuncRequest func = lyxaction.lookupFunc(cmd);
+			if (func.action == LFUN_UNKNOWN_ACTION) {
+				lexrc.printError("BN_ADDBIND: Unknown LyX function `$$Token'");
+				error = true;
+				break;
+			}
+
+			bind(seq, func, true);
+			break;
+		}
+
 		case BN_BINDFILE:
 			if (!lexrc.next()) {
 				lexrc.printError("BN_BINDFILE: Missing file name");
@@ -286,7 +353,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 unknown
+				return cit->lfuns.empty() ? unknown : cit->lfuns.front();
 			}
 		}
 	}
@@ -311,7 +388,8 @@
 }
 
 
-void KeyMap::bind(KeySequence * seq, FuncRequest const & func, unsigned int r)
+void KeyMap::bind(KeySequence * seq, FuncRequest const & func, bool append,
+                  unsigned int r)
 {
 	KeySymbol code = seq->sequence[r];
 	if (!code.isOK())
@@ -326,6 +404,13 @@
 		if (code == it->code
 		    && mod1 == it->mod.first
 		    && mod2 == it->mod.second) {
+			// found the key
+			if (append) {
+				LYXERR(Debug::KBMAP, "Adding 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 +419,35 @@
 				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, append, r + 1);
 				return;
 			}
 		}
 	}
 
+	// key was not found
+	if (append)
+		LYXERR(Debug::KBMAP, "Warning: Creating new binding for '"
+					<< to_utf8(seq->print(KeySequence::Portable))
+					<< "' 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, append, r + 1);
 	}
 }
 
@@ -382,10 +470,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 +525,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 +575,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/KeyMap.h
===================================================================
--- src/KeyMap.h	(revision 26501)
+++ src/KeyMap.h	(working copy)
@@ -29,21 +29,31 @@
 /// Defines key maps and actions for key sequences
 class KeyMap {
 public:
+	enum ItemType {
+		System,         //< loaded from a bind file
+		UserBind,       //< \bind loaded from user.bind
+		UserUnbind,     //< \unbind loaded from user.bind, with corresponding
+		                //<    entry in system bind file
+		UserExtraUnbind	//< \unbind loaded from user.bind, without
+		                //<    corresponding entry in system bind file.
+	};
 	/**
 	 * 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, bool append = false);
 	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);
+		    bool append = false, unsigned int r = 0);
 	void unbind(KeySequence * seq, FuncRequest const & func,
 		    unsigned int r = 0);
 
@@ -135,6 +145,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
@@ -142,7 +162,7 @@
 		/// Keymap for prefix keys
 		boost::shared_ptr<KeyMap> table;
 		/// Action for !prefix keys
-		FuncRequest func;
+		std::vector<FuncRequest> lfuns;
 	};
 
 	/**
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"

Reply via email to