The Buffer::dispatch() patch.

This patch enhances the Buffer::dispatch() method to make it a full-fledged
member of the dispatch sequence. The most immediate payoff is that LFUNs that
are handled in Buffer::dispatch() can be used from the command line.

We make better use of the DispatchResult object and return error information 
through it, rather than using return values. (This was JMarc's suggestion.) We 
also introduce a  corresponding Buffer::getStatus() method, and modify 
BufferView::getStatus() to return a flag indicating whether a decision has been 
made, as is already done in some other cases.

Finally, some LFUNs are moved to Buffer::dispatch(), including LFUN_BUFFER_PRINT.



git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@29125 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Richard Heck 2009-04-06 12:12:06 +00:00
parent 14845fcaec
commit 4daf7b5dfc
7 changed files with 235 additions and 169 deletions

View File

@ -24,12 +24,14 @@
#include "Chktex.h"
#include "Converter.h"
#include "Counters.h"
#include "DispatchResult.h"
#include "DocIterator.h"
#include "Encoding.h"
#include "ErrorList.h"
#include "Exporter.h"
#include "Format.h"
#include "FuncRequest.h"
#include "FuncStatus.h"
#include "InsetIterator.h"
#include "InsetList.h"
#include "Language.h"
@ -94,6 +96,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"
@ -126,6 +129,14 @@ int const LYX_FORMAT = 349; // jspitzm: initial XeTeX support
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 BufferSet : public std::set<Buffer const *> {};
@ -1506,21 +1517,61 @@ void Buffer::markDepClean(string const & name)
}
bool Buffer::dispatch(string const & command, bool * result)
bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag)
{
switch (cmd.action) {
case LFUN_BUFFER_EXPORT: {
docstring const arg = cmd.argument();
bool enable = arg == "custom" || isExportable(to_utf8(arg));
if (!enable)
flag.message(bformat(
_("Don't know how to export to format: %1$s"), arg));
flag.setEnabled(enable);
break;
}
case LFUN_BRANCH_ACTIVATE:
case LFUN_BRANCH_DEACTIVATE: {
BranchList const & branchList = params().branchlist();
docstring const branchName = cmd.argument();
flag.setEnabled(!branchName.empty()
&& branchList.find(branchName));
break;
}
case LFUN_BUFFER_PRINT:
// if no Buffer is present, then of course we won't be called!
flag.setEnabled(true);
break;
default:
return false;
}
return true;
}
void Buffer::dispatch(string const & command, DispatchResult & result)
{
return dispatch(lyxaction.lookupFunc(command), result);
}
bool Buffer::dispatch(FuncRequest const & func, bool * result)
// NOTE We can end up here even if we have no GUI, because we are called
// by LyX::exec to handled command-line requests. So we may need to check
// whether we have a GUI or not. The boolean use_gui holds this information.
void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr)
{
// We'll set this back to false if need be.
bool dispatched = true;
switch (func.action) {
case LFUN_BUFFER_EXPORT: {
bool const tmp = doExport(to_utf8(func.argument()), false);
if (result)
*result = tmp;
bool success = doExport(to_utf8(func.argument()), false);
dr.setError(success);
if (!success)
dr.setMessage(bformat(_("Error exporting to format: %1$s."),
func.argument()));
break;
}
@ -1534,18 +1585,139 @@ bool Buffer::dispatch(FuncRequest const & func, bool * result)
break;
}
Branch * branch = branchList.find(branchName);
if (!branch)
if (!branch) {
LYXERR0("Branch " << branchName << " does not exist.");
else
dr.setError(true);
docstring const msg =
bformat(_("Branch \%1$s\" does not exist."), branchName);
dr.setMessage(msg);
} else {
branch->setSelected(func.action == LFUN_BRANCH_ACTIVATE);
if (result)
*result = true;
dr.setError(false);
dr.update(Update::Force);
}
break;
}
case LFUN_BUFFER_PRINT: {
// we'll assume there's a problem until we succeed
dr.setError(true);
string target = func.getArg(0);
string target_name = func.getArg(1);
string command = func.getArg(2);
if (target.empty()
|| target_name.empty()
|| command.empty()) {
LYXERR0("Unable to parse " << func.argument());
docstring const msg =
bformat(_("Unable to parse \"%1$s\""), func.argument());
dr.setMessage(msg);
break;
}
if (target != "printer" && target != "file") {
LYXERR0("Unrecognized target \"" << target << '"');
docstring const msg =
bformat(_("Unrecognized target \"%1$s\""), from_utf8(target));
dr.setMessage(msg);
break;
}
if (!doExport("dvi", true)) {
showPrintError(absFileName());
dr.setMessage(_("Error exporting to DVI."));
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)
dr.setError(false);
else {
dr.setMessage(_("Error running external commands."));
showPrintError(absFileName());
}
break;
}
default:
dispatched = false;
break;
}
return dispatched;
dr.dispatched(dispatched);
}

View File

