OK, here's the patch we've been discussing. I'll hold it until after Beta 1, if that's what Jose would prefer. It doesn't SEEM to break things, but, well, you know.

The patch makes Buffer::dispatch() part of the dispatch system, and it moves (for now) just a few features there that can be used if we have a Buffer but not necessarily a BufferView---in particular, features that could be used from the command line. It also adds a "-nogui" command line option to run the commands given and then exit. I can separate these when I commit, of course, but the point of this isn't apparent without what's here.

For example, and as a kind of proof of concept:
   lyx -nogui -x "buffer-print printer default dvips" myfile.lyx
will print myfile.lyx to the default printer via dvips, using whatever the default print settings are. Other things can also be moved to Buffer::dispatch(), too, if this works.

Comments on the patch welcome, of course. A couple items in particular.
(i) I had to change the signatures of some of the dispatch methods to get them to play nice together. Please let me know if there was a better way to do this. (ii) From what I can tell, the LFUN_BUFFER_PRINT line in LyXFunc::getStatus never gets hit. So I didn't bother moving it or anything. It seems like this is simply controlled by whether we have a Buffer, but if so then the logic is nearly impossible to follow. I don't at all understand the comment "// these are handled in our dispatch()" that comes at the end of that long string (around line 590 of LyXFunc.cpp). Can someone explain all of this?

Richard

Index: src/Buffer.h
===================================================================
--- src/Buffer.h	(revision 24983)
+++ src/Buffer.h	(working copy)
@@ -12,6 +12,8 @@
 #ifndef BUFFER_H
 #define BUFFER_H
 
+#include "update_flags.h"
+
 #include "insets/InsetCode.h"
 
 #include "support/strfwd.h"
@@ -30,6 +32,7 @@
 class ErrorItem;
 class ErrorList;
 class FuncRequest;
+class FuncStatus;
 class Inset;
 class InsetRef;
 class InsetLabel;
@@ -119,13 +122,18 @@
 	~Buffer();
 
 	/** High-level interface to buffer functionality.
-	    This function parses a command string and executes it
+	    This function parses a command string and executes it.
+	    \return true unless there is an error 
 	*/
-	bool dispatch(std::string const & command, bool * result = 0);
+	bool dispatch(std::string const & command, Update::flags & uflags);
 
 	/// Maybe we know the function already by number...
-	bool dispatch(FuncRequest const & func, bool * result = 0);
+	/// \return true unless there is an error 
+	bool dispatch(FuncRequest const & func, Update::flags & uflags);
 
+	/// Can this function be exectued?
+	bool getStatus(FuncRequest const & cmd, FuncStatus & flag);
+
 	/// Load the autosaved file.
 	void loadAutoSaveFile();
 
Index: src/Buffer.cpp
===================================================================
--- src/Buffer.cpp	(revision 24983)
+++ src/Buffer.cpp	(working copy)
@@ -30,6 +30,7 @@
 #include "Exporter.h"
 #include "Format.h"
 #include "FuncRequest.h"
+#include "FuncStatus.h"
 #include "InsetIterator.h"
 #include "InsetList.h"
 #include "Language.h"
@@ -91,6 +92,7 @@
 #include "support/os.h"
 #include "support/Package.h"
 #include "support/Path.h"
+#include "support/Systemcall.h"
 #include "support/textutils.h"
 #include "support/types.h"
 
@@ -120,6 +122,14 @@
 typedef map<string, bool> DepClean;
 typedef map<docstring, pair<InsetLabel const *, Buffer::References> > RefCache;
 
+void showPrintError(string const & name)
+{
+	docstring str = bformat(_("Could not print the document %1$s.\n"
+					    "Check that your printer is set up correctly."),
+			     makeDisplayPath(name, 50));
+	Alert::error(_("Print document failed"), str);
+}
+
 } // namespace anon
 
 class Buffer::Impl
@@ -1410,21 +1420,50 @@
 }
 
 
