From: Jean-Marc Lasgouttes [lasgout...@lyx.org]
Sent: Friday, July 20, 2012 4:45 AM

>The problem is that the LFUN is mixing real hidden buffers (visible nowhere)
>with the ones that are visible in some other window. I think these two things
>are very different from a user point of view.

>Conclusion: you should not ask only the current view for visibleness (?), but
>all views.

From: Jean-Marc Lasgouttes [lasgout...@lyx.org]
Sent: Friday, July 20, 2012 5:06 AM

>* the default should be "all buffers visible in a view"

I agree with the above points you made. LFUN_BUFFER_FORALL now supports
multiple views and I tried to add some structure on the terms "visible" and
"hidden", which are now defined in the LFUN documentation. The language is kind
of confusing, but I cannot think of a clearer way. I'm pasting the definitions
here for your thoughts and so that the rest of this email makes sense:

+ * \li Notion: A buffer is `locally visible' with respect to a view if it
+               is visible within that view. If not, it is `locally hidden'.
+               A buffer is `globally visible' if there exists an open view in
+               which it is locally visible. If not, it is `globally hidden'.


I decided to use the term "view" in the LFUN documentation. Even though I think
the term "window" is more familiar to users, I think "view" will help the new
user realize the connection of a buffer being "visible" if it is in a "view"
(similar words). Actually, if this is my argument then maybe "viewable" is a
better word than "visible". Any thoughts?

>* It may be better to force the use of the first argument, because as it is
>this argument is not well defined. You could use * for all buffers, as in
>inset-forall

The first argument is now required. This is also a good idea because it avoids
the problem if someone misspells the first argument. For example, if the
command were 'buffer-forall hiden self-insert hello', buffer-forall would have
treated "hiden" as the LFUN. The first argument is now
[lVisible|gVisible|lHidden|gHidden|*]

Note that lVisible does not make sense with no tabs (the user might as well
just run the LFUN directly). lHidden could be useful without tabs if you wanted
to run an LFUN on all buffers *except* the one you're working on.

I could have made the first argument [lVisible|gVisible|lHidden|gHidden|*]
into two arguments: [visible|hidden] [locally|globally]. I'm not sure where
"*" would fit in best there. Any thoughts?

>* if you want to handle multiple view, I suspect that the logic of you main
>loop will have to change. An alternative would be to invoke
>BufferView::dispatch or even Buffer::dispatch, but this is restrictive.

The main loop did change a lot. I am a little disappointed that the code had to
get a lot more complicated, but I don't see an easier way and I think it is
worth it. I went through several iterations of different logic and ended up on
the one in the attached patch. I hope I did not fall into the feature-bloat
trap but now most sets of buffers are supported and the terms are well-defined.
I used more comments than usual because of the added complexity. Please let me
know which comments I should remove.

>* if the action closes the only open buffer of the lyxview, does it crash?

'buffer-forall * buffer-close' works fine.
'buffer-forall * lyx-quit' does not (for more than one buffer open). I did not
try to address this. Should I?

I tested buffer-forall a lot with "Open documents in tabs" checked and 
unchecked.
I'm not sure if this is enough to know how things will work on Mac and Windows.
Should I put out an email asking for testers on lyx-devel?

Any comments on variable names, style, or general logic would be great. There
are some things that could still be added. For example, perhaps the user wants
to run the LFUN on all buffers that are visible in all windows. "visible in all
windows" (i.e. "locally visible" in all views) is not a supported set by
buffer-forall. Please let me know if you can think of additional functionality
that is missing and that should be added.

I'm sorry to take up so much of your time on this LFUN, JMarc. 

Thanks,

