Here is the second iteration of my inset-forall patch. This version
allows for matching all insets with the syntax
  inset-forall * cmd args
and for matching insets names with spaces
  inset-forall "inset name" cms args

To this end, I have added a FuncRequest::getLongArg, that I will commit
separately. [with this new method, I think it will be possible now to
replace all explicit parsing of cmd.argument(). It could even be
possible to declare the number and form of the arguments in
LyXAction.cpp]

Another new 'feature' is that the number of iteration is limited to
avoid endless loops. This is a crude hack to avoid shooting oneself in
the foot. In this version, the number of iterations is limited to 1000.
Is it large enough?

A few questions remain:

* if I use "inset-forall Note inset-toggle close", this fails in the
  case of a note in a note. I still do not know why

* for each inset, lyx::dispatch is invoked with the cursor in front of
  the inset. An alternative would be to invoke inset->dispatch. These
  two alternatives allow for different applications... What shall I do?

JMarc

svndiff

Index: src/LyXAction.cpp
===================================================================
--- src/LyXAction.cpp	(révision 32281)
+++ src/LyXAction.cpp	(copie de travail)
@@ -3434,6 +3434,27 @@ void LyXAction::init()
 		{ LFUN_GRAPHICS_RELOAD, "graphics-reload", ReadOnly | AtPoint, Edit },
 
 
+/*!
+ * \var lyx::FuncCode lyx::LFUN_INSET_FORALL
+
+ * \li Action: Apply the given commands on insets of a given name. WARNING: use
+               at your own risks; this function gives you too many ways of
+	       shooting yourself in the foot. A ty[ical example is
+                   inset-forall Note note-insert
+               which starts an infinite loop. This is mitigated by the fact 
+               that the number of actions is arbitrarily limited to 1000.
+ * \li Syntax: inset-forall <NAME> <LFUN-COMMAND>
+               If <NAME> is *, all insets are matched.
+ * \li Sample: The name is used like for InsetLayout in layout files: "Note" 
+               matches all note insets, while "Note:Note" only matches LyX 
+	       yellow note insets. The following command closes all note insets
+                   inset-forall Note inset-toggle close
+ * \li Origin: lasgouttes, 27 nov. 2009
+ * \endvar
+ */
+		{ LFUN_INSET_FORALL, "inset-forall", ReadOnly, Edit },
+
+
 		{ LFUN_NOACTION, "", Noop, Hidden }
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
 	};