-bool Buffer::dispatch(string const & command, bool * result)
+bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag)
 {
-	return dispatch(lyxaction.lookupFunc(command), result);
+	bool enable = false;
+	switch (cmd.action) {
+		case LFUN_BUFFER_EXPORT: {
+			docstring const arg = cmd.argument();
+			enable = arg == "custom" || isExportable(to_utf8(arg));
+			flag.enabled(enable);
+			return true;
+		}
+
+		case LFUN_BRANCH_ACTIVATE: 
+		case LFUN_BRANCH_DEACTIVATE: {
+			bool enable = false;
+			docstring const branchName = cmd.argument();
+			if (!branchName.empty())
+				enable = params().branchlist().find(branchName);
+			flag.enabled(enable);
+			return true;
+		}
+
+		default:
+			break;
+	}
+	return false;
 }
 
 
-bool Buffer::dispatch(FuncRequest const & func, bool * result)
+bool Buffer::dispatch(string const & command, Update::flags & uflags)
 {
-	bool dispatched = true;
+	return dispatch(lyxaction.lookupFunc(command), uflags);
+}
 
+
+// NOTE We can end up here even if we have no GUI. So we may need to 
+// check from time to time whether we have one or not. The boolean
+// use_gui holds this information.
+bool Buffer::dispatch(FuncRequest const & func, Update::flags & uflags)
+{
+	bool result = false;
+
 	switch (func.action) {
 		case LFUN_BUFFER_EXPORT: {
-			bool const tmp = doExport(to_utf8(func.argument()), false);
-			if (result)
-				*result = tmp;
+			result = doExport(to_utf8(func.argument()), false);
 			break;
 		}
 
@@ -1433,18 +1472,123 @@
 			BranchList & branchList = params().branchlist();
 			docstring const branchName = func.argument();
 			Branch * branch = branchList.find(branchName);
-			if (!branch)
+			if (!branch) {
 				LYXERR0("Branch " << branchName << " does not exist.");
-			else 
+				result = false;
+			} else {
 				branch->setSelected(func.action == LFUN_BRANCH_ACTIVATE);
-			if (result)
-				*result = true;
+				result = true;
+				uflags = uflags | Update::Force;
+			}
 		}
 
+		case LFUN_BUFFER_PRINT: {
+			string target = func.getArg(0);
+			string target_name = func.getArg(1);
+			string command = func.getArg(2);
+
+			if (target.empty()
+			    || target_name.empty()
+			    || command.empty()) {
+				lyxerr << "Unable to parse \""
+				       << to_utf8(func.argument()) << '"' << endl;
+				break;
+			}
+			if (target != "printer" && target != "file") {
+				lyxerr << "Unrecognized target \""
+				       << target << '"' << endl;
+				break;
+			}
+
+			if (!doExport("dvi", true)) {
+				showPrintError(absFileName());
+				break;
+			}
+
+			// Push directory path.
+			string const path = temppath();
+			// Prevent the compiler from optimizing away p
+			FileName pp(path);
+			PathChanger p(pp);
+
+			// there are three cases here:
+			// 1. we print to a file
+			// 2. we print directly to a printer
+			// 3. we print using a spool command (print to file first)
+			Systemcall one;
+			int res = 0;
+			string const dviname = changeExtension(latexName(true), "dvi");
+
+			if (target == "printer") {
+				if (!lyxrc.print_spool_command.empty()) {
+					// case 3: print using a spool
+					string const psname = changeExtension(dviname,".ps");
+					command += ' ' + lyxrc.print_to_file
+						+ quoteName(psname)
+						+ ' '
+						+ quoteName(dviname);
+
+					string command2 = lyxrc.print_spool_command + ' ';
+					if (target_name != "default") {
+						command2 += lyxrc.print_spool_printerprefix
+							+ target_name
+							+ ' ';
+					}
+					command2 += quoteName(psname);
+					// First run dvips.
+					// If successful, then spool command
+					res = one.startscript(Systemcall::Wait, command);
+
+					if (res == 0) {
+						// If there's no GUI, we have to wait on this command. Otherwise,
+						// LyX deletes the temporary directory, and with it the spooled
+						// file, before it can be printed!!
+						Systemcall::Starttype stype = use_gui ?
+							Systemcall::DontWait : Systemcall::Wait;
+						res = one.startscript(stype, command2);
+					}
+				} else {
+					// case 2: print directly to a printer
+					if (target_name != "default")
+						command += ' ' + lyxrc.print_to_printer + target_name + ' ';
+					// as above....
+					Systemcall::Starttype stype = use_gui ?
+						Systemcall::DontWait : Systemcall::Wait;
+					res = one.startscript(stype, command + quoteName(dviname));
+				}
+
+			} else {
+				// case 1: print to a file
+				FileName const filename(makeAbsPath(target_name, filePath()));
+				FileName const dvifile(makeAbsPath(dviname, path));
+				if (filename.exists()) {
+					docstring text = bformat(
+						_("The file %1$s already exists.\n\n"
+						  "Do you want to overwrite that file?"),
+						makeDisplayPath(filename.absFilename()));
+					if (Alert::prompt(_("Overwrite file?"),
+					    text, 0, 1, _("&Overwrite"), _("&Cancel")) != 0)
+						break;
+				}
+				command += ' ' + lyxrc.print_to_file
+					+ quoteName(filename.toFilesystemEncoding())
+					+ ' '
+					+ quoteName(dvifile.toFilesystemEncoding());
+				// as above....
+				Systemcall::Starttype stype = use_gui ?
+					Systemcall::DontWait : Systemcall::Wait;
+				res = one.startscript(stype, command);
+			}
+
+			if (res != 0)
+				showPrintError(absFileName());
+			break;
+		}
+
 		default:
-			dispatched = false;
+			break;
 	}