Scott
diff --git a/src/LyXAction.cpp b/src/LyXAction.cpp
index bf3db52..9f83b5c 100644
--- a/src/LyXAction.cpp
+++ b/src/LyXAction.cpp
@@ -3139,22 +3139,32 @@ void LyXAction::init()
 		{ LFUN_BUFFER_WRITE_AS, "buffer-write-as", ReadOnly, Buffer },
 /*!
  * \var lyx::FuncCode lyx::LFUN_BUFFER_FORALL
- * \li Action: Applies a command to all visible, hidden, or both types of buffers in the active window.
- * \li Syntax: buffer-forall [<BUFFER-TYPE>] <LFUN-COMMAND>
- * \li Params: <BUFFER-TYPE>: <visible|hidden|both default:> default: visible               
-               <LFUN-COMMAND>: The command that is to be applied to the buffers.
- * \li Sample: Close all Notes in all visible documents: \n
-	           buffer-forall inset-forall Note inset-toggle close \n
-               Toggle change tracking on all documents: \n
-	           buffer-forall both changes-track \n
-               Toggle read-only for all visible documents: \n
-	           buffer-forall buffer-toggle-read-only \n
-               Show statistics for each document: \n
-	           buffer-forall both statistics \n
-               Activate the branch named "Solutions" in all visible documents: \n
-	           buffer-forall branch-activate Solutions \n
-               Export all visible documents to PDF (pdflatex): \n
-	           buffer-forall buffer-export pdf2 \n
+ * \li Action: Applies a command to all (locally or globally) visible, hidden,
+               or both types of buffers.
+ * \li Notion: A buffer is `locally visible' with respect to a view if it
+               is visible within that view. If not, it is `locally hidden'.
+               A buffer is `globally visible' if there exists an open view in
+               which it is locally visible. If not, it is `globally hidden'.
+ * \li Syntax: buffer-forall <BUFFER-TYPE> <LFUN-COMMAND>
+ * \li Params: <BUFFER-TYPE>: <gVisible|gHidden|lVisible|lHidden|*>
+               `*' is used to mean all buffers.
+               <LFUN-COMMAND>: The command to be applied to the buffers.
+ * \li Sample: Close all Notes in all globally visible buffers: \n
+	           buffer-forall gVisible inset-forall Note inset-toggle close \n
+               Toggle change tracking on all open buffers: \n
+	           buffer-forall * changes-track \n
+               Toggle read-only for all locally visible buffer: \n
+	           buffer-forall lVisible buffer-toggle-read-only \n
+               Show statistics for each buffer: \n
+	           buffer-forall * statistics \n
+               Activate the branch named "Solutions" in all buffers: \n
+	           buffer-forall * branch-activate Solutions \n
+               Export all globally hidden buffers to PDF (pdflatex): \n
+	           buffer-forall gHidden buffer-export pdf2 \n
+               If `Open documents in tabs' is unchecked in preferences,
+               the following will close all Notes in all buffers except the one
+               in the view where the mini-buffer dispatched buffer-forall : \n
+	           buffer-forall lHidden inset-forall Note inset-toggle close \n
  * \li Origin: scottkostyshak, 20 Jul 2012
  * \endvar
  */
