X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2FBuffer.cpp;h=888fb93d9b16a7dbc19f0186b02ed28e3c730f01;hb=0c7bd9a57f2a308bb9659200eda3b7e45f8d5d3c;hp=8af68e43d95fb2be4bb1c8ebc482b15c90b8a5ad;hpb=3c2b3e6907cd2098ba7118bce029f20a43c676ba;p=lyx.git diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 8af68e43d9..888fb93d9b 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -57,6 +57,7 @@ #include "PDFOptions.h" #include "SpellChecker.h" #include "sgml.h" +#include "texstream.h" #include "TexRow.h" #include "Text.h" #include "TextClass.h" @@ -107,12 +108,12 @@ #include "support/types.h" #include "support/bind.h" -#include "support/shared_ptr.h" #include #include #include #include +#include #include #include #include @@ -133,14 +134,6 @@ int const LYX_FORMAT = LYX_FORMAT_LYX; typedef map DepClean; typedef map > 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 @@ -212,6 +205,13 @@ public: */ bool file_fully_loaded; + /// original format of loaded file + int file_format; + + /// if the file was originally loaded from an older format, do + /// we need to back it up still? + bool need_format_backup; + /// Ignore the parent (e.g. when exporting a child standalone)? bool ignore_parent; @@ -237,8 +237,8 @@ public: /// positions of child buffers in the buffer typedef map BufferPositionMap; struct ScopeBuffer { - ScopeBuffer() {} - ScopeBuffer(DocIterator const & s,Buffer const * b) + ScopeBuffer() : buffer(0) {} + ScopeBuffer(DocIterator const & s, Buffer const * b) : scope(s), buffer(b) {} DocIterator scope; Buffer const * buffer; @@ -371,6 +371,10 @@ public: + (with_blanks ? blank_count_ : 0); } + // does the buffer contain tracked changes? (if so, we automatically + // display the review toolbar, for instance) + mutable bool tracked_changes_present_; + private: /// So we can force access via the accessors. mutable Buffer const * parent_buffer; @@ -413,11 +417,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), internal_buffer(false), read_only(readonly_), filename(file), - file_fully_loaded(false), ignore_parent(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), inset(0), preview_loader_(0), - cloned_buffer_(cloned_buffer), clone_list_(0), + file_fully_loaded(false), file_format(LYX_FORMAT), need_format_backup(false), + ignore_parent(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), inset(0), + preview_loader_(0), cloned_buffer_(cloned_buffer), clone_list_(0), doing_export(false), parent_buffer(0), word_count_(0), char_count_(0), blank_count_(0) { @@ -443,6 +447,7 @@ Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_, preview_file_ = cloned_buffer_->d->preview_file_; preview_format_ = cloned_buffer_->d->preview_format_; preview_error_ = cloned_buffer_->d->preview_error_; + tracked_changes_present_ = cloned_buffer_->d->tracked_changes_present_; } @@ -518,8 +523,12 @@ Buffer::~Buffer() Impl::BufferPositionMap::iterator end = d->children_positions.end(); for (; it != end; ++it) { Buffer * child = const_cast(it->first); - if (theBufferList().isLoaded(child)) - theBufferList().releaseChild(this, child); + if (theBufferList().isLoaded(child)) { + if (theBufferList().isOthersChild(this, child)) + child->setParent(0); + else + theBufferList().release(child); + } } if (!isClean()) { @@ -534,8 +543,7 @@ Buffer::~Buffer() d->position_to_children.clear(); if (!d->temppath.destroyDirectory()) { - Alert::warning(_("Could not remove temporary directory"), - bformat(_("Could not remove the temporary directory %1$s"), + LYXERR0(bformat(_("Could not remove the temporary directory %1$s"), from_utf8(d->temppath.absFileName()))); } removePreviews(); @@ -1031,7 +1039,10 @@ bool Buffer::readDocument(Lexer & lex) params().indiceslist().addDefault(B_("Index")); // read main text - d->old_position = originFilePath(); + if (FileName::isAbsolute(params().origin)) + d->old_position = params().origin; + else + d->old_position = filePath(); bool const res = text().read(lex, errorList, d->inset); d->old_position.clear(); @@ -1141,10 +1152,15 @@ Buffer::ReadStatus Buffer::readFile(FileName const & fn) if (file_format != LYX_FORMAT) { FileName tmpFile; - ReadStatus const ret_clf = convertLyXFormat(fn, tmpFile, file_format); + ReadStatus ret_clf = convertLyXFormat(fn, tmpFile, file_format); if (ret_clf != ReadSuccess) return ret_clf; - return readFile(tmpFile); + ret_clf = readFile(tmpFile); + if (ret_clf == ReadSuccess) { + d->file_format = file_format; + d->need_format_backup = true; + } + return ret_clf; } // FIXME: InsetInfo needs to know whether the file is under VCS @@ -1303,6 +1319,41 @@ Buffer::ReadStatus Buffer::convertLyXFormat(FileName const & fn, } +FileName Buffer::getBackupName() const { + FileName const & fn = fileName(); + string const fname = fn.onlyFileNameWithoutExt(); + string const fext = fn.extension(); + string const fpath = lyxrc.backupdir_path.empty() ? + fn.onlyPath().absFileName() : + lyxrc.backupdir_path; + string const fform = convert(d->file_format); + string const backname = fname + "-lyxformat-" + fform; + FileName backup(addName(fpath, addExtension(backname, fext))); + + // limit recursion, just in case + int v = 1; + unsigned long orig_checksum = 0; + while (backup.exists() && v < 100) { + if (orig_checksum == 0) + orig_checksum = fn.checksum(); + unsigned long new_checksum = backup.checksum(); + if (orig_checksum == new_checksum) { + LYXERR(Debug::FILES, "Not backing up " << fn << + "since " << backup << "has the same checksum."); + // a bit of a hack, but we have to check this anyway + // below, and setting this is simpler than introducing + // a special boolean for this purpose. + v = 1000; + break; + } + string const newbackname = backname + "-" + convert(v); + backup.set(addName(fpath, addExtension(newbackname, fext))); + v++; + } + return v < 100 ? backup : FileName(); +} + + // Should probably be moved to somewhere else: BufferView? GuiView? bool Buffer::save() const { @@ -1347,9 +1398,8 @@ bool Buffer::save() const // proper location once that has been done successfully. that // way we preserve the original file if something goes wrong. string const justname = fileName().onlyFileNameWithoutExt(); - boost::scoped_ptr - tempfile(new TempFile(fileName().onlyPath(), - justname + "-XXXXXX.lyx")); + auto tempfile = make_unique(fileName().onlyPath(), + justname + "-XXXXXX.lyx"); bool const symlink = fileName().isSymLink(); if (!symlink) tempfile->setAutoRemove(false); @@ -1362,13 +1412,22 @@ bool Buffer::save() const // we will set this to false if we fail bool made_backup = true; - FileName backupName(absFileName() + '~'); - if (lyxrc.make_backup) { - if (!lyxrc.backupdir_path.empty()) { - string const mangledName = - subst(subst(backupName.absFileName(), '/', '!'), ':', '!'); - backupName = FileName(addName(lyxrc.backupdir_path, - mangledName)); + FileName backupName; + bool const needBackup = lyxrc.make_backup || d->need_format_backup; + if (needBackup) { + if (d->need_format_backup) + backupName = getBackupName(); + + // If we for some reason failed to find a backup name in case of + // a format change, this will still set one. It's the best we can + // do in this case. + if (backupName.empty()) { + backupName.set(fileName().absFileName() + "~"); + if (!lyxrc.backupdir_path.empty()) { + string const mangledName = + subst(subst(backupName.absFileName(), '/', '!'), ':', '!'); + backupName.set(addName(lyxrc.backupdir_path, mangledName)); + } } LYXERR(Debug::FILES, "Backing up original file to " << @@ -1385,6 +1444,10 @@ bool Buffer::save() const "Please check whether the directory exists and is writable."), from_utf8(backupName.absFileName()))); //LYXERR(Debug::DEBUG, "Fs error: " << fe.what()); + } else if (d->need_format_backup) { + // the original file has been backed up successfully, so we + // will not need to do that again + d->need_format_backup = false; } } @@ -1401,12 +1464,15 @@ bool Buffer::save() const // time stamp is invalidated by copying/moving saveCheckSum(); markClean(); + if (d->file_format != LYX_FORMAT) + // the file associated with this buffer is now in the current format + d->file_format = LYX_FORMAT; return true; } // else we saved the file, but failed to move it to the right location. - if (lyxrc.make_backup && made_backup && !symlink) { - // the original file was moved to filename.lyx~, so it will look + if (needBackup && made_backup && !symlink) { + // the original file was moved to some new location, so it will look // to the user as if it was deleted. (see bug #9234.) we could try // to restore it, but that would basically mean trying to do again // what we just failed to do. better to leave things as they are. @@ -1594,10 +1660,12 @@ 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"); + // XeTeX with TeX fonts is only safe with ASCII encoding (see also #9740), + // Check here, because the "flavor" is not known in BufferParams::encoding() + // (power users can override this safety measure selecting "utf8-plain"). + if (!params().useNonTeXFonts && (runparams.flavor == OutputParams::XETEX) + && (runparams.encoding->name() != "utf8-plain")) + runparams.encoding = encodings.fromLyXName("ascii"); string const encoding = runparams.encoding->iconvName(); LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << ", fname=" << fname.realPath()); @@ -1617,7 +1685,7 @@ bool Buffer::makeLaTeXFile(FileName const & fname, ErrorList & errorList = d->errorLists["Export"]; errorList.clear(); bool failed_export = false; - otexstream os(ofs, d->texrow); + otexstream os(ofs); // make sure we are ready to export // this needs to be done before we validate @@ -1627,7 +1695,6 @@ bool Buffer::makeLaTeXFile(FileName const & fname, updateMacroInstances(OutputUpdate); try { - os.texrow().reset(); writeLaTeXSource(os, original_path, runparams, output); } catch (EncodingException const & e) { @@ -1658,6 +1725,8 @@ bool Buffer::makeLaTeXFile(FileName const & fname, lyx_exit(1); } + d->texrow = move(os.texrow()); + ofs.close(); if (ofs.fail()) { failed_export = true; @@ -1681,10 +1750,15 @@ void Buffer::writeLaTeXSource(otexstream & os, 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"); + // XeTeX with TeX fonts is only safe with ASCII encoding, + // Check here, because the "flavor" is not known in BufferParams::encoding() + // (power users can override this safety measure selecting "utf8-plain"). + if (!params().useNonTeXFonts && (runparams.flavor == OutputParams::XETEX) + && (runparams.encoding->name() != "utf8-plain")) + runparams.encoding = encodings.fromLyXName("ascii"); + // FIXME: when only the current paragraph is shown, this is ignored + // (or not reached) and characters encodable in the current + // encoding are not converted to ASCII-representation. // If we are compiling a file standalone, even if this is the // child of some other buffer, let's cut the link here, so the @@ -1790,7 +1864,7 @@ void Buffer::writeLaTeXSource(otexstream & os, } os << "\\makeatletter\n" << "\\def\\input@path{{" - << docdir << "/}}\n" + << docdir << "}}\n" << "\\makeatother\n"; } } @@ -1850,14 +1924,7 @@ void Buffer::writeLaTeXSource(otexstream & os, } runparams_in.encoding = runparams.encoding; - // Just to be sure. (Asger) - os.texrow().newline(); - - //for (int i = 0; itexrow.rows(); i++) { - // int id,pos; - // if (d->texrow.getIdFromRow(i+1,id,pos) && id>0) - // lyxerr << i+1 << ":" << id << ":" << getParFromID(id).paragraph().asString()<<"\n"; - //} + os.texrow().finalize(); LYXERR(Debug::INFO, "Finished making LaTeX file."); LYXERR(Debug::INFO, "Row count was " << os.texrow().rows() - 1 << '.'); @@ -1894,7 +1961,7 @@ void Buffer::writeDocBookSource(odocstream & os, string const & fname, LaTeXFeatures features(*this, params(), runparams); validate(features); - d->texrow.reset(); + d->texrow.reset(false); DocumentClass const & tclass = params().documentClass(); string const & top_element = tclass.latexname(); @@ -2153,8 +2220,8 @@ void Buffer::validate(LaTeXFeatures & features) const if (!features.runparams().is_child) params().validate(features); - for_each(paragraphs().begin(), paragraphs().end(), - bind(&Paragraph::validate, _1, ref(features))); + for (Paragraph const & p : paragraphs()) + p.validate(features); if (lyxerr.debugging(Debug::LATEX)) { features.showStruct(); @@ -2171,9 +2238,9 @@ void Buffer::getLabelList(vector & list) const } list.clear(); - Toc & toc = d->toc_backend.toc("label"); - TocIterator toc_it = toc.begin(); - TocIterator end = toc.end(); + shared_ptr toc = d->toc_backend.toc("label"); + Toc::const_iterator toc_it = toc->begin(); + Toc::const_iterator end = toc->end(); for (; toc_it != end; ++toc_it) { if (toc_it->depth() == 0) list.push_back(toc_it->str()); @@ -2454,8 +2521,29 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag) break; case LFUN_BUFFER_VIEW_CACHE: - enable = (d->preview_file_).exists(); + (d->preview_file_).refresh(); + enable = (d->preview_file_).exists() && !(d->preview_file_).isFileEmpty(); + break; + + case LFUN_CHANGES_TRACK: + flag.setEnabled(true); + flag.setOnOff(params().track_changes); + break; + + case LFUN_CHANGES_OUTPUT: + flag.setEnabled(true); + flag.setOnOff(params().output_changes); + break; + + case LFUN_BUFFER_TOGGLE_COMPRESSION: { + flag.setOnOff(params().compressed); + break; + } + + case LFUN_BUFFER_TOGGLE_OUTPUT_SYNC: { + flag.setOnOff(params().output_sync); break; + } default: return false; @@ -2686,6 +2774,50 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) dr.setMessage(_("Error viewing the output file.")); break; + case LFUN_CHANGES_TRACK: + if (params().save_transient_properties) + undo().recordUndoBufferParams(CursorData()); + params().track_changes = !params().track_changes; + if (!params().track_changes) + dr.forceChangesUpdate(); + break; + + case LFUN_CHANGES_OUTPUT: + if (params().save_transient_properties) + undo().recordUndoBufferParams(CursorData()); + params().output_changes = !params().output_changes; + if (params().output_changes) { + bool dvipost = LaTeXFeatures::isAvailable("dvipost"); + bool xcolorulem = LaTeXFeatures::isAvailable("ulem") && + LaTeXFeatures::isAvailable("xcolor"); + + if (!dvipost && !xcolorulem) { + Alert::warning(_("Changes not shown in LaTeX output"), + _("Changes will not be highlighted in LaTeX output, " + "because neither dvipost nor xcolor/ulem are installed.\n" + "Please install these packages or redefine " + "\\lyxadded and \\lyxdeleted in the LaTeX preamble.")); + } else if (!xcolorulem) { + Alert::warning(_("Changes not shown in LaTeX output"), + _("Changes will not be highlighted in LaTeX output " + "when using pdflatex, because xcolor and ulem are not installed.\n" + "Please install both packages or redefine " + "\\lyxadded and \\lyxdeleted in the LaTeX preamble.")); + } + } + break; + + case LFUN_BUFFER_TOGGLE_COMPRESSION: + // turn compression on/off + undo().recordUndoBufferParams(CursorData()); + params().compressed = !params().compressed; + break; + + case LFUN_BUFFER_TOGGLE_OUTPUT_SYNC: + undo().recordUndoBufferParams(CursorData()); + params().output_sync = !params().output_sync; + break; + default: dispatched = false; break; @@ -2914,12 +3046,21 @@ string Buffer::filePath() const } -string Buffer::originFilePath() const +DocFileName Buffer::getReferencedFileName(string const & fn) const { - if (FileName::isAbsolute(params().origin)) - return params().origin; + DocFileName result; + if (FileName::isAbsolute(fn) || !FileName::isAbsolute(params().origin)) + result.set(fn, filePath()); + else { + // filePath() ends with a path separator + FileName const test(filePath() + fn); + if (test.exists()) + result.set(fn, filePath()); + else + result.set(fn, params().origin); + } - return filePath(); + return result; } @@ -3449,7 +3590,7 @@ Buffer::References & Buffer::getReferenceCache(docstring const & label) return it->second.second; static InsetLabel const * dummy_il = 0; - static References const dummy_refs; + static References const dummy_refs = References(); it = d->ref_cache_.insert( make_pair(label, make_pair(dummy_il, dummy_refs))).first; return it->second.second; @@ -3517,11 +3658,12 @@ 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, - OutputWhat output, bool master) const +// returns NULL if id-to-row conversion is unsupported +unique_ptr Buffer::getSourceCode(odocstream & os, string const & format, + pit_type par_begin, pit_type par_end, + OutputWhat output, bool master) const { + unique_ptr texrow; OutputParams runparams(¶ms().encoding()); runparams.nice = true; runparams.flavor = params().getOutputFlavor(format); @@ -3575,15 +3717,14 @@ void Buffer::getSourceCode(odocstream & os, string const & format, LaTeXFeatures features(*this, params(), runparams); params().validate(features); runparams.use_polyglossia = features.usePolyglossia(); - TexRow texrow; - texrow.reset(); - texrow.newline(); - texrow.newline(); // latex or literate - otexstream ots(os, texrow); - + otexstream ots(os); + // output above + ots.texrow().newlines(2); // the real stuff latexParagraphs(*this, text(), ots, runparams); + texrow = ots.releaseTexRow(); + texrow->finalize(); // Restore the parenthood if (!master) @@ -3618,15 +3759,17 @@ void Buffer::getSourceCode(odocstream & os, string const & format, writeDocBookSource(os, absFileName(), runparams, output); } else { // latex or literate - d->texrow.reset(); - d->texrow.newline(); - d->texrow.newline(); - otexstream ots(os, d->texrow); + otexstream ots(os); + // output above + ots.texrow().newlines(2); if (master) runparams.is_child = true; writeLaTeXSource(ots, string(), runparams, output); + texrow = ots.releaseTexRow(); + texrow->finalize(); } } + return texrow; } @@ -3709,7 +3852,7 @@ public: /// virtual shared_ptr clone() const { - return shared_ptr(new AutoSaveBuffer(*this)); + return make_shared(*this); } /// int start() @@ -4172,26 +4315,22 @@ Buffer::ExportStatus Buffer::preview(string const & format, bool includeall) con ExportStatus const status = doExport(format, true, false, result_file); FileName const previewFile(result_file); - LATTEST (isClone()); - d->cloned_buffer_->d->preview_file_ = previewFile; - d->cloned_buffer_->d->preview_format_ = format; - d->cloned_buffer_->d->preview_error_ = (status != ExportSuccess); + Impl * theimpl = isClone() ? d->cloned_buffer_->d : d; + theimpl->preview_file_ = previewFile; + theimpl->preview_format_ = format; + theimpl->preview_error_ = (status != ExportSuccess); if (status != ExportSuccess) return status; - if (previewFile.exists()) { - if (!formats.view(*this, previewFile, format)) - return PreviewError; - else - return PreviewSuccess; - } - else { - // Successful export but no output file? - // Probably a bug in error detection. - LATTEST (status != ExportSuccess); - return status; - } + if (previewFile.exists()) + return formats.view(*this, previewFile, format) ? + PreviewSuccess : PreviewError; + + // Successful export but no output file? + // Probably a bug in error detection. + LATTEST(status != ExportSuccess); + return status; } @@ -4452,6 +4591,7 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const // update all caches clearReferenceCache(); updateMacros(); + setChangesPresent(false); Buffer & cbuf = const_cast(*this); @@ -4465,7 +4605,8 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const d->bibinfo_cache_valid_ = true; d->cite_labels_valid_ = true; - cbuf.tocBackend().update(utype == OutputUpdate); + /// FIXME: Perf + cbuf.tocBackend().update(true, utype); if (scope == UpdateMaster) cbuf.structureChanged(); } @@ -4478,6 +4619,11 @@ static depth_type getDepth(DocIterator const & it) if (!it[i].inset().inMathed()) depth += it[i].paragraph().getDepth() + 1; // remove 1 since the outer inset does not count + // we should have at least one non-math inset, so + // depth should nevery be 0. but maybe it is worth + // marking this, just in case. + LATTEST(depth > 0); + // coverity[INTEGER_OVERFLOW] return depth - 1; } @@ -4714,6 +4860,9 @@ void Buffer::updateBuffer(ParIterator & parit, UpdateType utype) const // set the counter for this paragraph d->setLabel(parit, utype); + // update change-tracking flag + parit->addChangesToBuffer(*this); + // now the insets InsetList::const_iterator iit = parit->insetList().begin(); InsetList::const_iterator end = parit->insetList().end(); @@ -4952,12 +5101,23 @@ void Buffer::checkMasterBuffer() string Buffer::includedFilePath(string const & name, string const & ext) const { + if (d->old_position.empty() || + equivalent(FileName(d->old_position), FileName(filePath()))) + return name; + bool isabsolute = FileName::isAbsolute(name); - // old_position already contains a trailing path separator - string const absname = isabsolute ? name : d->old_position + name; + // both old_position and filePath() end with a path separator + string absname = isabsolute ? name : d->old_position + name; - if (d->old_position.empty() || d->old_position == filePath() - || !FileName(addExtension(absname, ext)).exists()) + // if old_position is set to origin, we need to do the equivalent of + // getReferencedFileName() (see readDocument()) + if (!isabsolute && d->old_position == params().origin) { + FileName const test(addExtension(filePath() + name, ext)); + if (test.exists()) + absname = filePath() + name; + } + + if (!FileName(addExtension(absname, ext)).exists()) return name; if (isabsolute) @@ -4967,4 +5127,29 @@ string Buffer::includedFilePath(string const & name, string const & ext) const from_utf8(filePath()))); } + +void Buffer::setChangesPresent(bool b) const +{ + d->tracked_changes_present_ = b; +} + + +bool Buffer::areChangesPresent() const +{ + return d->tracked_changes_present_; +} + + +void Buffer::updateChangesPresent() const +{ + LYXERR(Debug::CHANGES, "Buffer::updateChangesPresent"); + setChangesPresent(false); + ParConstIterator it = par_iterator_begin(); + ParConstIterator const end = par_iterator_end(); + for (; !areChangesPresent() && it != end; ++it) + it->addChangesToBuffer(*this); +} + + + } // namespace lyx