-	return dispatched;
+	return result;
 }
 
 
Index: src/BufferView.h
===================================================================
--- src/BufferView.h	(revision 24983)
+++ src/BufferView.h	(working copy)
@@ -184,8 +184,8 @@
 	/// translate and insert a character, using the correct keymap.
 	void translateAndInsert(char_type c, Text * t, Cursor & cur);
 
-	/// return true for events that will handle.
-	FuncStatus getStatus(FuncRequest const & cmd);
+	/// \return true if we've made a decision
+	bool getStatus(FuncRequest const & cmd, FuncStatus & flag);
 	/// execute the given function.
 	/// \return true if the function has been processed.
 	bool dispatch(FuncRequest const & argument);
Index: src/BufferView.cpp
===================================================================
--- src/BufferView.cpp	(revision 24983)
+++ src/BufferView.cpp	(working copy)
@@ -828,27 +828,25 @@
 }
 
 
-FuncStatus BufferView::getStatus(FuncRequest const & cmd)
+bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
 {
-	FuncStatus flag;
-
 	Cursor & cur = d->cursor_;
 
 	switch (cmd.action) {
 
 	case LFUN_UNDO:
 		flag.enabled(buffer_.undo().hasUndoStack());
-		break;
+		return true;
 	case LFUN_REDO:
 		flag.enabled(buffer_.undo().hasRedoStack());
-		break;
+		return true;
 	case LFUN_FILE_INSERT:
 	case LFUN_FILE_INSERT_PLAINTEXT_PARA:
 	case LFUN_FILE_INSERT_PLAINTEXT:
 	case LFUN_BOOKMARK_SAVE:
 		// FIXME: Actually, these LFUNS should be moved to Text
 		flag.enabled(cur.inTexted());
-		break;
+		return true;
 	case LFUN_FONT_STATE:
 	case LFUN_LABEL_INSERT:
 	case LFUN_INFO_INSERT:
@@ -866,7 +864,7 @@
 	case LFUN_BIBTEX_DATABASE_DEL:
 	case LFUN_STATISTICS:
 		flag.enabled(true);
-		break;
+		return true;
 
 	case LFUN_NEXT_INSET_TOGGLE: 
 	case LFUN_NEXT_INSET_MODIFY: {
@@ -879,24 +877,24 @@
 		Inset * inset = cur.nextInset();
 		if (!inset || !inset->getStatus(cur, tmpcmd, flag))
 			flag = lyx::getStatus(tmpcmd);
-		break;
+		return true;
 	}
 
 	case LFUN_LABEL_GOTO: {
 		flag.enabled(!cmd.argument().empty()
 		    || getInsetByCode<InsetRef>(cur, REF_CODE));
-		break;
+		return true;
 	}
 
 	case LFUN_CHANGES_TRACK:
 		flag.enabled(true);
 		flag.setOnOff(buffer_.params().trackChanges);
-		break;
+		return true;
 
 	case LFUN_CHANGES_OUTPUT:
 		flag.enabled(true);
 		flag.setOnOff(buffer_.params().outputChanges);
-		break;
+		return true;
 
 	case LFUN_CHANGES_MERGE:
 	case LFUN_CHANGE_NEXT:
@@ -907,11 +905,11 @@
 		// is a change in the document. However, without proper
 		// optimizations, this will inevitably result in poor performance.
 		flag.enabled(true);
-		break;
+		return true;
 
 	case LFUN_BUFFER_TOGGLE_COMPRESSION: {
 		flag.setOnOff(buffer_.params().compressed);
-		break;
+		return true;
 	}
 	
 	case LFUN_SCREEN_UP:
@@ -920,25 +918,25 @@
 	case LFUN_SCREEN_UP_SELECT:
 	case LFUN_SCREEN_DOWN_SELECT:
 		flag.enabled(true);
-		break;
+		return true;
 
 	case LFUN_LAYOUT_TABULAR:
 		flag.enabled(cur.innerInsetOfType(TABULAR_CODE));
-		break;
+		return true;
 
 	case LFUN_LAYOUT:
 		flag.enabled(!cur.inset().forceEmptyLayout(cur.idx()));
-		break;
+		return true;
 
 	case LFUN_LAYOUT_PARAGRAPH:
 		flag.enabled(cur.inset().allowParagraphCustomization(cur.idx()));
-		break;
+		return true;
 
 	case LFUN_INSET_SETTINGS: {
 		InsetCode code = cur.inset().lyxCode();
 		if (cmd.getArg(0) == insetName(code)) {
 			flag.enabled(true);
-			break;
+			return true;
 		}
 		bool enable = false;
 		InsetCode next_code = cur.nextInset()
@@ -954,39 +952,28 @@
 			case LISTINGS_CODE:
 				enable = (cmd.argument().empty() ||
 					  cmd.getArg(0) == insetName(next_code));
-				break;
+				return true;
 			default:
 				break;
 		}
 		flag.enabled(enable);
-		break;
+		return true;
 	}
 
 	case LFUN_DIALOG_SHOW_NEW_INSET:
 		flag.enabled(cur.inset().lyxCode() != ERT_CODE &&
 			cur.inset().lyxCode() != LISTINGS_CODE);
 		if (cur.inset().lyxCode() == CAPTION_CODE) {
-			FuncStatus flag;
 			if (cur.inset().getStatus(cur, cmd, flag))
-				return flag;
+				return true;
 		}
-		break;
+		return false;
 
-	case LFUN_BRANCH_ACTIVATE: 
-	case LFUN_BRANCH_DEACTIVATE: {
-		bool enable = false;
-		docstring const branchName = cmd.argument();
-		if (!branchName.empty())
-			enable = buffer_.params().branchlist().find(branchName);
-		flag.enabled(enable);
-		break;
-	}
-
 	default:
 		flag.enabled(false);
 	}
 
