#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"
#include "ParagraphParameters.h"
#include "ParIterator.h"
#include "PDFOptions.h"
+#include "SpellChecker.h"
#include "sgml.h"
#include "TexRow.h"
#include "TexStream.h"
#include "Undo.h"
#include "VCBackend.h"
#include "version.h"
+#include "WordLangTuple.h"
#include "WordList.h"
#include "insets/InsetBibitem.h"
#include "support/lassert.h"
#include "support/convert.h"
#include "support/debug.h"
+#include "support/docstring_list.h"
#include "support/ExceptionMessage.h"
#include "support/FileName.h"
#include "support/FileNameList.h"
#include "support/os.h"
#include "support/Package.h"
#include "support/Path.h"
+#include "support/Systemcall.h"
#include "support/textutils.h"
#include "support/types.h"
// Do not remove the comment below, so we get merge conflict in
// independent branches. Instead add your own.
-int const LYX_FORMAT = 348; // uwestoehr: add support for \*phantom
+int const LYX_FORMAT = 351; // uwestoehr: support for page background color
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 *> {};
LyXVC lyxvc;
FileName temppath;
mutable TexRow texrow;
- Buffer const * parent_buffer;
/// need to regenerate .tex?
DepClean dep_clean;
/// our Text that should be wrapped in an InsetText
InsetText * inset;
+
+ /// This is here to force the test to be done whenever parent_buffer
+ /// is accessed.
+ Buffer const * parent() const {
+ // if parent_buffer is not loaded, then it has been unloaded,
+ // which means that parent_buffer is an invalid pointer. So we
+ // set it to null in that case.
+ if (!theBufferList().isLoaded(parent_buffer))
+ parent_buffer = 0;
+ return parent_buffer;
+ }
+ ///
+ void setParent(Buffer const * pb) { parent_buffer = pb; }
+private:
+ /// So we can force access via the accessors.
+ mutable Buffer const * parent_buffer;
};
Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
- : parent_buffer(0), lyx_clean(true), bak_clean(true), unnamed(false),
+ : lyx_clean(true), bak_clean(true), unnamed(false),
read_only(readonly_), filename(file), file_fully_loaded(false),
toc_backend(&parent), macro_lock(false), timestamp_(0),
- checksum_(0), wa_(0), undo_(parent), bibinfoCacheValid_(false)
+ checksum_(0), wa_(0), undo_(parent), bibinfoCacheValid_(false),
+ parent_buffer(0)
{
temppath = createBufferTmpDir();
lyxvc.setBuffer(&parent);
Buffer * master =
checkAndLoadLyXFile(master_file, true);
if (master) {
+ // necessary e.g. after a reload
+ // to re-register the child (bug 5873)
+ // FIXME: clean up updateMacros (here, only
+ // child registering is needed).
+ master->updateMacros();
// set master as master buffer, but only
// if we are a real child
if (master->isChild(this))
else if (master->isFullyLoaded())
LYXERR0("The master '"
<< params().master
- << "' assigned to this document '"
+ << "' assigned to this document ("
<< absFileName()
- << "' does not include "
+ << ") does not include "
"this document. Ignoring the master assignment.");
}
}
return false;
}
- removeAutosaveFile(d->filename.absFilename());
+ removeAutosaveFile();
saveCheckSum(d->filename);
message(str + _(" done."));
bool Buffer::makeLaTeXFile(FileName const & fname,
string const & original_path,
- OutputParams const & runparams,
+ OutputParams const & runparams_in,
bool output_preamble, bool output_body) const
{
+ OutputParams runparams = runparams_in;
+ if (params().useXetex)
+ runparams.flavor = OutputParams::XETEX;
+
string const encoding = runparams.encoding->iconvName();
LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "...");
// This happens for example if only a child document is printed.
Buffer const * save_parent = 0;
if (output_preamble) {
- save_parent = d->parent_buffer;
- d->parent_buffer = 0;
+ save_parent = d->parent();
+ d->setParent(0);
}
// the real stuff
// Restore the parenthood if needed
if (output_preamble)
- d->parent_buffer = save_parent;
+ d->setParent(save_parent);
// add this just in case after all the paragraphs
os << endl;
void Buffer::getLabelList(vector<docstring> & list) const
{
// If this is a child document, use the parent's list instead.
- if (d->parent_buffer) {
- d->parent_buffer->getLabelList(list);
+ Buffer const * const pbuf = d->parent();
+ if (pbuf) {
+ pbuf->getLabelList(list);
return;
}
void Buffer::updateBibfilesCache(UpdateScope scope) const
{
// If this is a child document, use the parent's cache instead.
- if (d->parent_buffer && scope != UpdateChildOnly) {
- d->parent_buffer->updateBibfilesCache();
+ Buffer const * const pbuf = d->parent();
+ if (pbuf && scope != UpdateChildOnly) {
+ pbuf->updateBibfilesCache();
return;
}
support::FileNameList const & Buffer::getBibfilesCache(UpdateScope scope) const
{
// If this is a child document, use the parent's cache instead.
- if (d->parent_buffer && scope != UpdateChildOnly)
- return d->parent_buffer->getBibfilesCache();
+ Buffer const * const pbuf = d->parent();
+ if (pbuf && scope != UpdateChildOnly)
+ return pbuf->getBibfilesCache();
// We update the cache when first used instead of at loading time.
if (d->bibfilesCache_.empty())
}
-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;
}
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);
}
void Buffer::setParent(Buffer const * buffer)
{
// Avoids recursive include.
- d->parent_buffer = buffer == this ? 0 : buffer;
+ d->setParent(buffer == this ? 0 : buffer);
updateMacros();
}
Buffer const * Buffer::parent() const
{
- return d->parent_buffer;
+ return d->parent();
}
Buffer const * Buffer::masterBuffer() const
{
- if (!d->parent_buffer)
+ Buffer const * const pbuf = d->parent();
+ if (!pbuf)
return this;
- return d->parent_buffer->masterBuffer();
+ return pbuf->masterBuffer();
}
return data;
// If there is a master buffer, query that
- if (d->parent_buffer) {
+ Buffer const * const pbuf = d->parent();
+ if (pbuf) {
d->macro_lock = true;
- MacroData const * macro = d->parent_buffer->getMacro(
+ MacroData const * macro = pbuf->getMacro(
name, *this, false);
d->macro_lock = false;
if (macro)
it->first->listMacroNames(macros);
// call parent
- if (d->parent_buffer)
- d->parent_buffer->listMacroNames(macros);
+ Buffer const * const pbuf = d->parent();
+ if (pbuf)
+ pbuf->listMacroNames(macros);
d->macro_lock = false;
}
void Buffer::listParentMacros(MacroSet & macros, LaTeXFeatures & features) const
{
- if (!d->parent_buffer)
+ Buffer const * const pbuf = d->parent();
+ if (!pbuf)
return;
MacroNameSet names;
- d->parent_buffer->listMacroNames(names);
+ pbuf->listMacroNames(names);
// resolve macros
MacroNameSet::iterator it = names.begin();
for (; it != end; ++it) {
// defined?
MacroData const * data =
- d->parent_buffer->getMacro(*it, *this, false);
+ pbuf->getMacro(*it, *this, false);
if (data) {
macros.insert(data);
Buffer::References & Buffer::references(docstring const & label)
{
- if (d->parent_buffer)
+ if (d->parent())
return const_cast<Buffer *>(masterBuffer())->references(label);
RefCache::iterator it = d->ref_cache_.find(label);
void Buffer::clearReferenceCache() const
{
- if (!d->parent_buffer)
+ if (!d->parent())
d->ref_cache_.clear();
}
{
OutputParams runparams(¶ms().encoding());
runparams.nice = true;
- runparams.flavor = OutputParams::LATEX;
+ runparams.flavor = params().useXetex ?
+ OutputParams::XETEX : OutputParams::LATEX;
runparams.linelen = lyxrc.plaintext_linelen;
// No side effect of file copying and image conversion
runparams.dryrun = true;
} // namespace anon
+FileName Buffer::getAutosaveFilename() const
+{
+ string const fpath = isUnnamed() ? lyxrc.document_path : filePath();
+ string const fname = "#" + d->filename.onlyFileName() + "#";
+ return makeAbsPath(fname, fpath);
+}
+
+
+void Buffer::removeAutosaveFile() const
+{
+ FileName const f = getAutosaveFilename();
+ if (f.exists())
+ f.removeFile();
+}
+
+
// Perfect target for a thread...
void Buffer::autoSave() const
{
// emit message signal.
message(_("Autosaving current document..."));
-
- // create autosave filename
- string fname = filePath();
- fname += '#';
- fname += d->filename.onlyFileName();
- fname += '#';
-
- AutoSaveBuffer autosave(*this, FileName(fname));
+ AutoSaveBuffer autosave(*this, getAutosaveFilename());
autosave.start();
markBakClean();
return "docbook";
if (isLiterate())
return "literate";
+ if (params().useXetex)
+ return "xetex";
if (params().encoding().package() == Encoding::japanese)
return "platex";
return "latex";
}
+string Buffer::getDefaultOutputFormat() const
+{
+ if (!params().defaultOutputFormat.empty()
+ && params().defaultOutputFormat != "default")
+ return params().defaultOutputFormat;
+ typedef vector<Format const *> Formats;
+ Formats formats = exportableFormats(true);
+ if (isDocBook()
+ || isLiterate()
+ || params().useXetex
+ || params().encoding().package() == Encoding::japanese) {
+ if (formats.empty())
+ return string();
+ // return the first we find
+ return formats.front()->name();
+ }
+ return lyxrc.default_view_format;
+}
+
+
+
bool Buffer::doExport(string const & format, bool put_in_tempdir,
string & result_file) const
{
bool inword = false;
bool ignoreword = false;
string lang_code;
+ // Go backward a bit if needed in order to return the word currently
+ // pointed by 'from'.
+ while (from && from.pos() && isLetter(from))
+ from.backwardPos();
+ // OK, we start from here.
to = from;
-
while (to.depth()) {
if (isLetter(to)) {
if (!inword) {
}
to.forwardPos();
}
-
+ from = to;
+ word.clear();
return false;
}
+
+int Buffer::spellCheck(DocIterator & from, DocIterator & to,
+ WordLangTuple & word_lang, docstring_list & suggestions) const
+{
+ int progress = 0;
+ SpellChecker::Result res = SpellChecker::OK;
+ SpellChecker * speller = theSpellChecker();
+ suggestions.clear();
+ docstring word;
+ while (nextWord(from, to, word)) {
+ ++progress;
+ string lang_code = lyxrc.spellchecker_use_alt_lang
+ ? lyxrc.spellchecker_alt_lang
+ : from.paragraph().getFontSettings(params(), from.pos()).language()->code();
+ WordLangTuple wl(word, lang_code);
+ res = speller->check(wl);
+ // ... just bail out if the spellchecker reports an error.
+ if (!speller->error().empty()) {
+ throw ExceptionMessage(WarningException,
+ _("The spellchecker has failed."), speller->error());
+ }
+ if (res != SpellChecker::OK && res != SpellChecker::IGNORED_WORD) {
+ word_lang = wl;
+ break;
+ }
+ from = to;
+ }
+ while (!(word = speller->nextMiss()).empty())
+ suggestions.push_back(word);
+ return progress;
+}
+
} // namespace lyx