X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBuffer.cpp;h=888fb93d9b16a7dbc19f0186b02ed28e3c730f01;hb=0c7bd9a57f2a308bb9659200eda3b7e45f8d5d3c;hp=3911edb41bb64d5a220ab4dff18859fc171a3021;hpb=01691cde49aac9579a1931d2de48e582c93a2363;p=lyx.git diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 3911edb41b..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; @@ -219,7 +219,13 @@ public: mutable TocBackend toc_backend; /// macro tables - typedef pair ScopeMacro; + struct ScopeMacro { + ScopeMacro() {} + ScopeMacro(DocIterator const & s, MacroData const & m) + : scope(s), macro(m) {} + DocIterator scope; + MacroData macro; + }; typedef map PositionScopeMacroMap; typedef map NamePositionScopeMacroMap; /// map from the macro name to the position map, @@ -230,13 +236,29 @@ public: /// positions of child buffers in the buffer typedef map BufferPositionMap; - typedef pair ScopeBuffer; + struct ScopeBuffer { + ScopeBuffer() : buffer(0) {} + ScopeBuffer(DocIterator const & s, Buffer const * b) + : scope(s), buffer(b) {} + DocIterator scope; + Buffer const * buffer; + }; typedef map PositionScopeBufferMap; /// position of children buffers in this buffer BufferPositionMap children_positions; /// map from children inclusion positions to their scope and their buffer PositionScopeBufferMap position_to_children; + /// Contains the old buffer filePath() while saving-as, or the + /// directory where the document was last saved while loading. + string old_position; + + /** Keeps track of the path of local layout files. + * If possible, it is always relative to the buffer path. + * Empty for layouts in system or user directory. + */ + string layout_position; + /// Container for all sort of Buffer dependant errors. map errorLists; @@ -274,6 +296,15 @@ public: /// to be updated. mutable bool cite_labels_valid_; + /// These two hold the file name and format, written to by + /// Buffer::preview and read from by LFUN_BUFFER_VIEW_CACHE. + FileName preview_file_; + string preview_format_; + /// If there was an error when previewing, on the next preview we do + /// a fresh compile (e.g. in case the user installed a package that + /// was missing). + bool preview_error_; + mutable RefCache ref_cache_; /// our Text that should be wrapped in an InsetText @@ -340,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; @@ -355,7 +390,7 @@ private: static FileName createBufferTmpDir() { // FIXME This would be the ideal application for a TempDir class (like - // TempFile but for directories) + // TempFile but for directories) string counter; { static int count; @@ -382,12 +417,13 @@ 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), preview_loader_(0), - cloned_buffer_(cloned_buffer), clone_list_(0), - doing_export(false), parent_buffer(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) { if (!cloned_buffer_) { temppath = createBufferTmpDir(); @@ -407,6 +443,11 @@ Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_, cite_labels_valid_ = cloned_buffer_->d->cite_labels_valid_; unnamed = cloned_buffer_->d->unnamed; internal_buffer = cloned_buffer_->d->internal_buffer; + layout_position = cloned_buffer_->d->layout_position; + 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_; } @@ -425,7 +466,6 @@ Buffer::Buffer(string const & file, bool readonly, Buffer const * cloned_buffer) it.paragraph().setId(cloned_it.paragraph().id()); } else d->inset = new InsetText(this); - d->inset->setAutoBreakRows(true); d->inset->getText(0)->setMacrocontextPosition(par_iterator_begin()); } @@ -483,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()) { @@ -499,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(); @@ -558,7 +601,7 @@ void Buffer::cloneWithChildren(BufferMap & bufmap, CloneList * clones) const for (; it != end; ++it) { DocIterator dit = it->first.clone(buffer_clone); dit.setBuffer(buffer_clone); - Buffer * child = const_cast(it->second.second); + Buffer * child = const_cast(it->second.buffer); child->cloneWithChildren(bufmap, clones); BufferMap::iterator const bit = bufmap.find(child); @@ -775,7 +818,7 @@ string Buffer::logName(LogType * type) const // Also consider the master buffer log file FileName masterfname = fname; - LogType mtype; + LogType mtype = latexlog; if (masterBuffer() != this) { string const mlogfile = masterBuffer()->logName(&mtype); masterfname = FileName(mlogfile); @@ -895,12 +938,11 @@ int Buffer::readHeader(Lexer & lex) LYXERR(Debug::PARSER, "Handling document header token: `" << token << '\''); - string unknown = params().readToken(lex, token, d->filename.onlyPath()); - if (!unknown.empty()) { - if (unknown[0] != '\\' && token == "\\textclass") { - Alert::warning(_("Unknown document class"), - bformat(_("Using the default document class, because the " - "class %1$s is unknown."), from_utf8(unknown))); + string const result = + params().readToken(lex, token, d->filename.onlyPath()); + if (!result.empty()) { + if (token == "\\textclass") { + d->layout_position = result; } else { ++unknown_tokens; docstring const s = bformat(_("Unknown token: " @@ -997,7 +1039,12 @@ bool Buffer::readDocument(Lexer & lex) params().indiceslist().addDefault(B_("Index")); // read main text + 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(); // inform parent buffer about local macros if (parent()) { @@ -1105,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 @@ -1143,6 +1195,12 @@ void Buffer::setFullyLoaded(bool value) } +bool Buffer::lastPreviewError() const +{ + return d->preview_error_; +} + + PreviewLoader * Buffer::loader() const { if (!isExporting() && lyxrc.preview == LyXRC::PREVIEW_OFF) @@ -1261,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 { @@ -1294,16 +1387,19 @@ bool Buffer::save() const // if the file does not yet exist, none of the backup activity // that follows is necessary - if (!fileName().exists()) - return writeFile(fileName()); + if (!fileName().exists()) { + if (!writeFile(fileName())) + return false; + markClean(); + return true; + } // we first write the file to a new name, then move it to its // 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); @@ -1316,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 " << @@ -1339,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; } } @@ -1355,22 +1464,25 @@ 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. Alert::error(_("Write failure"), - bformat(_("The file has successfully been saved as:\n %1$s.\n" - "But LyX could not move it to:\n %2$s.\n" - "Your original file has been backed up to:\n %3$s"), - from_utf8(savefile.absFileName()), - from_utf8(fileName().absFileName()), - from_utf8(backupName.absFileName()))); + bformat(_("The file has successfully been saved as:\n %1$s.\n" + "But LyX could not move it to:\n %2$s.\n" + "Your original file has been backed up to:\n %3$s"), + from_utf8(savefile.absFileName()), + from_utf8(fileName().absFileName()), + from_utf8(backupName.absFileName()))); } else { // either we did not try to make a backup, or else we tried and failed, // or else the original file was a symlink, in which case it was copied, @@ -1509,7 +1621,7 @@ bool Buffer::write(ostream & ofs) const // now write out the buffer parameters. ofs << "\\begin_header\n"; - params().writeFile(ofs); + params().writeFile(ofs, this); ofs << "\\end_header\n"; // write the text @@ -1548,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()); @@ -1571,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 @@ -1581,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) { @@ -1612,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; @@ -1635,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 @@ -1744,7 +1864,7 @@ void Buffer::writeLaTeXSource(otexstream & os, } os << "\\makeatletter\n" << "\\def\\input@path{{" - << docdir << "/}}\n" + << docdir << "}}\n" << "\\makeatother\n"; } } @@ -1804,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 << '.'); @@ -1848,10 +1961,10 @@ 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(); + string const & top_element = tclass.latexname(); bool const output_preamble = output == FullSource || output == OnlyPreamble; @@ -2107,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(); @@ -2125,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()); @@ -2400,7 +2513,6 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag) case LFUN_BRANCH_ADD: case LFUN_BRANCHES_RENAME: - case LFUN_BUFFER_PRINT: // if no Buffer is present, then of course we won't be called! break; @@ -2408,6 +2520,31 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag) enable = !isReadonly(); break; + case LFUN_BUFFER_VIEW_CACHE: + (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; } @@ -2505,7 +2642,8 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) // Execute the command in the background Systemcall call; - call.startscript(Systemcall::DontWait, command, filePath()); + call.startscript(Systemcall::DontWait, command, + filePath(), layoutPos()); break; } @@ -2630,122 +2768,55 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) break; } - case LFUN_BUFFER_PRINT: { - // we'll assume there's a problem until we succeed - dr.setError(true); - string target = func.getArg(0); - string target_name = func.getArg(1); - string command = func.getArg(2); - - if (target.empty() - || target_name.empty() - || command.empty()) { - LYXERR0("Unable to parse " << func.argument()); - docstring const msg = - bformat(_("Unable to parse \"%1$s\""), func.argument()); - dr.setMessage(msg); - break; - } - if (target != "printer" && target != "file") { - LYXERR0("Unrecognized target \"" << target << '"'); - docstring const msg = - bformat(_("Unrecognized target \"%1$s\""), from_utf8(target)); - dr.setMessage(msg); - break; - } - - if (doExport("dvi", true) != ExportSuccess) { - showPrintError(absFileName()); - dr.setMessage(_("Error exporting to DVI.")); - break; - } + case LFUN_BUFFER_VIEW_CACHE: + if (!formats.view(*this, d->preview_file_, + d->preview_format_)) + dr.setMessage(_("Error viewing the output file.")); + break; - // Push directory path. - string const path = temppath(); - // Prevent the compiler from optimizing away p - FileName pp(path); - PathChanger p(pp); - - // there are three cases here: - // 1. we print to a file - // 2. we print directly to a printer - // 3. we print using a spool command (print to file first) - Systemcall one; - int res = 0; - string const dviname = changeExtension(latexName(true), "dvi"); - - if (target == "printer") { - if (!lyxrc.print_spool_command.empty()) { - // case 3: print using a spool - string const psname = changeExtension(dviname,".ps"); - command += ' ' + lyxrc.print_to_file - + quoteName(psname) - + ' ' - + quoteName(dviname); - - string command2 = lyxrc.print_spool_command + ' '; - if (target_name != "default") { - command2 += lyxrc.print_spool_printerprefix - + target_name - + ' '; - } - command2 += quoteName(psname); - // First run dvips. - // If successful, then spool command - res = one.startscript(Systemcall::Wait, command, - filePath()); - - if (res == 0) { - // If there's no GUI, we have to wait on this command. Otherwise, - // LyX deletes the temporary directory, and with it the spooled - // file, before it can be printed!! - Systemcall::Starttype stype = use_gui ? - Systemcall::DontWait : Systemcall::Wait; - res = one.startscript(stype, command2, - filePath()); - } - } else { - // case 2: print directly to a printer - if (target_name != "default") - command += ' ' + lyxrc.print_to_printer + target_name + ' '; - // as above.... - Systemcall::Starttype stype = use_gui ? - Systemcall::DontWait : Systemcall::Wait; - res = one.startscript(stype, command + - quoteName(dviname), filePath()); - } + 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; - } else { - // case 1: print to a file - FileName const filename(makeAbsPath(target_name, filePath())); - FileName const dvifile(makeAbsPath(dviname, path)); - if (filename.exists()) { - docstring text = bformat( - _("The file %1$s already exists.\n\n" - "Do you want to overwrite that file?"), - makeDisplayPath(filename.absFileName())); - if (Alert::prompt(_("Overwrite file?"), - text, 0, 1, _("&Overwrite"), _("&Cancel")) != 0) - break; + 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.")); } - command += ' ' + lyxrc.print_to_file - + quoteName(filename.toFilesystemEncoding()) - + ' ' - + quoteName(dvifile.toFilesystemEncoding()); - // as above.... - Systemcall::Starttype stype = use_gui ? - Systemcall::DontWait : Systemcall::Wait; - res = one.startscript(stype, command, filePath()); } + break; - if (res == 0) - dr.setError(false); - else { - dr.setMessage(_("Error running external commands.")); - showPrintError(absFileName()); - } + 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; @@ -2975,6 +3046,47 @@ string Buffer::filePath() const } +DocFileName Buffer::getReferencedFileName(string const & fn) const +{ + 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 result; +} + + +string Buffer::layoutPos() const +{ + return d->layout_position; +} + + +void Buffer::setLayoutPos(string const & path) +{ + if (path.empty()) { + d->layout_position.clear(); + return; + } + + LATTEST(FileName::isAbsolute(path)); + + d->layout_position = + to_utf8(makeRelPath(from_utf8(path), from_utf8(filePath()))); + + if (d->layout_position.empty()) + d->layout_position = "."; +} + + bool Buffer::isReadonly() const { return d->read_only; @@ -3121,12 +3233,12 @@ MacroData const * Buffer::Impl::getBufferMacro(docstring const & name, if (it != nameIt->second.end()) { while (true) { // scope ends behind pos? - if (pos < it->second.first) { + if (pos < it->second.scope) { // Looks good, remember this. If there // is no external macro behind this, // we found the right one already. bestPos = it->first; - bestData = &it->second.second; + bestData = &it->second.macro; break; } @@ -3151,13 +3263,13 @@ MacroData const * Buffer::Impl::getBufferMacro(docstring const & name, break; // scope ends behind pos? - if (pos < it->second.first + if (pos < it->second.scope && (cloned_buffer_ || - theBufferList().isLoaded(it->second.second))) { + theBufferList().isLoaded(it->second.buffer))) { // look for macro in external file macro_lock = true; MacroData const * data - = it->second.second->getMacro(name, false); + = it->second.buffer->getMacro(name, false); macro_lock = false; if (data) { bestPos = it->first; @@ -3322,7 +3434,7 @@ void Buffer::Impl::updateMacros(DocIterator & it, DocIterator & scope) continue; // register macro - // FIXME (Abdel), I don't understandt why we pass 'it' here + // FIXME (Abdel), I don't understand why we pass 'it' here // instead of 'macroTemplate' defined above... is this correct? macros[macroTemplate.name()][it] = Impl::ScopeMacro(scope, MacroData(const_cast(owner_), it)); @@ -3478,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; @@ -3546,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); @@ -3604,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) @@ -3632,7 +3744,7 @@ void Buffer::getSourceCode(odocstream & os, string const & format, if (output == FullSource) write(ods); else if (output == OnlyPreamble) - params().writeFile(ods); + params().writeFile(ods, this); else if (output == OnlyBody) text().write(ods); os << from_utf8(ods.str()); @@ -3647,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; } @@ -3738,7 +3852,7 @@ public: /// virtual shared_ptr clone() const { - return shared_ptr(new AutoSaveBuffer(*this)); + return make_shared(*this); } /// int start() @@ -4106,12 +4220,10 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir d->cloned_buffer_->d->errorLists[error_type] = d->errorLists[error_type]; } - if (!success) - return ExportConverterError; if (put_in_tempdir) { result_file = tmp_result_file.absFileName(); - return ExportSuccess; + return success ? ExportSuccess : ExportConverterError; } if (dest_filename.empty()) @@ -4176,7 +4288,7 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir formats.prettyName(format))); } - return ExportSuccess; + return success ? ExportSuccess : ExportConverterError; } @@ -4201,11 +4313,24 @@ Buffer::ExportStatus Buffer::preview(string const & format, bool includeall) con } // (2) export with included children only ExportStatus const status = doExport(format, true, false, result_file); + FileName const previewFile(result_file); + + 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 (!formats.view(*this, FileName(result_file), format)) - return PreviewError; - return PreviewSuccess; + + 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; } @@ -4440,9 +4565,9 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const if (master != this) { bufToUpdate.insert(this); master->updateBuffer(UpdateMaster, utype); - // If the master buffer has no gui associated with it, then the TocModel is - // not updated during the updateBuffer call and TocModel::toc_ is invalid - // (bug 5699). The same happens if the master buffer is open in a different + // If the master buffer has no gui associated with it, then the TocModel is + // not updated during the updateBuffer call and TocModel::toc_ is invalid + // (bug 5699). The same happens if the master buffer is open in a different // window. This test catches both possibilities. // See: http://marc.info/?l=lyx-devel&m=138590578911716&w=2 // There remains a problem here: If there is another child open in yet a third @@ -4466,6 +4591,7 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const // update all caches clearReferenceCache(); updateMacros(); + setChangesPresent(false); Buffer & cbuf = const_cast(*this); @@ -4479,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(); } @@ -4492,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; } @@ -4728,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(); @@ -4750,13 +4885,13 @@ int Buffer::spellCheck(DocIterator & from, DocIterator & to, DocIterator const end = to_end ? doc_iterator_end(this) : to; // OK, we start from here. for (; from != end; from.forwardPos()) { - // We are only interested in text so remove the math CursorSlice. - while (from.inMathed()) { + // This skips all insets with spell check disabled. + while (!from.allowSpellCheck()) { from.pop_back(); from.pos()++; } // If from is at the end of the document (which is possible - // when leaving the mathed) LyX will crash later otherwise. + // when "from" was changed above) LyX will crash later otherwise. if (from.atEnd() || (!to_end && from >= end)) break; to = from; @@ -4766,7 +4901,6 @@ int Buffer::spellCheck(DocIterator & from, DocIterator & to, word_lang = wl; break; } - // Do not increase progress when from == to, otherwise the word // count will be wrong. if (from != to) { @@ -4884,6 +5018,8 @@ bool Buffer::saveAs(FileName const & fn) FileName const old_name = fileName(); FileName const old_auto = getAutosaveFileName(); bool const old_unnamed = isUnnamed(); + bool success = true; + d->old_position = filePath(); setFileName(fn); markDirty(); @@ -4901,22 +5037,19 @@ bool Buffer::saveAs(FileName const & fn) // are still valid. checkChildBuffers(); checkMasterBuffer(); - return true; } else { // save failed // reset the old filename and unnamed state setFileName(old_name); setUnnamed(old_unnamed); - return false; + success = false; } + + d->old_position.clear(); + return success; } -// FIXME We could do better here, but it is complicated. What would be -// nice is to offer either (a) to save the child buffer to an appropriate -// location, so that it would "move with the master", or else (b) to update -// the InsetInclude so that it pointed to the same file. But (a) is a bit -// complicated, because the code for this lives in GuiView. void Buffer::checkChildBuffers() { Impl::BufferPositionMap::iterator it = d->children_positions.begin(); @@ -4936,11 +5069,6 @@ void Buffer::checkChildBuffers() if (oldloc == newloc) continue; // the location of the child file is incorrect. - Alert::warning(_("Included File Invalid"), - bformat(_("Saving this document to a new location has made the file:\n" - " %1$s\n" - "inaccessible. You will need to update the included filename."), - from_utf8(oldloc))); cbuf->setParent(0); inset_inc->setChildBuffer(0); } @@ -4970,4 +5098,58 @@ void Buffer::checkMasterBuffer() setParent(0); } + +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); + // both old_position and filePath() end with a path separator + string absname = isabsolute ? name : d->old_position + name; + + // 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) + return to_utf8(makeRelPath(from_utf8(name), from_utf8(filePath()))); + + return to_utf8(makeRelPath(from_utf8(FileName(absname).realPath()), + 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