-	return flag;
+	return false;
 }
 
 
@@ -1394,12 +1381,6 @@
 		break;
 	}
 
-	case LFUN_BRANCH_ACTIVATE:
-	case LFUN_BRANCH_DEACTIVATE:
-		buffer_.dispatch(cmd);
-		processUpdateFlags(Update::Force);
-		break;
-
 	default:
 		return false;
 	}
Index: src/LyXFunc.cpp
===================================================================
--- src/LyXFunc.cpp	(revision 24983)
+++ src/LyXFunc.cpp	(working copy)
@@ -444,11 +444,6 @@
 			flag.setOnOff(true);
 		break;
 
-	case LFUN_BUFFER_EXPORT:
-		enable = cmd.argument() == "custom"
-			|| buf->isExportable(to_utf8(cmd.argument()));
-		break;
-
 	case LFUN_BUFFER_CHKTEX:
 		enable = buf->isLatex() && !lyxrc.chktex_command.empty();
 		break;
@@ -613,14 +608,22 @@
 		if (lyx_view_->getStatus(cmd, flag))
 			break;
 
-		// If we have a BufferView, try cursor position and
-		// then the BufferView.
+		// If we do not have a BufferView, then other functions are disabled
 		if (!view()) {
 			enable = false;
 			break;
 		}