diff --git a/src/frontends/qt4/GuiApplication.cpp b/src/frontends/qt4/GuiApplication.cpp
index c5048dd..0b587c1 100644
--- a/src/frontends/qt4/GuiApplication.cpp
+++ b/src/frontends/qt4/GuiApplication.cpp
@@ -1092,8 +1092,20 @@ bool GuiApplication::getStatus(FuncRequest const & cmd, FuncStatus & flag) const
 		break;
 
 	case LFUN_BUFFER_FORALL: {
-		if (!currentView() || !currentView()->currentBufferView() || !&currentView()->currentBufferView()->buffer()) {
-			flag.message(from_utf8(N_("Command not allowed without any visible document in the active window")));
+		if (theBufferList().empty()) {
+			flag.message(from_utf8(N_("Command not allowed without any buffer open")));
+			flag.setEnabled(false);
+			break;
+		}
+		if (cmd.getArg(0) != "gVisible" && cmd.getArg(0) != "*" && cmd.getArg(0) != "gHidden" && cmd.getArg(0) != "lVisible" && cmd.getArg(0) != "lHidden") {
+			flag.message(from_utf8(N_("The first argument is required and must be one of \"gVisible\", \"gHidden\", \"*\", \"lVisible\", or \"lHidden\"")));
+			flag.setEnabled(false);
+			break;
+		}
+
+		FuncRequest const cmdToPass = lyxaction.lookupFunc(cmd.getLongArg(1));
+		if (cmdToPass.action() == LFUN_UNKNOWN_ACTION) {
+			flag.message(from_utf8(N_("the <LFUN-COMMAND> argument of buffer-forall is not valid")));
 			flag.setEnabled(false);
 		}
 		break;
@@ -1616,59 +1628,108 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 	}
 
 	case LFUN_BUFFER_FORALL: {
-		GuiView * gv = currentView();
-		Buffer * const buf = &gv->currentBufferView()->buffer();
-
-		bool processVisible = true;
-		bool processHidden = false;
-		docstring msg = _("Applied the following command to all visible buffers in the active window: ");
-		string commandToRun = argument;
-		if (cmd.getArg(0) == "both") {
-			processHidden = true;
-			msg = _("Applied the following command to all visible and hidden buffers in the active window: ");
-			commandToRun = cmd.getLongArg(1);
-		} else if (cmd.getArg(0) == "visible") {
-			commandToRun = cmd.getLongArg(1);
-		} else if (cmd.getArg(0) == "hidden") {
-			processHidden = true;
-			processVisible = false;
-			commandToRun = cmd.getLongArg(1);
-			msg = _("Applied the following command to all hidden buffers in the active window: ");
-		}
+		bool const processVisible = (cmd.getArg(0) == "gVisible" || cmd.getArg(0) == "lVisible" || cmd.getArg(0) == "*");
+		bool const processHidden = (cmd.getArg(0) == "*" || cmd.getArg(0) == "gHidden" || cmd.getArg(0) == "lHidden");
+
+		string const commandToRun = cmd.getLongArg(1);
 		FuncRequest const funcToRun = lyxaction.lookupFunc(commandToRun);
-		dr.setMessage(bformat(_("%1$s%2$s"), msg, from_utf8(commandToRun)));
 
+		map<Buffer *, GuiView *> views_lVisible;
+		map<GuiView *, Buffer *> activeBuffers;
+
+		QList<GuiView *> viewsToProcess;
+		if (cmd.getArg(0) == "lVisible" || cmd.getArg(0) == "lHidden")
+			viewsToProcess.append(currentView());
+		else
+			viewsToProcess = d->views_.values();
+
+		//this foreach does not modify any buffer. It just collects info on local visibility of buffers
+		//and on which buffer is active in each view.
+		foreach (GuiView * view, viewsToProcess) {
+			//all of the buffers might be locally hidden. That is, there is no active buffer.
+			if (!view || !view->currentBufferView() || !&view->currentBufferView()->buffer())
+				activeBuffers[view] = 0;
+			else
+				activeBuffers[view] = &view->currentBufferView()->buffer();
+
+			Buffer * const last = theBufferList().last();
+			Buffer * b = theBufferList().first();
+
+			//find out if each is locally visible or locally hidden.
+			//we cannot use a for loop as the buffer list cycles.
+			while (true) {
+				bool const locallyVisible = view && view->workArea(*b);
+				if (locallyVisible) {
+					bool const exists_ = (views_lVisible.find(b) != views_lVisible.end());
+					//only need to overwrite/add if we don't already know a buffer is gVisible
+					//or we do know but we would prefer to dispatch LFUN from the current view
+					//because of cursor position issues.
+					if (!exists_ || (exists_ && views_lVisible[b] != current_view_))
+						views_lVisible[b] = view;
+				}
+				if (b == last)
+					break;
+				b = theBufferList().next(b);
+			}
+		}
+
+		GuiView * const homeView = currentView();
 		Buffer * const last = theBufferList().last();
 		Buffer * b = theBufferList().first();
 		Buffer * nextBuf = 0;
-		// We cannot use a for loop as the buffer list cycles.
+		int numHiddenProcessed = 0;
+		int numVisibleProcessed = 0;
 		while (true) {
 			if (b != last)
-				nextBuf = theBufferList().next(b); //get next now bc LFUN might close current 
-
-			bool const hidden = !(gv && gv->workArea(*b));
-			if (hidden) {
-				if (processHidden) {
-					gv->setBuffer(b);
-					lyx::dispatch(funcToRun);
-					gv->currentWorkArea()->view().hideWorkArea(gv->currentWorkArea());
+				nextBuf = theBufferList().next(b); //get next now bc LFUN might close current.
+
+			bool const globallyVisible = (views_lVisible.find(b) != views_lVisible.end());
+				if (globallyVisible) {
+					if (processVisible) {
+						//first change to a view where b is lVisible, preferably current_view_.
+						GuiView * const vLv = views_lVisible[b];
+						vLv->setBuffer(b);
+						lyx::dispatch(funcToRun);
+						numVisibleProcessed++;
+					}
 				}
-			}
-
-			else {
-				if (processVisible) {
-					gv->setBuffer(b);
-					lyx::dispatch(funcToRun);
+				else {
+					if (processHidden) {
+						homeView->setBuffer(b);
+						lyx::dispatch(funcToRun);
+						numHiddenProcessed++;
+						if (theBufferList().isLoaded(b)) //re-hide buffer if it is still there.
+							homeView->currentWorkArea()->view().hideWorkArea(homeView->currentWorkArea());
+					}
 				}
-			}
 
 			if (b == last)
 				break;
 			b = nextBuf;
 		}
 
-		if (theBufferList().isLoaded(buf)) //the LFUN might have closed buf
-			gv->setBuffer(buf);
+		//put things back to how they were (if possible).
+		foreach (GuiView * view, viewsToProcess) {
+			Buffer * originalBuf = activeBuffers[view];
+			//there might not have been an active buffer in this view or it might have been closed by the LFUN.
+			if (theBufferList().isLoaded(originalBuf))
+				view->setBuffer(originalBuf);
+		}
+		homeView->setFocus();
+
+		if (cmd.getArg(0) == "gVisible")
+			dr.setMessage(bformat(_("Applied \"%1$s\" to %2$d globally visible buffer(s)"), from_utf8(commandToRun), numVisibleProcessed));
+		else if (cmd.getArg(0) == "lVisible")
+			dr.setMessage(bformat(_("Applied \"%1$s\" to %2$d locally visible buffer(s)"), from_utf8(commandToRun), numVisibleProcessed));
+		else if (cmd.getArg(0) == "*")
+			dr.setMessage(bformat(_("Applied \"%1$s\" to %2$d globally visible and %3$d globally hidden buffers"), from_utf8(commandToRun),
+			numVisibleProcessed, numHiddenProcessed));
+		else if (cmd.getArg(0) == "gHidden")
+			dr.setMessage(bformat(_("Applied \"%1$s\" to %2$d globally hidden buffer(s)"), from_utf8(commandToRun), numHiddenProcessed));
+		else if (cmd.getArg(0) == "lHidden")
+			dr.setMessage(bformat(_("Applied \"%1$s\" to %2$d locally hidden buffer(s)"), from_utf8(commandToRun), numHiddenProcessed));
+		else
+			LASSERT(false, /**/);
 		break;
 	}
 
diff --git a/src/support/lstrings.cpp b/src/support/lstrings.cpp
index 1aee6f2..0bf5aa0 100644
--- a/src/support/lstrings.cpp
+++ b/src/support/lstrings.cpp
@@ -1362,6 +1362,17 @@ docstring bformat(docstring const & fmt, docstring arg1, int arg2)
 	return subst(str, from_ascii("%%"), from_ascii("%"));
 }
 
+template<>
+docstring bformat(docstring const & fmt, docstring arg1, int arg2, int arg3)
+{
+	LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
+	LASSERT(contains(fmt, from_ascii("%2$d")), /**/);
+	LASSERT(contains(fmt, from_ascii("%3$d")), /**/);
+	docstring str = subst(fmt, from_ascii("%1$s"), arg1);
+	str = subst(str, from_ascii("%2$d"), convert<docstring>(arg2));
+	str = subst(str, from_ascii("%3$d"), convert<docstring>(arg3));
+	return subst(str, from_ascii("%%"), from_ascii("%"));
+}
 
 template<>
 docstring bformat(docstring const & fmt, char const * arg1, docstring arg2)
diff --git a/src/support/lstrings.h b/src/support/lstrings.h
index 23ac712..23cb33f 100644
--- a/src/support/lstrings.h
+++ b/src/support/lstrings.h
@@ -306,6 +306,7 @@ 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, docstring arg1, int arg2, int arg3);
 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);

Reply via email to