@ -12,6 +12,8 @@
#ifndef BUFFER_H
#define BUFFER_H
#include "update_flags.h"
#include "insets/InsetCode.h"
#include "support/strfwd.h"
@ -27,11 +29,13 @@ namespace lyx {
class BiblioInfo;
class BufferParams;
class BufferSet;
class DispatchResult;
class DocIterator;
class docstring_list;
class ErrorItem;
class ErrorList;
class FuncRequest;
class FuncStatus;
class Inset;
class InsetRef;
class InsetLabel;
@ -125,12 +129,16 @@ public:
~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.
*/
bool dispatch(std::string const & command, bool * result = 0);
void dispatch(std::string const & command, DispatchResult & result);
/// Maybe we know the function already by number...
bool dispatch(FuncRequest const & func, bool * result = 0);
void dispatch(FuncRequest const & func, DispatchResult & result);
/// Can this function be exectued?
/// \return true if we made a decision
bool getStatus(FuncRequest const & cmd, FuncStatus & flag);
/// read a new document from a string
bool readString(std::string const &);

View File

@ -895,10 +895,8 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool recenter)
}
FuncStatus BufferView::getStatus(FuncRequest const & cmd)
bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
{
FuncStatus flag;
Cursor & cur = d->cursor_;
switch (cmd.action) {
@ -916,6 +914,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd)
// FIXME: Actually, these LFUNS should be moved to Text
flag.setEnabled(cur.inTexted());
break;
case LFUN_FONT_STATE:
case LFUN_LABEL_INSERT:
case LFUN_INFO_INSERT:
@ -1049,29 +1048,18 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd)
}
case LFUN_DIALOG_SHOW_NEW_INSET:
if (cur.inset().lyxCode() == CAPTION_CODE)
return cur.inset().getStatus(cur, cmd, flag);
flag.setEnabled(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;
}
break;
case LFUN_BRANCH_ACTIVATE:
case LFUN_BRANCH_DEACTIVATE: {
BranchList const & branchList = buffer_.params().branchlist();
docstring const branchName = cmd.argument();
flag.setEnabled(!branchName.empty()
&& branchList.find(branchName));
break;
}
default:
flag.setEnabled(false);
return false;
}
return flag;
return true;
}
@ -1506,14 +1494,6 @@ bool BufferView::dispatch(FuncRequest const & cmd)
break;
}
case LFUN_BRANCH_ACTIVATE:
case LFUN_BRANCH_DEACTIVATE:
if (cmd.argument().empty())
return false;
buffer_.dispatch(cmd);
processUpdateFlags(Update::Force);
break;
// This could be rewriten using some command like forall <insetname> <command>
// once the insets refactoring is done.
case LFUN_NOTES_MUTATE: {

View File

@ -195,8 +195,8 @@ public:
/// 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);

View File

@ -15,6 +15,8 @@
#include "update_flags.h"
#include "support/docstring.h"
namespace lyx {
/// Maybe this can go entirely
@ -24,19 +26,31 @@ public:
DispatchResult() : dispatched_(false), update_(Update::None) {}
///
DispatchResult(bool disp, Update::flags f) : dispatched_(disp), update_(f) {}
//
///
bool dispatched() const { return dispatched_; }
///
void dispatched(bool disp) { dispatched_ = disp; }
///
bool error() const { return error_; }
///
void setError(bool e) { error_ = e; }
///
docstring message() { return message_; }
///
void setMessage(docstring m) { message_ = m; }
///
Update::flags update() const { return update_; }
///
void update(Update::flags f) { update_ = f; }
private:
/// was the event fully dispatched?
bool dispatched_;
/// was there an error?
bool error_;
/// do we need to redraw the screen afterwards?
Update::flags update_;
///
docstring message_;
};

View File

@ -311,13 +311,13 @@ int LyX::exec(int & argc, char * argv[])
Buffer * buf = *I;
if (buf != buf->masterBuffer())
continue;
bool success = false;
vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
DispatchResult dr;
for (; bcit != bcend; bcit++) {
LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
buf->dispatch(*bcit, &success);
final_success |= success;
buf->dispatch(*bcit, dr);
final_success |= !dr.error();
}
}
prepareExit();

View File

@ -457,11 +457,6 @@ FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
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;
@ -632,7 +627,6 @@ FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
case LFUN_KEYMAP_TOGGLE:
case LFUN_REPEAT:
case LFUN_BUFFER_EXPORT_CUSTOM:
case LFUN_BUFFER_PRINT:
case LFUN_PREFERENCES_SAVE:
case LFUN_MESSAGE:
case LFUN_INSET_EDIT:
@ -666,8 +660,7 @@ FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
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;
@ -679,8 +672,13 @@ FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
&& inset && inset->getStatus(view()->cursor(), cmd, flag))
break;
if (!getLocalStatus(view()->cursor(), cmd, flag))
flag = view()->getStatus(cmd);
bool decided = getLocalStatus(view()->cursor(), cmd, flag);
if (!decided)
// try the BufferView
decided = view()->getStatus(cmd, flag);
if (!decided)
// try the Buffer
view()->buffer().getStatus(cmd, flag);
}
if (!enable)
@ -743,15 +741,6 @@ bool LyXFunc::ensureBufferClean(BufferView * bv)
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)) {
@ -956,111 +945,6 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
break;
}
case LFUN_BUFFER_PRINT: {
LASSERT(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;
}
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,
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:
@ -1725,6 +1609,14 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
break;
}
// OK, so try the Buffer itself
DispatchResult dr;
view()->buffer().dispatch(cmd, dr);
if (dr.dispatched()) {
updateFlags = dr.update();
break;
}
// Is this a function that acts on inset at point?
Inset * inset = view()->cursor().nextInset();
if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)