Index: src/BufferView.cpp
===================================================================
--- src/BufferView.cpp	(révision 32281)
+++ src/BufferView.cpp	(copie de travail)
@@ -1090,6 +1090,7 @@ bool BufferView::getStatus(FuncRequest c
 	case LFUN_SCROLL:
 	case LFUN_SCREEN_UP_SELECT:
 	case LFUN_SCREEN_DOWN_SELECT:
+	case LFUN_INSET_FORALL:
 		flag.setEnabled(true);
 		break;
 
@@ -1729,6 +1730,53 @@ bool BufferView::dispatch(FuncRequest co
 		break;
 	}
 
+
+	// This would be in Buffer class if only Cursor did not
+	// require a bufferview
+	case LFUN_INSET_FORALL: {
+		docstring const name = from_utf8(cmd.getArg(0));
+		string const commandstr = cmd.getLongArg(1);
+		FuncRequest const fr = lyxaction.lookupFunc(commandstr);
+
+		// an arbitrary number to limit number of iterations
+		const int max_iter = 1000;
+		int iterations = 0;
+		Cursor & cur = d->cursor_;
+		cur.reset();
+		if (!cur.nextInset())
+			cur.forwardInset();
+		cur.beginUndoGroup();
+		while(cur && iterations < max_iter) {
+			Inset * ins = cur.nextInset();
+			if (!ins)
+				break;
+			docstring insname = ins->name();
+			while (!insname.empty()) {
+				if (insname == name || name == from_utf8("*")) {
+					cur.recordUndo();
+					lyx::dispatch(fr);
+					++iterations;
+					break;
+				}
+				size_t const i = insname.rfind(':');
+				if (i == string::npos)
+					break;
+				insname = insname.substr(0, i);
+			}
+			cur.forwardInset();
+		}
+		cur.endUndoGroup();
+		cur.reset();
+		processUpdateFlags(Update::Force);
+
+		if (iterations >= max_iter)
+			cur.errorMessage(bformat(_("inset-forall interrupted because number of actions is larger than %1$s"), max_iter));
+		else
+			cur.message(bformat(_("Applied \"%1$s\" to %2$d insets"), from_utf8(commandstr), iterations));
+		break;
+	}
+
+
 	case LFUN_ALL_INSETS_TOGGLE: {
 		string action;
 		string const name = split(to_utf8(cmd.argument()), action, ' ');
Index: src/FuncRequest.h
===================================================================
--- src/FuncRequest.h	(révision 32281)
+++ src/FuncRequest.h	(copie de travail)
@@ -64,6 +64,10 @@ public:
 	/// argument parsing, extract argument i as std::string
 	std::string getArg(unsigned int i) const;
 
+	/// argument parsing, extract argument i as std::string,
+	/// eating all characters up to the end of the command line
+	std::string getLongArg(unsigned int i) const;
+
 	/// access the whole argument
 	docstring const & argument() const { return argument_; }
 
Index: src/support/lstrings.h
===================================================================
--- src/support/lstrings.h	(révision 32281)
+++ src/support/lstrings.h	(copie de travail)
@@ -282,6 +282,7 @@ template<> docstring bformat(docstring c
 template<> docstring bformat(docstring const & fmt, docstring arg1);
 template<> docstring bformat(docstring const & fmt, char * arg1);
 template<> docstring bformat(docstring const & fmt, docstring arg1, docstring arg2);
+template<> docstring bformat(docstring const & fmt, docstring arg1, int arg2);
 template<> docstring bformat(docstring const & fmt, char const * arg1, docstring arg2);
 template<> docstring bformat(docstring const & fmt, int arg1, int arg2);
 template<> docstring bformat(docstring const & fmt, docstring arg1, docstring arg2, docstring arg3);
Index: src/support/lstrings.cpp
===================================================================
--- src/support/lstrings.cpp	(révision 32281)
+++ src/support/lstrings.cpp	(copie de travail)
@@ -1171,6 +1171,17 @@ docstring bformat(docstring const & fmt,
 
 
 template<>
+docstring bformat(docstring const & fmt, docstring arg1, int arg2)
+{
+	LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
+	LASSERT(contains(fmt, from_ascii("%2$d")), /**/);
+	docstring str = subst(fmt, from_ascii("%1$s"), arg1);
+	str = subst(str, from_ascii("%2$d"), convert<docstring>(arg2));
+	return subst(str, from_ascii("%%"), from_ascii("%"));
+}
+
+
+template<>
 docstring bformat(docstring const & fmt, char const * arg1, docstring arg2)
 {
 	LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
Index: src/FuncCode.h
===================================================================
--- src/FuncCode.h	(révision 32281)
+++ src/FuncCode.h	(copie de travail)
@@ -443,6 +443,7 @@ enum FuncCode
 	LFUN_GRAPHICS_RELOAD,           // vfr 20090810
 	LFUN_SCREEN_SHOW_CURSOR,        // vfr, 20090325
 	LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE, // ARRae 971202
+	LFUN_INSET_FORALL,		// lasgouttes, 20091127
 
 	LFUN_LASTACTION                 // end of the table
 };
Index: src/FuncRequest.cpp
===================================================================
--- src/FuncRequest.cpp	(révision 32281)
+++ src/FuncRequest.cpp	(copie de travail)
@@ -13,11 +13,14 @@
 #include "FuncRequest.h"
 #include "LyXAction.h"
 
+#include "support/lstrings.h"
+
 #include <iostream>
 #include <sstream>
 #include <vector>
 
 using namespace std;
+using namespace lyx::support;
 
 namespace lyx {
 
@@ -71,10 +74,19 @@ mouse_button::state FuncRequest::button(
 }
 
 
-void splitArg(vector<string> & args, string const & str)
+namespace {
+
+void splitArg(vector<string> & args, string const & str, unsigned int max)
 {
 	istringstream is(str);
 	while (is) {
+		if (args.size() == max) {
+			string s;
+			getline(is, s);
+			args.push_back(trim(s));
+			return;
+		}		
+
 		char c;
 		string s;
 		is >> c;
@@ -90,11 +102,20 @@ void splitArg(vector<string> & args, str
 	}
 }
 
+}
 
 string FuncRequest::getArg(unsigned int i) const
 {
 	vector<string> args;
-	splitArg(args, to_utf8(argument_));
+	splitArg(args, to_utf8(argument_), string::npos);
+	return i < args.size() ? args[i] : string();
+}
+
+
+string FuncRequest::getLongArg(unsigned int i) const
+{
+	vector<string> args;
+	splitArg(args, to_utf8(argument_), i);
 	return i < args.size() ? args[i] : string();
 }
 

Reply via email to