X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBuffer.cpp;h=6c3841ba638ee1941effd418265a999564c4107f;hb=1f820d11a26a156fd26dced06ea5d8b7a07896e2;hp=9997c81877c7113a968d0e360508bb6eaf7b60ca;hpb=4d0e8cb5f0d9afab5ae768e94d3015f251dc5fa5;p=lyx.git diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 9997c81877..6c3841ba63 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -66,7 +66,6 @@ #include "WordLangTuple.h" #include "WordList.h" -#include "insets/InsetBibitem.h" #include "insets/InsetBibtex.h" #include "insets/InsetBranch.h" #include "insets/InsetInclude.h" @@ -78,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" @@ -118,6 +117,7 @@ using namespace std; using namespace lyx::support; +using namespace lyx::graphics; namespace lyx { @@ -128,7 +128,7 @@ namespace { // Do not remove the comment below, so we get merge conflict in // independent branches. Instead add your own. -int const LYX_FORMAT = 408; // gb add script inset +int const LYX_FORMAT = 413; // rgh: html_css_as_file typedef map DepClean; typedef map > RefCache; @@ -151,6 +151,7 @@ public: ~Impl() { + delete preview_loader_; if (wa_) { wa_->closeAll(); delete wa_; @@ -258,12 +259,19 @@ public: mutable bool bibfile_cache_valid_; /// Cache of timestamps of .bib files map bibfile_status_; + /// Indicates whether the bibinfo has changed since the last time + /// we ran updateBuffer(), i.e., whether citation labels may need + /// to be updated. + mutable bool cite_labels_valid_; mutable RefCache ref_cache_; /// 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 { @@ -328,10 +336,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), 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(); @@ -348,6 +357,7 @@ Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_, bibinfo_cache_valid_ = cloned_buffer_->d->bibinfo_cache_valid_; 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_; } @@ -423,29 +433,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; } @@ -598,7 +629,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; @@ -673,7 +704,7 @@ int Buffer::readHeader(Lexer & lex) params().headsep.erase(); params().footskip.erase(); params().columnsep.erase(); - params().fontsCJK.erase(); + params().fonts_cjk.erase(); params().listings_params.clear(); params().clearLayoutModules(); params().clearRemovedModules(); @@ -821,9 +852,17 @@ bool Buffer::readDocument(Lexer & lex) // read main text bool const res = text().read(lex, errorList, d->inset); + // inform parent buffer about local macros + if (parent()) { + Buffer const * pbuf = parent(); + UserMacroSet::const_iterator cit = usermacros.begin(); + UserMacroSet::const_iterator end = usermacros.end(); + for (; cit != end; ++cit) + pbuf->usermacros.insert(*cit); + } usermacros.clear(); updateMacros(); - updateMacroInstances(); + updateMacroInstances(InternalUpdate); return res; } @@ -861,7 +900,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); @@ -889,8 +933,8 @@ Buffer::ReadStatus Buffer::readFile(FileName const & fn) } d->file_fully_loaded = true; - d->read_only = !fname.isWritable(); - params().compressed = fname.isZippedFile(); + d->read_only = !d->filename.isWritable(); + params().compressed = d->filename.isZippedFile(); saveCheckSum(); return ReadSuccess; } @@ -908,16 +952,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 (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(); } @@ -988,13 +1051,13 @@ Buffer::ReadStatus Buffer::convertLyXFormat(FileName const & fn, if (from_format < LYX_FORMAT) { Alert::error(_("Conversion script failed"), bformat(_("%1$s is from an older version" - " of LyX, but the lyx2lyx script" + " of LyX and the lyx2lyx script" " failed to convert it."), from_utf8(fn.absFileName()))); return LyX2LyXOlderFormat; } else { Alert::error(_("Conversion script failed"), - bformat(_("%1$s is from an newer version" + bformat(_("%1$s is from a newer version" " of LyX and the lyx2lyx script" " failed to convert it."), from_utf8(fn.absFileName()))); @@ -1008,11 +1071,25 @@ Buffer::ReadStatus Buffer::convertLyXFormat(FileName const & fn, // Should probably be moved to somewhere else: BufferView? GuiView? bool Buffer::save() const { + docstring const file = makeDisplayPath(absFileName(), 20); + d->filename.refresh(); + + // check the read-only status before moving the file as a backup + if (d->filename.exists()) { + bool const read_only = !d->filename.isWritable(); + if (read_only) { + Alert::warning(_("File is read-only"), + bformat(_("The file %1$s cannot be written because it " + "is marked as read-only."), file)); + return false; + } + } + // ask if the disk file has been externally modified (use checksum method) if (fileName().exists() && isExternallyModified(checksum_method)) { - docstring const file = makeDisplayPath(absFileName(), 20); - docstring text = bformat(_("Document %1$s has been externally modified. Are you sure " - "you want to overwrite this file?"), file); + docstring text = + bformat(_("Document %1$s has been externally modified. " + "Are you sure you want to overwrite this file?"), file); int const ret = Alert::prompt(_("Overwrite modified file?"), text, 1, 1, _("&Overwrite"), _("&Cancel")); if (ret == 1) @@ -1034,13 +1111,18 @@ bool Buffer::save() const backupName = FileName(addName(lyxrc.backupdir_path, mangledName)); } - // do not copy because of #6587 - if (fileName().moveTo(backupName)) { - madeBackup = true; - } else { + + // Except file is symlink do not copy because of #6587. + // Hard links have bad luck. + if (fileName().isSymLink()) + madeBackup = fileName().copyTo(backupName); + else + madeBackup = fileName().moveTo(backupName); + + if (!madeBackup) { Alert::error(_("Backup failure"), bformat(_("Cannot create backup file %1$s.\n" - "Please check whether the directory exists and is writeable."), + "Please check whether the directory exists and is writable."), from_utf8(backupName.absFileName()))); //LYXERR(Debug::DEBUG, "Fs error: " << fe.what()); } @@ -1060,6 +1142,11 @@ bool Buffer::save() const bool Buffer::writeFile(FileName const & fname) const { + // FIXME Do we need to do these here? I don't think writing + // the LyX file depends upon it. (RGH) + // updateBuffer(); + // updateMacroInstances(); + if (d->read_only && fname == d->filename) return false; @@ -1113,24 +1200,24 @@ docstring Buffer::emergencyWrite() LYXERR0(" " << s); if (writeFile(FileName(s))) { markClean(); - user_message += bformat(_(" Saved to %1$s. Phew.\n"), from_utf8(s)); + user_message += " " + bformat(_("Saved to %1$s. Phew.\n"), from_utf8(s)); return user_message; } else { - user_message += _(" Save failed! Trying again...\n"); + user_message += " " + _("Save failed! Trying again...\n"); } } // 2) In HOME directory. - string s = addName(package().home_dir().absFileName(), absFileName()); + string s = addName(Package::get_home_dir().absFileName(), absFileName()); s += ".emergency"; lyxerr << ' ' << s << endl; if (writeFile(FileName(s))) { markClean(); - user_message += bformat(_(" Saved to %1$s. Phew.\n"), from_utf8(s)); + user_message += " " + bformat(_("Saved to %1$s. Phew.\n"), from_utf8(s)); return user_message; } - user_message += _(" Save failed! Trying yet again...\n"); + user_message += " " + _("Save failed! Trying yet again...\n"); // 3) In "/tmp" directory. // MakeAbsPath to prepend the current @@ -1140,11 +1227,11 @@ docstring Buffer::emergencyWrite() lyxerr << ' ' << s << endl; if (writeFile(FileName(s))) { markClean(); - user_message += bformat(_(" Saved to %1$s. Phew.\n"), from_utf8(s)); + user_message += " " + bformat(_("Saved to %1$s. Phew.\n"), from_utf8(s)); return user_message; } - user_message += _(" Save failed! Bummer. Document is lost."); + user_message += " " + _("Save failed! Bummer. Document is lost."); // Don't try again. markClean(); return user_message; @@ -1161,7 +1248,7 @@ bool Buffer::write(ostream & ofs) const // The top of the file should not be written by params(). // write out a comment in the top of the file - ofs << "#LyX " << lyx_version + ofs << "#LyX " << lyx_version_major << "." << lyx_version_minor << " created this file. For more info see http://www.lyx.org/\n" << "\\lyxformat " << LYX_FORMAT << "\n" << "\\begin_document\n"; @@ -1219,6 +1306,11 @@ bool Buffer::makeLaTeXFile(FileName const & fname, { OutputParams runparams = runparams_in; + // This is necessary for LuaTeX/XeTeX with tex fonts. + // See FIXME in BufferParams::encoding() + if (runparams.isFullUnicode()) + runparams.encoding = encodings.fromLyXName("utf8-plain"); + string const encoding = runparams.encoding->iconvName(); LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "..."); @@ -1238,9 +1330,18 @@ bool Buffer::makeLaTeXFile(FileName const & fname, ErrorList & errorList = d->errorLists["Export"]; errorList.clear(); bool failed_export = false; + otexstream os(ofs, d->texrow); + + // make sure we are ready to export + // this needs to be done before we validate + // FIXME Do we need to do this all the time? I.e., in children + // of a master we are exporting? + updateBuffer(); + updateMacroInstances(OutputUpdate); + try { - d->texrow.reset(); - writeLaTeXSource(ofs, original_path, + os.texrow().reset(); + writeLaTeXSource(os, original_path, runparams, output_preamble, output_body); } catch (EncodingException & e) { @@ -1283,7 +1384,7 @@ bool Buffer::makeLaTeXFile(FileName const & fname, } -void Buffer::writeLaTeXSource(odocstream & os, +void Buffer::writeLaTeXSource(otexstream & os, string const & original_path, OutputParams const & runparams_in, bool const output_preamble, bool const output_body) const @@ -1308,20 +1409,9 @@ void Buffer::writeLaTeXSource(odocstream & os, "For more info, see http://www.lyx.org/.\n" "%% Do not edit unless you really know what " "you are doing.\n"; - d->texrow.newline(); - d->texrow.newline(); } LYXERR(Debug::INFO, "lyx document header finished"); - // Don't move this behind the parent_buffer=0 code below, - // because then the macros will not get the right "redefinition" - // flag as they don't see the parent macros which are output before. - updateMacros(); - - // fold macros if possible, still with parent buffer as the - // macros will be put in the prefix anyway. - updateMacroInstances(); - // 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 @@ -1335,9 +1425,7 @@ void Buffer::writeLaTeXSource(odocstream & os, if (output_preamble) { if (!runparams.nice) { // code for usual, NOT nice-latex-file - os << "\\batchmode\n"; // changed - // from \nonstopmode - d->texrow.newline(); + os << "\\batchmode\n"; // changed from \nonstopmode } if (!original_path.empty()) { // FIXME UNICODE @@ -1373,9 +1461,6 @@ void Buffer::writeLaTeXSource(odocstream & os, << "\\def\\input@path{{" << inputpath << "/}}\n" << "\\makeatother\n"; - d->texrow.newline(); - d->texrow.newline(); - d->texrow.newline(); } } @@ -1387,7 +1472,6 @@ void Buffer::writeLaTeXSource(odocstream & os, runparams.use_polyglossia = features.usePolyglossia(); // Write the preamble runparams.use_babel = params().writeLaTeX(os, features, - d->texrow, d->filename.onlyPath()); runparams.use_japanese = features.isRequired("japanese"); @@ -1397,19 +1481,18 @@ void Buffer::writeLaTeXSource(odocstream & os, // make the body. os << "\\begin{document}\n"; - d->texrow.newline(); // output the parent macros MacroSet::iterator it = parentMacros.begin(); MacroSet::iterator end = parentMacros.end(); for (; it != end; ++it) { - int num_lines = (*it)->write(os, true); - d->texrow.newlines(num_lines); + int num_lines = (*it)->write(os.os(), true); + os.texrow().newlines(num_lines); } } // output_preamble - d->texrow.start(paragraphs().begin()->id(), 0); + os.texrow().start(paragraphs().begin()->id(), 0); LYXERR(Debug::INFO, "preamble finished, now the body."); @@ -1423,7 +1506,7 @@ void Buffer::writeLaTeXSource(odocstream & os, } // the real stuff - latexParagraphs(*this, text(), os, d->texrow, runparams); + latexParagraphs(*this, text(), os, runparams); // Restore the parenthood if needed if (output_preamble) @@ -1431,11 +1514,9 @@ void Buffer::writeLaTeXSource(odocstream & os, // add this just in case after all the paragraphs os << endl; - d->texrow.newline(); if (output_preamble) { os << "\\end{document}\n"; - d->texrow.newline(); LYXERR(Debug::LATEX, "makeLaTeXFile...done"); } else { LYXERR(Debug::LATEX, "LaTeXFile for inclusion made."); @@ -1443,7 +1524,7 @@ void Buffer::writeLaTeXSource(odocstream & os, runparams_in.encoding = runparams.encoding; // Just to be sure. (Asger) - d->texrow.newline(); + os.texrow().newline(); //for (int i = 0; itexrow.rows(); i++) { // int id,pos; @@ -1452,25 +1533,7 @@ void Buffer::writeLaTeXSource(odocstream & os, //} LYXERR(Debug::INFO, "Finished making LaTeX file."); - LYXERR(Debug::INFO, "Row count was " << d->texrow.rows() - 1 << '.'); -} - - -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; + LYXERR(Debug::INFO, "Row count was " << os.texrow().rows() - 1 << '.'); } @@ -1484,6 +1547,11 @@ void Buffer::makeDocBookFile(FileName const & fname, if (!openFileWrite(ofs, fname)) return; + // make sure we are ready to export + // this needs to be done before we validate + updateBuffer(); + updateMacroInstances(OutputUpdate); + writeDocBookSource(ofs, fname.absFileName(), runparams, body_only); ofs.close(); @@ -1558,8 +1626,6 @@ void Buffer::writeDocBookSource(odocstream & os, string const & fname, params().documentClass().counters().reset(); - updateMacros(); - sgml::openTag(os, top); os << '\n'; docbookParagraphs(text(), *this, os, runparams); @@ -1568,8 +1634,7 @@ void Buffer::writeDocBookSource(odocstream & os, string const & fname, void Buffer::makeLyXHTMLFile(FileName const & fname, - OutputParams const & runparams, - bool const body_only) const + OutputParams const & runparams) const { LYXERR(Debug::LATEX, "makeLyXHTMLFile..."); @@ -1577,7 +1642,12 @@ void Buffer::makeLyXHTMLFile(FileName const & fname, if (!openFileWrite(ofs, fname)) return; - writeLyXHTMLSource(ofs, runparams, body_only); + // make sure we are ready to export + // this has to be done before we validate + updateBuffer(UpdateMaster, OutputUpdate); + updateMacroInstances(OutputUpdate); + + writeLyXHTMLSource(ofs, runparams); ofs.close(); if (ofs.fail()) @@ -1591,10 +1661,7 @@ void Buffer::writeLyXHTMLSource(odocstream & os, { LaTeXFeatures features(*this, params(), runparams); validate(features); - updateBuffer(UpdateMaster, OutputUpdate); d->bibinfo_.makeCitationLabels(*this); - updateMacros(); - updateMacroInstances(); if (!only_body) { os << "\n" @@ -1680,8 +1747,6 @@ void Buffer::validate(LaTeXFeatures & features) const { params().validate(features); - updateMacros(); - for_each(paragraphs().begin(), paragraphs().end(), bind(&Paragraph::validate, _1, ref(features))); @@ -1743,12 +1808,14 @@ void Buffer::updateBibfilesCache(UpdateScope scope) const } d->bibfile_cache_valid_ = true; d->bibinfo_cache_valid_ = false; + d->cite_labels_valid_ = false; } void Buffer::invalidateBibinfoCache() const { d->bibinfo_cache_valid_ = false; + d->cite_labels_valid_ = false; // also invalidate the cache for the parent buffer Buffer const * const pbuf = d->parent(); if (pbuf) @@ -1760,6 +1827,7 @@ void Buffer::invalidateBibfileCache() const { d->bibfile_cache_valid_ = false; d->bibinfo_cache_valid_ = false; + d->cite_labels_valid_ = false; // also invalidate the cache for the parent buffer Buffer const * const pbuf = d->parent(); if (pbuf) @@ -1791,43 +1859,78 @@ BiblioInfo const & Buffer::masterBibInfo() const } -void Buffer::checkBibInfoCache() const +void Buffer::checkIfBibInfoCacheIsValid() const { // use the master's cache Buffer const * const tmp = masterBuffer(); if (tmp != this) { - tmp->checkBibInfoCache(); + tmp->checkIfBibInfoCacheIsValid(); return; } - // this will also reload the cache if it is invalid - support::FileNameList const & bibfiles_cache = getBibfilesCache(); - // compare the cached timestamps with the actual ones. - support::FileNameList::const_iterator ei = bibfiles_cache.begin(); - support::FileNameList::const_iterator en = bibfiles_cache.end(); + FileNameList const & bibfiles_cache = getBibfilesCache(); + FileNameList::const_iterator ei = bibfiles_cache.begin(); + FileNameList::const_iterator en = bibfiles_cache.end(); for (; ei != en; ++ ei) { time_t lastw = ei->lastModified(); time_t prevw = d->bibfile_status_[*ei]; if (lastw != prevw) { d->bibinfo_cache_valid_ = false; + d->cite_labels_valid_ = false; d->bibfile_status_[*ei] = lastw; } } - - // if not valid, then reload the info - if (!d->bibinfo_cache_valid_) { - d->bibinfo_.clear(); - fillWithBibKeys(d->bibinfo_); - d->bibinfo_cache_valid_ = true; +} + + +void Buffer::reloadBibInfoCache() const +{ + // use the master's cache + Buffer const * const tmp = masterBuffer(); + if (tmp != this) { + tmp->reloadBibInfoCache(); + return; } + + checkIfBibInfoCacheIsValid(); + if (d->bibinfo_cache_valid_) + return; + + d->bibinfo_.clear(); + collectBibKeys(); + d->bibinfo_cache_valid_ = true; } -void Buffer::fillWithBibKeys(BiblioInfo & keys) const +void Buffer::collectBibKeys() const { for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) - it->fillWithBibKeys(keys, it); + it->collectBibKeys(it); +} + + +void Buffer::addBiblioInfo(BiblioInfo const & bi) const +{ + Buffer const * tmp = masterBuffer(); + BiblioInfo & masterbi = (tmp == this) ? + d->bibinfo_ : tmp->d->bibinfo_; + masterbi.mergeBiblioInfo(bi); +} + + +void Buffer::addBibTeXInfo(docstring const & key, BibTeXInfo const & bi) const +{ + Buffer const * tmp = masterBuffer(); + BiblioInfo & masterbi = (tmp == this) ? + d->bibinfo_ : tmp->d->bibinfo_; + masterbi[key] = bi; +} + + +bool Buffer::citeLabelsValid() const +{ + return masterBuffer()->d->cite_labels_valid_; } @@ -1846,21 +1949,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()) { @@ -1889,7 +1977,7 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag) case LFUN_BUFFER_EXPORT: { docstring const arg = cmd.argument(); - enable = arg == "custom" || isExportable(to_utf8(arg)); + enable = arg == "custom" || params().isExportable(to_utf8(arg)); if (!enable) flag.message(bformat( _("Don't know how to export to format: %1$s"), arg)); @@ -1897,11 +1985,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: @@ -2012,7 +2100,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; } @@ -2190,7 +2278,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, @@ -2198,7 +2287,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 @@ -2207,7 +2297,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 { @@ -2230,7 +2321,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) @@ -2388,6 +2479,8 @@ bool Buffer::isExternallyModified(CheckMethod method) const void Buffer::saveCheckSum() const { FileName const & file = d->filename; + + file.refresh(); if (file.exists()) { d->timestamp_ = file.lastModified(); d->checksum_ = file.checksum(); @@ -2492,7 +2585,7 @@ Buffer const * Buffer::parent() const ListOfBuffers Buffer::allRelatives() const { ListOfBuffers lb = masterBuffer()->getDescendents(); - lb.push_front(const_cast(this)); + lb.push_front(const_cast(masterBuffer())); return lb; } @@ -2547,6 +2640,12 @@ void Buffer::collectChildren(ListOfBuffers & clist, bool grand_children) const // there might be grandchildren child->collectChildren(clist, true); } + // Make sure we have not included ourselves. + ListOfBuffers::iterator bit = find(clist.begin(), clist.end(), this); + if (bit != clist.end()) { + LYXERR0("Recursive include detected in `" << fileName() << "'."); + clist.erase(bit); + } } @@ -2633,7 +2732,9 @@ MacroData const * Buffer::Impl::getBufferMacro(docstring const & name, break; // scope ends behind pos? - if (pos < it->second.first) { + if (pos < it->second.first + && (cloned_buffer_ || + theBufferList().isLoaded(it->second.second))) { // look for macro in external file macro_lock = true; MacroData const * data @@ -2867,7 +2968,7 @@ void Buffer::getUsedBranches(std::list & result, bool const from_mast } -void Buffer::updateMacroInstances() const +void Buffer::updateMacroInstances(UpdateType utype) const { LYXERR(Debug::MACROS, "updateMacroInstances for " << d->filename.onlyFileName()); @@ -2885,7 +2986,7 @@ void Buffer::updateMacroInstances() const MacroContext mc = MacroContext(this, it); for (DocIterator::idx_type i = 0; i < n; ++i) { MathData & data = minset->cell(i); - data.updateMacros(0, mc); + data.updateMacros(0, mc, utype); } } } @@ -2995,22 +3096,23 @@ void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to, { //FIXME: This does not work for child documents yet. LASSERT(code == CITE_CODE, /**/); + + reloadBibInfoCache(); + // Check if the label 'from' appears more than once - vector labels; - string paramName; - checkBibInfoCache(); BiblioInfo const & keys = masterBibInfo(); BiblioInfo::const_iterator bit = keys.begin(); BiblioInfo::const_iterator bend = keys.end(); + vector labels; for (; bit != bend; ++bit) // FIXME UNICODE labels.push_back(bit->first); - paramName = "key"; if (count(labels.begin(), labels.end(), from) > 1) return; + string const paramName = "key"; for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) { if (it->lyxCode() == code) { InsetCommand * inset = it->asInsetCommand(); @@ -3024,13 +3126,13 @@ void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to, } -void Buffer::getSourceCode(odocstream & os, pit_type par_begin, - pit_type par_end, bool full_source) const +void Buffer::getSourceCode(odocstream & os, string const format, + pit_type par_begin, pit_type par_end, + bool full_source) const { OutputParams runparams(¶ms().encoding()); runparams.nice = true; - runparams.flavor = params().useXetex ? - OutputParams::XETEX : OutputParams::LATEX; + runparams.flavor = params().getOutputFlavor(format); runparams.linelen = lyxrc.plaintext_linelen; // No side effect of file copying and image conversion runparams.dryrun = true; @@ -3040,11 +3142,15 @@ void Buffer::getSourceCode(odocstream & os, pit_type par_begin, d->texrow.reset(); d->texrow.newline(); d->texrow.newline(); - if (isDocBook()) + if (params().isDocBook()) writeDocBookSource(os, absFileName(), runparams, false); - else + else if (runparams.flavor == OutputParams::HTML) + writeLyXHTMLSource(os, runparams, false); + else { // latex or literate - writeLaTeXSource(os, string(), runparams, true, true); + otexstream ots(os, d->texrow); + writeLaTeXSource(ots, string(), runparams, true, true); + } } else { runparams.par_begin = par_begin; runparams.par_end = par_end; @@ -3064,23 +3170,23 @@ void Buffer::getSourceCode(odocstream & os, pit_type par_begin, texrow.newline(); texrow.newline(); // output paragraphs - if (isDocBook()) + if (params().isDocBook()) docbookParagraphs(text(), *this, os, runparams); - else + else if (runparams.flavor == OutputParams::HTML) { + XHTMLStream xs(os); + xhtmlParagraphs(text(), *this, xs, runparams); + } else { // latex or literate - latexParagraphs(*this, text(), os, texrow, runparams); + otexstream ots(os, texrow); + latexParagraphs(*this, text(), ots, runparams); + } } } 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]; } @@ -3304,39 +3410,6 @@ bool Buffer::autoSave() const } -string Buffer::bufferFormat() const -{ - string format = params().documentClass().outputFormat(); - if (format == "latex") { - if (params().useXetex) - return "xetex"; - if (params().encoding().package() == Encoding::japanese) - return "platex"; - } - return format; -} - - -string Buffer::getDefaultOutputFormat() const -{ - if (!params().defaultOutputFormat.empty() - && params().defaultOutputFormat != "default") - return params().defaultOutputFormat; - typedef vector 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; -} - - namespace { // helper class, to guarantee this gets reset properly class MarkAsExporting { @@ -3359,6 +3432,11 @@ namespace { void Buffer::setExportStatus(bool e) const { d->doing_export = e; + ListOfBuffers clist = getDescendents(); + ListOfBuffers::const_iterator cit = clist.begin(); + ListOfBuffers::const_iterator const cen = clist.end(); + for (; cit != cen; ++cit) + (*cit)->d->doing_export = e; } @@ -3377,13 +3455,15 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, 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 + converters.buildGraph(); Graph::EdgePath path; for (vector::const_iterator it = backs.begin(); it != backs.end(); ++it) { - Graph::EdgePath p = theConverters().getPath(*it, format); + Graph::EdgePath p = converters.getPath(*it, format); if (!p.empty() && (path.empty() || p.size() < path.size())) { backend_format = *it; path = p; @@ -3399,7 +3479,7 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, } return false; } - runparams.flavor = theConverters().getFlavor(path); + runparams.flavor = converters.getFlavor(path); } else { backend_format = format; @@ -3417,9 +3497,6 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, filename = changeExtension(filename, formats.extension(backend_format)); - // fix macros - updateMacroInstances(); - // Plain text backend if (backend_format == "text") { runparams.flavor = OutputParams::TEXT; @@ -3440,22 +3517,26 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, break; case BufferParams::LaTeX: runparams.math_flavor = OutputParams::MathAsLaTeX; - break; + break; } - makeLyXHTMLFile(FileName(filename), runparams); - } else if (backend_format == "lyx") + } 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); } // LaTeX backend else if (backend_format == format) { runparams.nice = true; - if (!makeLaTeXFile(FileName(filename), string(), runparams)) + if (!makeLaTeXFile(FileName(filename), string(), runparams)) { + if (d->cloned_buffer_) { + d->cloned_buffer_->d->errorLists["Export"] = + d->errorLists["Export"]; + } return false; + } } else if (!lyxrc.tex_allows_spaces && contains(filePath(), ' ')) { Alert::error(_("File name error"), @@ -3463,21 +3544,26 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, return false; } else { runparams.nice = false; - if (!makeLaTeXFile(FileName(filename), filePath(), runparams)) + if (!makeLaTeXFile(FileName(filename), filePath(), runparams)) { + if (d->cloned_buffer_) { + d->cloned_buffer_->d->errorLists["Export"] = + d->errorLists["Export"]; + } return false; + } } 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)); - bool const success = theConverters().convert(this, FileName(filename), + bool const success = converters.convert(this, FileName(filename), tmp_result_file, FileName(absFileName()), backend_format, format, error_list); // Emit the signal to show the error list or copy it back to the - // cloned Buffer so that it cab be emitted afterwards. + // cloned Buffer so that it can be emitted afterwards. if (format != backend_format) { if (d->cloned_buffer_) { d->cloned_buffer_->d->errorLists[error_type] = @@ -3490,6 +3576,10 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, ListOfBuffers::const_iterator const cen = clist.end(); for (; cit != cen; ++cit) { if (d->cloned_buffer_) { + // Enable reverse search by copying back the + // texrow object to the cloned buffer. + // FIXME: this is not thread safe. + (*cit)->d->cloned_buffer_->d->texrow = (*cit)->d->texrow; (*cit)->d->cloned_buffer_->d->errorLists[error_type] = (*cit)->d->errorLists[error_type]; } else @@ -3503,7 +3593,7 @@ 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]; } @@ -3585,48 +3675,6 @@ bool Buffer::preview(string const & format, bool includeall) const } -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(); - vector result = - theConverters().getReachable(backs[0], only_viewable, true); - for (vector::const_iterator it = backs.begin() + 1; - it != backs.end(); ++it) { - vector r = - theConverters().getReachable(*it, only_viewable, false); - 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"); - else if (v.back() == "xetex") - v.push_back("luatex"); - v.push_back("xhtml"); - v.push_back("text"); - v.push_back("lyx"); - return v; -} - - Buffer::ReadStatus Buffer::extractFromVC() { bool const found = LyXVC::file_not_found_hook(d->filename); @@ -3659,6 +3707,13 @@ Buffer::ReadStatus Buffer::loadEmergency() ReadStatus const ret_llf = loadThisLyXFile(emergencyFile); bool const success = (ret_llf == ReadSuccess); if (success) { + if (isReadonly()) { + Alert::warning(_("File is read-only"), + bformat(_("An emergency file is successfully loaded, " + "but the original file %1$s is marked read-only. " + "Please make sure to save the document as a different " + "file."), from_utf8(d->filename.absFileName()))); + } markDirty(); str = _("Document was successfully recovered."); } else @@ -3714,6 +3769,14 @@ Buffer::ReadStatus Buffer::loadAutosave() ReadStatus const ret_llf = loadThisLyXFile(autosaveFile); // the file is not saved if we load the autosave file. if (ret_llf == ReadSuccess) { + if (isReadonly()) { + Alert::warning(_("File is read-only"), + bformat(_("A backup file is successfully loaded, " + "but the original file %1$s is marked read-only. " + "Please make sure to save the document as a " + "different file."), + from_utf8(d->filename.absFileName()))); + } markDirty(); return ReadSuccess; } @@ -3758,24 +3821,56 @@ Buffer::ReadStatus Buffer::loadThisLyXFile(FileName const & fn) void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const { - TeXErrors::Errors::const_iterator cit = terr.begin(); + TeXErrors::Errors::const_iterator it = terr.begin(); TeXErrors::Errors::const_iterator end = terr.end(); + ListOfBuffers clist = getDescendents(); + ListOfBuffers::const_iterator cen = clist.end(); - for (; cit != end; ++cit) { + for (; it != end; ++it) { int id_start = -1; int pos_start = -1; - int errorRow = cit->error_in_line; - bool found = d->texrow.getIdFromRow(errorRow, id_start, - pos_start); + int errorRow = it->error_in_line; + Buffer const * buf = 0; + Impl const * p = d; + if (it->child_name.empty()) + p->texrow.getIdFromRow(errorRow, id_start, pos_start); + else { + // The error occurred in a child + ListOfBuffers::const_iterator cit = clist.begin(); + for (; cit != cen; ++cit) { + string const child_name = + DocFileName(changeExtension( + (*cit)->absFileName(), "tex")). + mangledFileName(); + if (it->child_name != child_name) + continue; + (*cit)->d->texrow.getIdFromRow(errorRow, + id_start, pos_start); + if (id_start != -1) { + buf = d->cloned_buffer_ + ? (*cit)->d->cloned_buffer_->d->owner_ + : (*cit)->d->owner_; + p = (*cit)->d; + break; + } + } + } int id_end = -1; int pos_end = -1; + bool found; do { ++errorRow; - found = d->texrow.getIdFromRow(errorRow, id_end, pos_end); + found = p->texrow.getIdFromRow(errorRow, id_end, pos_end); } while (found && id_start == id_end && pos_start == pos_end); - errorList.push_back(ErrorItem(cit->error_desc, - cit->error_text, id_start, pos_start, pos_end)); + if (id_start != id_end) { + // Next registered position is outside the inset where + // the error occurred, so signal end-of-paragraph + pos_end = 0; + } + + errorList.push_back(ErrorItem(it->error_desc, + it->error_text, id_start, pos_start, pos_end, buf)); } } @@ -3794,7 +3889,7 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const // do this only if we are the top-level Buffer if (master == this) - checkBibInfoCache(); + reloadBibInfoCache(); // keep the buffers to be children in this set. If the call from the // master comes back we can see which of them were actually seen (i.e. @@ -3838,6 +3933,8 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const // TocBackend update will be done later. return; + d->bibinfo_cache_valid_ = true; + d->cite_labels_valid_ = true; cbuf.tocBackend().update(); if (scope == UpdateMaster) cbuf.structureChanged(); @@ -3933,10 +4030,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()); } @@ -3944,11 +4043,11 @@ void Buffer::Impl::setLabel(ParIterator & it, UpdateType utype) const switch(layout.labeltype) { case LABEL_COUNTER: if (layout.toclevel <= bp.secnumdepth - && (layout.latextype != LATEX_ENVIRONMENT - || it.text()->isFirstInSequence(it.pit()))) { - counters.step(layout.counter, utype); - par.params().labelString( - par.expandLabel(layout, bp)); + && (layout.latextype != LATEX_ENVIRONMENT + || it.text()->isFirstInSequence(it.pit()))) { + if (counters.hasCounter(layout.counter)) + counters.step(layout.counter, utype); + par.params().labelString(par.expandLabel(layout, bp)); } else par.params().labelString(docstring()); break; @@ -4134,6 +4233,7 @@ Buffer::ReadStatus Buffer::reload() updateTitles(); markClean(); message(bformat(_("Document %1$s reloaded."), disp_fn)); + d->undo_.clear(); } else { message(bformat(_("Could not reload document %1$s."), disp_fn)); }