X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBuffer.cpp;h=aa6d936aec8e6fa00b4c92a4e71c386280e21078;hb=94f0968e9212f24997cfee0d17c6f060210edbc6;hp=07a82c03714fd6f75086387facdaee941cb73160;hpb=33ee5c9ae8c4206e9028cb228c7f2d92d0b3ed1b;p=lyx.git diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 07a82c0371..aa6d936aec 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -77,12 +77,12 @@ #include "mathed/MathMacroTemplate.h" #include "mathed/MathSupport.h" +#include "graphics/PreviewLoader.h" + #include "frontends/alert.h" #include "frontends/Delegates.h" #include "frontends/WorkAreaManager.h" -#include "graphics/Previews.h" - #include "support/lassert.h" #include "support/convert.h" #include "support/debug.h" @@ -117,6 +117,7 @@ using namespace std; using namespace lyx::support; +using namespace lyx::graphics; namespace lyx { @@ -125,9 +126,7 @@ namespace os = support::os; namespace { -// Do not remove the comment below, so we get merge conflict in -// independent branches. Instead add your own. -int const LYX_FORMAT = 413; // rgh: html_css_as_file +int const LYX_FORMAT = LYX_FORMAT_LYX; typedef map DepClean; typedef map > RefCache; @@ -150,6 +149,7 @@ public: ~Impl() { + delete preview_loader_; if (wa_) { wa_->closeAll(); delete wa_; @@ -267,6 +267,9 @@ public: /// our Text that should be wrapped in an InsetText InsetText * inset; + /// + PreviewLoader * preview_loader_; + /// This is here to force the test to be done whenever parent_buffer /// is accessed. Buffer const * parent() const { @@ -331,10 +334,11 @@ Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_, Buffer const * cloned_buffer) : owner_(owner), lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_), filename(file), file_fully_loaded(false), - toc_backend(owner), macro_lock(false), timestamp_(0), - checksum_(0), wa_(0), gui_(0), undo_(*owner), bibinfo_cache_valid_(false), - bibfile_cache_valid_(false), cite_labels_valid_(false), - cloned_buffer_(cloned_buffer), doing_export(false), parent_buffer(0) + toc_backend(owner), macro_lock(false), timestamp_(0), checksum_(0), + wa_(0), gui_(0), undo_(*owner), bibinfo_cache_valid_(false), + bibfile_cache_valid_(false), cite_labels_valid_(false), + preview_loader_(0), cloned_buffer_(cloned_buffer), + doing_export(false), parent_buffer(0) { if (!cloned_buffer_) { temppath = createBufferTmpDir(); @@ -352,6 +356,7 @@ Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_, bibfile_cache_valid_ = cloned_buffer_->d->bibfile_cache_valid_; bibfile_status_ = cloned_buffer_->d->bibfile_status_; cite_labels_valid_ = cloned_buffer_->d->cite_labels_valid_; + unnamed = cloned_buffer_->d->unnamed; } @@ -395,7 +400,7 @@ Buffer::~Buffer() Impl::BufferPositionMap::iterator end = d->children_positions.end(); for (; it != end; ++it) { Buffer * child = const_cast(it->first); - if (d->cloned_buffer_) + if (isClone()) delete child; // The child buffer might have been closed already. else if (theBufferList().isLoaded(child)) @@ -427,29 +432,50 @@ Buffer::~Buffer() Buffer * Buffer::clone() const { + BufferMap bufmap; + masterBuffer()->clone(bufmap); + BufferMap::iterator it = bufmap.find(this); + LASSERT(it != bufmap.end(), return 0); + return it->second; +} + + +void Buffer::clone(BufferMap & bufmap) const +{ + // have we already been cloned? + if (bufmap.find(this) != bufmap.end()) + return; + Buffer * buffer_clone = new Buffer(fileName().absFileName(), false, this); + bufmap[this] = buffer_clone; buffer_clone->d->macro_lock = true; buffer_clone->d->children_positions.clear(); // FIXME (Abdel 09/01/2010): this is too complicated. The whole children_positions and // math macro caches need to be rethought and simplified. // I am not sure wether we should handle Buffer cloning here or in BufferList. // Right now BufferList knows nothing about buffer clones. - Impl::BufferPositionMap::iterator it = d->children_positions.begin(); - Impl::BufferPositionMap::iterator end = d->children_positions.end(); + Impl::PositionScopeBufferMap::iterator it = d->position_to_children.begin(); + Impl::PositionScopeBufferMap::iterator end = d->position_to_children.end(); for (; it != end; ++it) { - DocIterator dit = it->second.clone(buffer_clone); + DocIterator dit = it->first.clone(buffer_clone); dit.setBuffer(buffer_clone); - Buffer * child = const_cast(it->first); - Buffer * child_clone = child->clone(); + Buffer * child = const_cast(it->second.second); + + child->clone(bufmap); + BufferMap::iterator const bit = bufmap.find(child); + LASSERT(bit != bufmap.end(), continue); + Buffer * child_clone = bit->second; + Inset * inset = dit.nextInset(); LASSERT(inset && inset->lyxCode() == INCLUDE_CODE, continue); InsetInclude * inset_inc = static_cast(inset); inset_inc->setChildBuffer(child_clone); child_clone->d->setParent(buffer_clone); + // FIXME Do we need to do this now, or can we wait until we run updateMacros()? buffer_clone->setChild(dit, child_clone); } buffer_clone->d->macro_lock = false; - return buffer_clone; + return; } @@ -602,7 +628,7 @@ string Buffer::logName(LogType * type) const FileName const bname( addName(path, onlyFileName( changeExtension(filename, - formats.extension(bufferFormat()) + ".out")))); + formats.extension(params().bufferFormat()) + ".out")))); // Also consider the master buffer log file FileName masterfname = fname; @@ -686,10 +712,10 @@ int Buffer::readHeader(Lexer & lex) params().indiceslist().clear(); params().backgroundcolor = lyx::rgbFromHexName("#ffffff"); params().isbackgroundcolor = false; - params().fontcolor = lyx::rgbFromHexName("#000000"); + params().fontcolor = RGBColor(0, 0, 0); params().isfontcolor = false; - params().notefontcolor = lyx::rgbFromHexName("#cccccc"); - params().boxbgcolor = lyx::rgbFromHexName("#ff0000"); + params().notefontcolor = RGBColor(0xCC, 0xCC, 0xCC); + params().boxbgcolor = RGBColor(0xFF, 0, 0); params().html_latex_start.clear(); params().html_latex_end.clear(); params().html_math_img_scale = 1.0; @@ -873,7 +899,12 @@ Buffer::ReadStatus Buffer::readFile(FileName const & fn) { FileName fname(fn); Lexer lex; - lex.setFile(fname); + if (!lex.setFile(fname)) { + Alert::error(_("File Not Found"), + bformat(_("Unable to open file `%1$s'."), + from_utf8(fn.absFileName()))); + return ReadFileNotFound; + } int file_format; ReadStatus const ret_plf = parseLyXFormat(lex, fn, file_format); @@ -920,16 +951,35 @@ void Buffer::setFullyLoaded(bool value) } -void Buffer::updatePreviews() const +PreviewLoader * Buffer::loader() const { - if (graphics::Previews::status() != LyXRC::PREVIEW_OFF) - thePreviews().generateBufferPreviews(*this); + if (!isExporting() && lyxrc.preview == LyXRC::PREVIEW_OFF) + return 0; + if (!d->preview_loader_) + d->preview_loader_ = new PreviewLoader(*this); + return d->preview_loader_; } void Buffer::removePreviews() const { - thePreviews().removeLoader(*this); + delete d->preview_loader_; + d->preview_loader_ = 0; +} + + +void Buffer::updatePreviews() const +{ + PreviewLoader * ploader = loader(); + if (!ploader) + return; + + InsetIterator it = inset_iterator_begin(*d->inset); + InsetIterator const end = inset_iterator_end(*d->inset); + for (; it != end; ++it) + it->addPreview(it, *ploader); + + ploader->startLoading(); } @@ -1251,7 +1301,7 @@ bool Buffer::write(ostream & ofs) const bool Buffer::makeLaTeXFile(FileName const & fname, string const & original_path, OutputParams const & runparams_in, - bool output_preamble, bool output_body) const + OutputWhat output) const { OutputParams runparams = runparams_in; @@ -1261,7 +1311,7 @@ bool Buffer::makeLaTeXFile(FileName const & fname, runparams.encoding = encodings.fromLyXName("utf8-plain"); string const encoding = runparams.encoding->iconvName(); - LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "..."); + LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << ", fname=" << fname.realPath()); ofdocstream ofs; try { ofs.reset(encoding); } @@ -1290,8 +1340,7 @@ bool Buffer::makeLaTeXFile(FileName const & fname, try { os.texrow().reset(); - writeLaTeXSource(os, original_path, - runparams, output_preamble, output_body); + writeLaTeXSource(os, original_path, runparams, output); } catch (EncodingException & e) { odocstringstream ods; @@ -1336,7 +1385,7 @@ bool Buffer::makeLaTeXFile(FileName const & fname, void Buffer::writeLaTeXSource(otexstream & os, string const & original_path, OutputParams const & runparams_in, - bool const output_preamble, bool const output_body) const + OutputWhat output) const { // The child documents, if any, shall be already loaded at this point. @@ -1351,6 +1400,11 @@ void Buffer::writeLaTeXSource(otexstream & os, validate(features); LYXERR(Debug::LATEX, " Buffer validation done."); + bool const output_preamble = + output == FullSource || output == OnlyPreamble; + bool const output_body = + output == FullSource || output == OnlyBody; + // The starting paragraph of the coming rows is the // first paragraph of the document. (Asger) if (output_preamble && runparams.nice) { @@ -1362,12 +1416,13 @@ void Buffer::writeLaTeXSource(otexstream & os, LYXERR(Debug::INFO, "lyx document header finished"); // There are a few differences between nice LaTeX and usual files: - // usual is \batchmode and has a - // special input@path to allow the including of figures - // with either \input or \includegraphics (what figinsets do). - // input@path is set when the actual parameter - // original_path is set. This is done for usual tex-file, but not - // for nice-latex-file. (Matthias 250696) + // usual files have \batchmode and special input@path to allow + // inclusion of figures specified by an explicitly relative path + // (i.e., a path starting with './' or '../') with either \input or + // \includegraphics, as the TEXINPUTS method doesn't work in this case. + // input@path is set when the actual parameter original_path is set. + // This is done for usual tex-file, but not for nice-latex-file. + // (Matthias 250696) // Note that input@path is only needed for something the user does // in the preamble, included .tex files or ERT, files included by // LyX work without it. @@ -1379,7 +1434,7 @@ void Buffer::writeLaTeXSource(otexstream & os, if (!original_path.empty()) { // FIXME UNICODE // We don't know the encoding of inputpath - docstring const inputpath = from_utf8(support::latex_path(original_path)); + docstring const inputpath = from_utf8(original_path); docstring uncodable_glyphs; Encoding const * const enc = runparams.encoding; if (enc) { @@ -1397,18 +1452,41 @@ void Buffer::writeLaTeXSource(otexstream & os, // warn user if we found uncodable glyphs. if (!uncodable_glyphs.empty()) { - frontend::Alert::warning(_("Uncodable character in file path"), - support::bformat(_("The path of your document\n" - "(%1$s)\n" - "contains glyphs that are unknown in the\n" - "current document encoding (namely %2$s).\n" - "This will likely result in incomplete output.\n\n" - "Choose an appropriate document encoding (such as utf8)\n" - "or change the file path name."), inputpath, uncodable_glyphs)); + frontend::Alert::warning( + _("Uncodable character in file path"), + support::bformat( + _("The path of your document\n" + "(%1$s)\n" + "contains glyphs that are unknown " + "in the current document encoding " + "(namely %2$s). This may result in " + "incomplete output, unless " + "TEXINPUTS contains the document " + "directory and you don't use " + "explicitly relative paths (i.e., " + "paths starting with './' or " + "'../') in the preamble or in ERT." + "\n\nIn case of problems, choose " + "an appropriate document encoding\n" + "(such as utf8) or change the " + "file path name."), + inputpath, uncodable_glyphs)); } else { + string docdir = + support::latex_path(original_path); + if (contains(docdir, '#')) { + docdir = subst(docdir, "#", "\\#"); + os << "\\catcode`\\#=11" + "\\def\\#{#}\\catcode`\\#=6\n"; + } + if (contains(docdir, '%')) { + docdir = subst(docdir, "%", "\\%"); + os << "\\catcode`\\%=11" + "\\def\\%{%}\\catcode`\\%=14\n"; + } os << "\\makeatletter\n" << "\\def\\input@path{{" - << inputpath << "/}}\n" + << docdir << "/}}\n" << "\\makeatother\n"; } } @@ -1486,27 +1564,9 @@ void Buffer::writeLaTeXSource(otexstream & os, } -bool Buffer::isLatex() const -{ - return params().documentClass().outputType() == LATEX; -} - - -bool Buffer::isLiterate() const -{ - return params().documentClass().outputType() == LITERATE; -} - - -bool Buffer::isDocBook() const -{ - return params().documentClass().outputType() == DOCBOOK; -} - - void Buffer::makeDocBookFile(FileName const & fname, OutputParams const & runparams, - bool const body_only) const + OutputWhat output) const { LYXERR(Debug::LATEX, "makeDocBookFile..."); @@ -1519,7 +1579,7 @@ void Buffer::makeDocBookFile(FileName const & fname, updateBuffer(); updateMacroInstances(OutputUpdate); - writeDocBookSource(ofs, fname.absFileName(), runparams, body_only); + writeDocBookSource(ofs, fname.absFileName(), runparams, output); ofs.close(); if (ofs.fail()) @@ -1529,7 +1589,7 @@ void Buffer::makeDocBookFile(FileName const & fname, void Buffer::writeDocBookSource(odocstream & os, string const & fname, OutputParams const & runparams, - bool const only_body) const + OutputWhat output) const { LaTeXFeatures features(*this, params(), runparams); validate(features); @@ -1539,7 +1599,12 @@ void Buffer::writeDocBookSource(odocstream & os, string const & fname, DocumentClass const & tclass = params().documentClass(); string const top_element = tclass.latexname(); - if (!only_body) { + bool const output_preamble = + output == FullSource || output == OnlyPreamble; + bool const output_body = + output == FullSource || output == OnlyBody; + + if (output_preamble) { if (runparams.flavor == OutputParams::XML) os << "\n"; @@ -1574,35 +1639,36 @@ void Buffer::writeDocBookSource(odocstream & os, string const & fname, os << ">\n\n"; } - string top = top_element; - top += " lang=\""; - if (runparams.flavor == OutputParams::XML) - top += params().language->code(); - else - top += params().language->code().substr(0, 2); - top += '"'; - - if (!params().options.empty()) { - top += ' '; - top += params().options; + if (output_body) { + string top = top_element; + top += " lang=\""; + if (runparams.flavor == OutputParams::XML) + top += params().language->code(); + else + top += params().language->code().substr(0, 2); + top += '"'; + + if (!params().options.empty()) { + top += ' '; + top += params().options; + } + + os << "\n"; + + params().documentClass().counters().reset(); + + sgml::openTag(os, top); + os << '\n'; + docbookParagraphs(text(), *this, os, runparams); + sgml::closeTag(os, top_element); } - - os << "\n"; - - params().documentClass().counters().reset(); - - sgml::openTag(os, top); - os << '\n'; - docbookParagraphs(text(), *this, os, runparams); - sgml::closeTag(os, top_element); } void Buffer::makeLyXHTMLFile(FileName const & fname, - OutputParams const & runparams, - bool const body_only) const + OutputParams const & runparams) const { LYXERR(Debug::LATEX, "makeLyXHTMLFile..."); @@ -1615,7 +1681,7 @@ void Buffer::makeLyXHTMLFile(FileName const & fname, updateBuffer(UpdateMaster, OutputUpdate); updateMacroInstances(OutputUpdate); - writeLyXHTMLSource(ofs, runparams, body_only); + writeLyXHTMLSource(ofs, runparams, FullSource); ofs.close(); if (ofs.fail()) @@ -1625,13 +1691,18 @@ void Buffer::makeLyXHTMLFile(FileName const & fname, void Buffer::writeLyXHTMLSource(odocstream & os, OutputParams const & runparams, - bool const only_body) const + OutputWhat output) const { LaTeXFeatures features(*this, params(), runparams); validate(features); d->bibinfo_.makeCitationLabels(*this); - if (!only_body) { + bool const output_preamble = + output == FullSource || output == OnlyPreamble; + bool const output_body = + output == FullSource || output == OnlyBody; + + if (output_preamble) { os << "\n" << "\n" // FIXME Language should be set properly. @@ -1643,7 +1714,9 @@ void Buffer::writeLyXHTMLSource(odocstream & os, docstring const & doctitle = features.htmlTitle(); os << "" - << (doctitle.empty() ? from_ascii("LyX Document") : doctitle) + << (doctitle.empty() ? + from_ascii("LyX Document") : + html::htmlize(doctitle, XHTMLStream::ESCAPE_ALL)) << "\n"; os << "\n\n" @@ -1658,14 +1731,34 @@ void Buffer::writeLyXHTMLSource(odocstream & os, << styleinfo << "\n"; } - os << "\n\n"; + + bool const needfg = params().fontcolor != RGBColor(0, 0, 0); + bool const needbg = params().backgroundcolor != RGBColor(0xFF, 0xFF, 0xFF); + if (needfg || needbg) { + os << "\n"; + } + os << "\n"; } - XHTMLStream xs(os); - params().documentClass().counters().reset(); - xhtmlParagraphs(text(), *this, xs, runparams); - if (!only_body) - os << "\n\n"; + if (output_body) { + os << "\n"; + XHTMLStream xs(os); + params().documentClass().counters().reset(); + xhtmlParagraphs(text(), *this, xs, runparams); + os << "\n"; + } + + if (output_preamble) + os << "\n"; } @@ -1917,21 +2010,6 @@ void Buffer::markDepClean(string const & name) } -bool Buffer::isExportableFormat(string const & format) const -{ - typedef vector Formats; - Formats formats; - formats = exportableFormats(true); - Formats::const_iterator fit = formats.begin(); - Formats::const_iterator end = formats.end(); - for (; fit != end ; ++fit) { - if ((*fit)->name() == format) - return true; - } - return false; -} - - bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag) { if (isInternal()) { @@ -1960,7 +2038,15 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag) case LFUN_BUFFER_EXPORT: { docstring const arg = cmd.argument(); - enable = arg == "custom" || isExportable(to_utf8(arg)); + if (arg == "custom") { + enable = true; + break; + } + string format = to_utf8(arg); + size_t pos = format.find(' '); + if (pos != string::npos) + format = format.substr(0, pos); + enable = params().isExportable(format); if (!enable) flag.message(bformat( _("Don't know how to export to format: %1$s"), arg)); @@ -1968,11 +2054,11 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag) } case LFUN_BUFFER_CHKTEX: - enable = isLatex() && !lyxrc.chktex_command.empty(); + enable = params().isLatex() && !lyxrc.chktex_command.empty(); break; case LFUN_BUILD_PROGRAM: - enable = isExportable("program"); + enable = params().isExportable("program"); break; case LFUN_BRANCH_ACTIVATE: @@ -2032,16 +2118,16 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) break; case LFUN_BUFFER_EXPORT: { - bool success = doExport(argument, false, false); - dr.setError(!success); - if (!success) + ExportStatus const status = doExport(argument, false); + dr.setError(status != ExportSuccess); + if (status != ExportSuccess) dr.setMessage(bformat(_("Error exporting to format: %1$s."), func.argument())); break; } case LFUN_BUILD_PROGRAM: - doExport("program", true, false); + doExport("program", true); break; case LFUN_BUFFER_CHKTEX: @@ -2073,7 +2159,7 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) break; } else { - doExport(format_name, true, false, filename); + doExport(format_name, true, filename); } // Substitute $$FName for filename @@ -2083,7 +2169,7 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) // Execute the command in the background Systemcall call; - call.startscript(Systemcall::DontWait, command); + call.startscript(Systemcall::DontWait, command, filePath()); break; } @@ -2220,10 +2306,7 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) break; } - bool const update_unincluded = - params().maintain_unincluded_children - && !params().getIncludedChildren().empty(); - if (!doExport("dvi", true, update_unincluded)) { + if (!doExport("dvi", true)) { showPrintError(absFileName()); dr.setMessage(_("Error exporting to DVI.")); break; @@ -2261,7 +2344,8 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) command2 += quoteName(psname); // First run dvips. // If successful, then spool command - res = one.startscript(Systemcall::Wait, command); + res = one.startscript(Systemcall::Wait, command, + filePath()); if (res == 0) { // If there's no GUI, we have to wait on this command. Otherwise, @@ -2269,7 +2353,8 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) // file, before it can be printed!! Systemcall::Starttype stype = use_gui ? Systemcall::DontWait : Systemcall::Wait; - res = one.startscript(stype, command2); + res = one.startscript(stype, command2, + filePath()); } } else { // case 2: print directly to a printer @@ -2278,7 +2363,8 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) // as above.... Systemcall::Starttype stype = use_gui ? Systemcall::DontWait : Systemcall::Wait; - res = one.startscript(stype, command + quoteName(dviname)); + res = one.startscript(stype, command + + quoteName(dviname), filePath()); } } else { @@ -2301,7 +2387,7 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) // as above.... Systemcall::Starttype stype = use_gui ? Systemcall::DontWait : Systemcall::Wait; - res = one.startscript(stype, command); + res = one.startscript(stype, command, filePath()); } if (res == 0) @@ -2313,18 +2399,6 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) break; } - case LFUN_BUFFER_LANGUAGE: { - Language const * oldL = params().language; - Language const * newL = languages.getLanguage(argument); - if (!newL || oldL == newL) - break; - if (oldL->rightToLeft() == newL->rightToLeft() && !isMultiLingual()) { - changeLanguage(oldL, newL); - dr.forceBufferUpdate(); - } - break; - } - default: dispatched = false; break; @@ -2627,6 +2701,12 @@ ListOfBuffers Buffer::getChildren() const { ListOfBuffers v; collectChildren(v, false); + // Make sure we have not included ourselves. + ListOfBuffers::iterator bit = find(v.begin(), v.end(), this); + if (bit != v.end()) { + LYXERR0("Recursive include detected in `" << fileName() << "'."); + v.erase(bit); + } return v; } @@ -2635,6 +2715,12 @@ ListOfBuffers Buffer::getDescendents() const { ListOfBuffers v; collectChildren(v, true); + // Make sure we have not included ourselves. + ListOfBuffers::iterator bit = find(v.begin(), v.end(), this); + if (bit != v.end()) { + LYXERR0("Recursive include detected in `" << fileName() << "'."); + v.erase(bit); + } return v; } @@ -3102,30 +3188,16 @@ void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to, void Buffer::getSourceCode(odocstream & os, string const format, pit_type par_begin, pit_type par_end, - bool full_source) const + OutputWhat output) const { OutputParams runparams(¶ms().encoding()); runparams.nice = true; - runparams.flavor = getOutputFlavor(format); + runparams.flavor = params().getOutputFlavor(format); runparams.linelen = lyxrc.plaintext_linelen; // No side effect of file copying and image conversion runparams.dryrun = true; - if (full_source) { - os << "% " << _("Preview source code") << "\n\n"; - d->texrow.reset(); - d->texrow.newline(); - d->texrow.newline(); - if (isDocBook()) - writeDocBookSource(os, absFileName(), runparams, false); - else if (runparams.flavor == OutputParams::HTML) - writeLyXHTMLSource(os, runparams, false); - else { - // latex or literate - otexstream ots(os, d->texrow); - writeLaTeXSource(ots, string(), runparams, true, true); - } - } else { + if (output == CurrentParagraph) { runparams.par_begin = par_begin; runparams.par_end = par_end; if (par_begin + 1 == par_end) { @@ -3144,7 +3216,7 @@ void Buffer::getSourceCode(odocstream & os, string const format, texrow.newline(); texrow.newline(); // output paragraphs - if (isDocBook()) + if (params().isDocBook()) docbookParagraphs(text(), *this, os, runparams); else if (runparams.flavor == OutputParams::HTML) { XHTMLStream xs(os); @@ -3154,18 +3226,34 @@ void Buffer::getSourceCode(odocstream & os, string const format, otexstream ots(os, texrow); latexParagraphs(*this, text(), ots, runparams); } + } else { + os << "% "; + if (output == FullSource) + os << _("Preview source code"); + else if (output == OnlyPreamble) + os << _("Preview preamble"); + else if (output == OnlyBody) + os << _("Preview body"); + os << "\n\n"; + d->texrow.reset(); + d->texrow.newline(); + d->texrow.newline(); + if (params().isDocBook()) + writeDocBookSource(os, absFileName(), runparams, output); + else if (runparams.flavor == OutputParams::HTML) + writeLyXHTMLSource(os, runparams, output); + else { + // latex or literate + otexstream ots(os, d->texrow); + writeLaTeXSource(ots, string(), runparams, output); + } } } ErrorList & Buffer::errorList(string const & type) const { - static ErrorList emptyErrorList; - map::iterator it = d->errorLists.find(type); - if (it == d->errorLists.end()) - return emptyErrorList; - - return it->second; + return d->errorLists[type]; } @@ -3389,92 +3477,23 @@ bool Buffer::autoSave() const } -string Buffer::bufferFormat() const -{ - string format = params().documentClass().outputFormat(); - if (format == "latex") { - if (params().useNonTeXFonts) - return "xetex"; - if (params().encoding().package() == Encoding::japanese) - return "platex"; - } - return format; -} - - -string Buffer::getDefaultOutputFormat() const -{ - if (!params().default_output_format.empty() - && params().default_output_format != "default") - return params().default_output_format; - if (isDocBook() - || params().useNonTeXFonts - || params().encoding().package() == Encoding::japanese) { - vector const formats = exportableFormats(true); - if (formats.empty()) - return string(); - // return the first we find - return formats.front()->name(); +// helper class, to guarantee this gets reset properly +class Buffer::MarkAsExporting { +public: + MarkAsExporting(Buffer const * buf) : buf_(buf) + { + LASSERT(buf_, /* */); + buf_->setExportStatus(true); } - return lyxrc.default_view_format; -} - - -OutputParams::FLAVOR Buffer::getOutputFlavor(string const format) const -{ - string const dformat = (format.empty() || format == "default") ? - getDefaultOutputFormat() : format; - DefaultFlavorCache::const_iterator it = - default_flavors_.find(dformat); - - if (it != default_flavors_.end()) - return it->second; - - OutputParams::FLAVOR result = OutputParams::LATEX; - - if (dformat == "xhtml") - result = OutputParams::HTML; - else { - // Try to determine flavor of default output format - vector backs = backends(); - if (find(backs.begin(), backs.end(), dformat) == backs.end()) { - // Get shortest path to format - Graph::EdgePath path; - for (vector::const_iterator it = backs.begin(); - it != backs.end(); ++it) { - Graph::EdgePath p = theConverters().getPath(*it, dformat); - if (!p.empty() && (path.empty() || p.size() < path.size())) { - path = p; - } - } - if (!path.empty()) - result = theConverters().getFlavor(path); - } + ~MarkAsExporting() + { + buf_->setExportStatus(false); } - // cache this flavor - default_flavors_[dformat] = result; - return result; -} +private: + Buffer const * const buf_; +}; -namespace { - // helper class, to guarantee this gets reset properly - class MarkAsExporting { - public: - MarkAsExporting(Buffer const * buf) : buf_(buf) - { - LASSERT(buf_, /* */); - buf_->setExportStatus(true); - } - ~MarkAsExporting() - { - buf_->setExportStatus(false); - } - private: - Buffer const * const buf_; - }; -} - void Buffer::setExportStatus(bool e) const { @@ -3493,16 +3512,53 @@ bool Buffer::isExporting() const } -bool Buffer::doExport(string const & format, bool put_in_tempdir, +Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir) + const +{ + string result_file; + return doExport(target, put_in_tempdir, result_file); +} + +Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir, + string & result_file) const +{ + bool const update_unincluded = + params().maintain_unincluded_children + && !params().getIncludedChildren().empty(); + + // (1) export with all included children (omit \includeonly) + if (update_unincluded) { + ExportStatus const status = + doExport(target, put_in_tempdir, true, result_file); + if (status != ExportSuccess) + return status; + } + // (2) export with included children only + return doExport(target, put_in_tempdir, false, result_file); +} + + +Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir, bool includeall, string & result_file) const { + LYXERR(Debug::FILES, "target=" << target); + OutputParams runparams(¶ms().encoding()); + string format = target; + string dest_filename; + size_t pos = target.find(' '); + if (pos != string::npos) { + dest_filename = target.substr(pos + 1, target.length() - pos - 1); + format = target.substr(0, pos); + runparams.export_folder = FileName(dest_filename).onlyPath().realPath(); + FileName(dest_filename).onlyPath().createPath(); + LYXERR(Debug::FILES, "format=" << format << ", dest_filename=" << dest_filename << ", export_folder=" << runparams.export_folder); + } MarkAsExporting exporting(this); string backend_format; - OutputParams runparams(¶ms().encoding()); runparams.flavor = OutputParams::LATEX; runparams.linelen = lyxrc.plaintext_linelen; runparams.includeall = includeall; - vector backs = backends(); + vector backs = params().backends(); Converters converters = theConverters(); if (find(backs.begin(), backs.end(), format) == backs.end()) { // Get shortest path to format @@ -3524,17 +3580,20 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, _("No information for exporting the format %1$s."), formats.prettyName(format))); } - return false; + return ExportNoPathToFormat; } runparams.flavor = converters.getFlavor(path); } else { backend_format = format; + LYXERR(Debug::FILES, "backend_format=" << backend_format); // FIXME: Don't hardcode format names here, but use a flag if (backend_format == "pdflatex") runparams.flavor = OutputParams::PDFLATEX; else if (backend_format == "luatex") runparams.flavor = OutputParams::LUATEX; + else if (backend_format == "dviluatex") + runparams.flavor = OutputParams::DVILUATEX; else if (backend_format == "xetex") runparams.flavor = OutputParams::XETEX; } @@ -3543,6 +3602,7 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, filename = addName(temppath(), filename); filename = changeExtension(filename, formats.extension(backend_format)); + LYXERR(Debug::FILES, "filename=" << filename); // Plain text backend if (backend_format == "text") { @@ -3570,7 +3630,7 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, } else if (backend_format == "lyx") writeFile(FileName(filename)); // Docbook backend - else if (isDocBook()) { + else if (params().isDocBook()) { runparams.nice = !put_in_tempdir; makeDocBookFile(FileName(filename), runparams); } @@ -3582,13 +3642,13 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, d->cloned_buffer_->d->errorLists["Export"] = d->errorLists["Export"]; } - return false; + return ExportError; } } else if (!lyxrc.tex_allows_spaces && contains(filePath(), ' ')) { Alert::error(_("File name error"), _("The directory path to the document cannot contain spaces.")); - return false; + return ExportTexPathHasSpaces; } else { runparams.nice = false; if (!makeLaTeXFile(FileName(filename), filePath(), runparams)) { @@ -3596,12 +3656,12 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, d->cloned_buffer_->d->errorLists["Export"] = d->errorLists["Export"]; } - return false; + return ExportError; } } string const error_type = (format == "program") - ? "Build" : bufferFormat(); + ? "Build" : params().bufferFormat(); ErrorList & error_list = d->errorLists[error_type]; string const ext = formats.extension(format); FileName const tmp_result_file(changeExtension(filename, ext)); @@ -3640,24 +3700,28 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, // FIXME: There is a possibility of concurrent access to texrow // here from the main GUI thread that should be securized. d->cloned_buffer_->d->texrow = d->texrow; - string const error_type = bufferFormat(); + string const error_type = params().bufferFormat(); d->cloned_buffer_->d->errorLists[error_type] = d->errorLists[error_type]; } if (!success) - return false; + return ExportConverterError; if (put_in_tempdir) { result_file = tmp_result_file.absFileName(); - return true; + return ExportSuccess; } - result_file = changeExtension(d->exportFileName().absFileName(), ext); + if (dest_filename.empty()) + result_file = changeExtension(d->exportFileName().absFileName(), ext); + else + result_file = dest_filename; // We need to copy referenced files (e. g. included graphics // if format == "dvi") to the result dir. vector const files = runparams.exportdata->externalFiles(format); - string const dest = onlyPath(result_file); + string const dest = runparams.export_folder.empty() ? + onlyPath(result_file) : runparams.export_folder; bool use_force = use_gui ? lyxrc.export_overwrite == ALL_FILES : force_overwrite == ALL_FILES; CopyStatus status = use_force ? FORCE : SUCCESS; @@ -3666,14 +3730,27 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, vector::const_iterator const en = files.end(); for (; it != en && status != CANCEL; ++it) { string const fmt = formats.getFormatFromFile(it->sourceName); + string fixedName = it->exportName; + if (!runparams.export_folder.empty()) { + // Relative pathnames starting with ../ will be sanitized + // if exporting to a different folder + while (fixedName.substr(0, 3) == "../") + fixedName = fixedName.substr(3, fixedName.length() - 3); + } + FileName fixedFileName = makeAbsPath(fixedName, dest); + fixedFileName.onlyPath().createPath(); status = copyFile(fmt, it->sourceName, - makeAbsPath(it->exportName, dest), - it->exportName, status == FORCE); + fixedFileName, + it->exportName, status == FORCE, + runparams.export_folder.empty()); } if (status == CANCEL) { message(_("Document export cancelled.")); - } else if (tmp_result_file.exists()) { + return ExportCancel; + } + + if (tmp_result_file.exists()) { // Finally copy the main file use_force = use_gui ? lyxrc.export_overwrite != NO_FILES : force_overwrite != NO_FILES; @@ -3682,92 +3759,50 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, status = copyFile(format, tmp_result_file, FileName(result_file), result_file, status == FORCE); - message(bformat(_("Document exported as %1$s " - "to file `%2$s'"), - formats.prettyName(format), - makeDisplayPath(result_file))); + if (status == CANCEL) { + message(_("Document export cancelled.")); + return ExportCancel; + } else { + message(bformat(_("Document exported as %1$s " + "to file `%2$s'"), + formats.prettyName(format), + makeDisplayPath(result_file))); + } } else { // This must be a dummy converter like fax (bug 1888) message(bformat(_("Document exported as %1$s"), formats.prettyName(format))); } - return true; + return ExportSuccess; } -bool Buffer::doExport(string const & format, bool put_in_tempdir, - bool includeall) const +Buffer::ExportStatus Buffer::preview(string const & format) const { - string result_file; - // (1) export with all included children (omit \includeonly) - if (includeall && !doExport(format, put_in_tempdir, true, result_file)) - return false; - // (2) export with included children only - return doExport(format, put_in_tempdir, false, result_file); + bool const update_unincluded = + params().maintain_unincluded_children + && !params().getIncludedChildren().empty(); + return preview(format, update_unincluded); } - -bool Buffer::preview(string const & format, bool includeall) const +Buffer::ExportStatus Buffer::preview(string const & format, bool includeall) const { MarkAsExporting exporting(this); string result_file; // (1) export with all included children (omit \includeonly) - if (includeall && !doExport(format, true, true)) - return false; - // (2) export with included children only - if (!doExport(format, true, false, result_file)) - return false; - return formats.view(*this, FileName(result_file), format); -} - - -bool Buffer::isExportable(string const & format) const -{ - vector backs = backends(); - for (vector::const_iterator it = backs.begin(); - it != backs.end(); ++it) - if (theConverters().isReachable(*it, format)) - return true; - return false; -} - - -vector Buffer::exportableFormats(bool only_viewable) const -{ - vector const backs = backends(); - set excludes; - if (params().useNonTeXFonts) { - excludes.insert("latex"); - excludes.insert("pdflatex"); + if (includeall) { + ExportStatus const status = doExport(format, true, true, result_file); + if (status != ExportSuccess) + return status; } - vector result = - theConverters().getReachable(backs[0], only_viewable, true, excludes); - for (vector::const_iterator it = backs.begin() + 1; - it != backs.end(); ++it) { - vector r = - theConverters().getReachable(*it, only_viewable, false, excludes); - result.insert(result.end(), r.begin(), r.end()); - } - return result; -} - - -vector Buffer::backends() const -{ - vector v; - v.push_back(bufferFormat()); - // FIXME: Don't hardcode format names here, but use a flag - if (v.back() == "latex") { - v.push_back("pdflatex"); - v.push_back("luatex"); - v.push_back("xetex"); - } else if (v.back() == "xetex") - v.push_back("luatex"); - v.push_back("xhtml"); - v.push_back("text"); - v.push_back("lyx"); - return v; + // (2) export with included children only + ExportStatus const status = doExport(format, true, false, result_file); + if (status != ExportSuccess) + return status; + if (!formats.view(*this, FileName(result_file), format)) + return PreviewError; + return PreviewSuccess; } @@ -4126,10 +4161,12 @@ void Buffer::Impl::setLabel(ParIterator & it, UpdateType utype) const // Compute the item depth of the paragraph par.itemdepth = getItemDepth(it); - if (layout.margintype == MARGIN_MANUAL - || layout.latextype == LATEX_BIB_ENVIRONMENT) { + if (layout.margintype == MARGIN_MANUAL) { if (par.params().labelWidthString().empty()) par.params().labelWidthString(par.expandLabel(layout, bp)); + } else if (layout.latextype == LATEX_BIB_ENVIRONMENT) { + // we do not need to do anything here, since the empty case is + // handled during export. } else { par.params().labelWidthString(docstring()); } @@ -4280,8 +4317,9 @@ int Buffer::spellCheck(DocIterator & from, DocIterator & to, WordLangTuple wl; suggestions.clear(); word_lang = WordLangTuple(); + bool const to_end = to.empty(); + DocIterator const end = to_end ? doc_iterator_end(this) : to; // OK, we start from here. - DocIterator const end = doc_iterator_end(this); for (; from != end; from.forwardPos()) { // We are only interested in text so remove the math CursorSlice. while (from.inMathed()) { @@ -4289,8 +4327,8 @@ int Buffer::spellCheck(DocIterator & from, DocIterator & to, from.pos()++; } // If from is at the end of the document (which is possible - // when leaving the mathed) LyX will crash later. - if (from == end) + // when leaving the mathed) LyX will crash later otherwise. + if (from.atEnd() || (!to_end && from >= end)) break; to = from; from.paragraph().spellCheck();