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(); }