-		if (!getLocalStatus(view()->cursor(), cmd, flag))
-			flag = view()->getStatus(cmd);
+
+		// Check if the cursor knows what to do...
+		if (getLocalStatus(view()->cursor(), cmd, flag))
+			break;
+
+		// ...else the BufferView...
+		if (view()->getStatus(cmd, flag))
+			break;
+
+		// ...else try the Buffer
+		view()->buffer().getStatus(cmd, flag);
 	}
 
 	if (!enable)
@@ -674,15 +677,6 @@
 
 namespace {
 
-void showPrintError(string const & name)
-{
-	docstring str = bformat(_("Could not print the document %1$s.\n"
-					    "Check that your printer is set up correctly."),
-			     makeDisplayPath(name, 50));
-	Alert::error(_("Print document failed"), str);
-}
-
-
 bool loadLayoutFile(string const & name, string const & buf_path)
 {
 	if (!LayoutFileList::get().haveClass(name)) {
@@ -886,113 +880,6 @@
 			break;
 		}
 
-		case LFUN_BUFFER_PRINT: {
-			LASSERT(lyx_view_ && lyx_view_->buffer(), /**/);
-			// FIXME: cmd.getArg() might fail if one of the arguments
-			// contains double quotes
-			string target = cmd.getArg(0);
-			string target_name = cmd.getArg(1);
-			string command = cmd.getArg(2);
-
-			if (target.empty()
-			    || target_name.empty()
-			    || command.empty()) {
-				lyxerr << "Unable to parse \""
-				       << argument << '"' << endl;
-				break;
-			}
-			if (target != "printer" && target != "file") {
-				lyxerr << "Unrecognized target \""
-				       << target << '"' << endl;
-				break;
-			}
-
-			Buffer * buffer = lyx_view_->buffer();
-
-			if (!buffer->doExport("dvi", true)) {
-				showPrintError(buffer->absFileName());
-				break;
-			}
-
-			// Push directory path.
-			string const path = buffer->temppath();
-			// Prevent the compiler from optimizing away p
-			FileName pp(path);
-			PathChanger p(pp);
-
-			// there are three cases here:
-			// 1. we print to a file
-			// 2. we print directly to a printer
-			// 3. we print using a spool command (print to file first)
-			Systemcall one;
-			int res = 0;
-			string const dviname =
-				changeExtension(buffer->latexName(true), "dvi");
-
-			if (target == "printer") {
-				if (!lyxrc.print_spool_command.empty()) {
-					// case 3: print using a spool
-					string const psname =
-						changeExtension(dviname,".ps");
-					command += ' ' + lyxrc.print_to_file
-						+ quoteName(psname)
-						+ ' '
-						+ quoteName(dviname);
-
-					string command2 =
-						lyxrc.print_spool_command + ' ';
-					if (target_name != "default") {
-						command2 += lyxrc.print_spool_printerprefix
-							+ target_name
-							+ ' ';
-					}
-					command2 += quoteName(psname);
-					// First run dvips.
-					// If successful, then spool command
-					res = one.startscript(
-						Systemcall::Wait,
-						command);
-
-					if (res == 0)
-						res = one.startscript(
-							Systemcall::DontWait,
-							command2);
-				} else {
-					// case 2: print directly to a printer
-					if (target_name != "default")
-						command += ' ' + lyxrc.print_to_printer + target_name + ' ';
-					res = one.startscript(
-						Systemcall::DontWait,
-						command + quoteName(dviname));
-				}
-
-			} else {
-				// case 1: print to a file
-				FileName const filename(makeAbsPath(target_name,
-							lyx_view_->buffer()->filePath()));
-				FileName const dvifile(makeAbsPath(dviname, path));
-				if (filename.exists()) {
-					docstring text = bformat(
-						_("The file %1$s already exists.\n\n"
-						  "Do you want to overwrite that file?"),
-						makeDisplayPath(filename.absFilename()));
-					if (Alert::prompt(_("Overwrite file?"),
-					    text, 0, 1, _("&Overwrite"), _("&Cancel")) != 0)
-						break;
-				}
-				command += ' ' + lyxrc.print_to_file
-					+ quoteName(filename.toFilesystemEncoding())
-					+ ' '
-					+ quoteName(dvifile.toFilesystemEncoding());
-				res = one.startscript(Systemcall::DontWait,
-						      command);
-			}
-
-			if (res != 0)
-				showPrintError(buffer->absFileName());
-			break;
-		}
-
 		// FIXME: There is need for a command-line import.
 		/*
 		case LFUN_BUFFER_IMPORT:
@@ -1614,6 +1501,10 @@
 				break;
 			}
 
+			// OK, so try the Buffer itself
+			if (view()->buffer().dispatch(cmd, updateFlags))
+				break;
+
 			// Let the current Cursor dispatch its own actions.
 			Cursor old = view()->cursor();
 			view()->cursor().getPos(cursorPosBeforeDispatchX_,
Index: src/LyX.cpp
===================================================================
--- src/LyX.cpp	(revision 24983)
+++ src/LyX.cpp	(working copy)
@@ -395,13 +395,11 @@
 			Buffer * buf = *I;
 			if (buf != buf->masterBuffer())
 				continue;
-			bool success = false;
+			Update::flags uflags; // needed but not used
 			vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
 			vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
-			for (; bcit != bcend; bcit++) {
-				buf->dispatch(*bcit, &success);
-				final_success |= success;
-			}
+			for (; bcit != bcend; bcit++)
+				final_success |= buf->dispatch(*bcit, uflags);
 		}
 		prepareExit();
 		return !final_success;
@@ -1058,6 +1056,7 @@
 		  "\t-i [--import] fmt file.xxx\n"
 		  "                  where fmt is the import format of choice\n"
 		  "                  and file.xxx is the file to be imported.\n"
+		  "\t-nogui          execute commands and exit\n"
 		  "\t-version        summarize version and build info\n"
 			       "Check the LyX man page for more details.")) << endl;
 	exit(0);
@@ -1156,6 +1155,13 @@
 }
 
 
+int parse_nogui(string const & arg1, string const &, string &) 
+{
+	use_gui = false;
+	return 0;
+}
+
+
 } // namespace anon
 
 
@@ -1177,6 +1183,7 @@
 	cmdmap["-i"] = parse_import;
 	cmdmap["--import"] = parse_import;
 	cmdmap["-geometry"] = parse_geometry;
+	cmdmap["-nogui"] = parse_nogui;
 
 	for (int i = 1; i < argc; ++i) {
 		map<string, cmd_helper>::const_iterator it
Index: RELEASE-NOTES
===================================================================
--- RELEASE-NOTES	(revision 24988)
+++ RELEASE-NOTES	(working copy)
@@ -144,11 +144,6 @@
 - LFUN_BRANCH_ACTIVATE, "branch-activate"
 
 - LFUN_BRANCH_DEACTIVATE, "branch-deactivate" 
-  These can be used in export mode to turn branches on and off. 
-  Thus, something like:
-    lyx -e pdf2 -x "branch-activate answers" finalexam.lyx
-  could be used to export a pdf with the answers branch included, without 
-  one's having to open LyX and activate the branch manually.
 
 The following new LyX functions have been removed:
 
@@ -177,6 +172,20 @@
 
 For the detailed description of LyX functions look into doxygen documentation.
 
+There have been some changes to the LyX command line. First, the "-x" option
+may now be given more than once. If so, then the commands given will be 
+executed in the order given. Also, "-x" may be given with "-e", which will 
+cause the commands given to be executed before the file is exported. Thus, 
+something like:
+    lyx -e pdf2 -x "branch-activate answers" finalexam.lyx
+could be used to export a pdf with the answers branch included, without one's
+having to open LyX and activate the branch manually. Second, there is a new 
+option "-nogui" that causes LyX to run the given commands without opening a
+GUI window. Thus, something like:
+    lyx -nogui -x "buffer-print printer default dvips" myfile.lyx
+will cause LyX to print myfile.lyx to the default printer, using dvips and 
+the default print settings (which, of course, have to have been configured 
+already).
 
 Known issues in version 1.6.0
 ------------------------------------

Reply via email to