X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBuffer.cpp;h=0f37edd9dcb1868dbad7bce1159a87a1172b8e60;hb=44e86aa4e9a602c90a5f96b4c716f28cd2582192;hp=530200caa08e5290366927f0dc16c4a3f81b978f;hpb=cc1ce047984292b0509043f82165c506d15eb020;p=lyx.git diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 530200caa0..0f37edd9dc 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -25,9 +25,10 @@ #include "Converter.h" #include "Counters.h" #include "Cursor.h" +#include "CutAndPaste.h" #include "DispatchResult.h" #include "DocIterator.h" -#include "Encoding.h" +#include "BufferEncodings.h" #include "ErrorList.h" #include "Exporter.h" #include "Format.h" @@ -57,7 +58,6 @@ #include "SpellChecker.h" #include "sgml.h" #include "TexRow.h" -#include "TexStream.h" #include "Text.h" #include "TextClass.h" #include "TocBackend.h" @@ -97,10 +97,12 @@ #include "support/gzstream.h" #include "support/lstrings.h" #include "support/lyxalgo.h" +#include "support/mutex.h" #include "support/os.h" #include "support/Package.h" #include "support/PathChanger.h" #include "support/Systemcall.h" +#include "support/TempFile.h" #include "support/textutils.h" #include "support/types.h" @@ -131,14 +133,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 @@ -217,7 +211,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, @@ -228,13 +228,29 @@ public: /// positions of child buffers in the buffer typedef map BufferPositionMap; - typedef pair ScopeBuffer; + struct ScopeBuffer { + ScopeBuffer() {} + 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; @@ -272,6 +288,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 @@ -321,7 +346,7 @@ public: CloneList * clone_list_; /// are we in the process of exporting this buffer? mutable bool doing_export; - + /// compute statistics /// \p from initial position /// \p to points to the end position @@ -352,12 +377,20 @@ private: /// Creates the per buffer temporary directory static FileName createBufferTmpDir() { - static int count; + // FIXME This would be the ideal application for a TempDir class (like + // TempFile but for directories) + string counter; + { + static int count; + static Mutex mutex; + Mutex::Locker locker(&mutex); + counter = convert(count++); + } // We are in our own directory. Why bother to mangle name? // In fact I wrote this code to circumvent a problematic behaviour // (bug?) of EMX mkstemp(). FileName tmpfl(package().temp_dir().absFileName() + "/lyx_tmpbuf" + - convert(count++)); + counter); if (!tmpfl.createDirectory(0777)) { throw ExceptionMessage(WarningException, _("Disk Error: "), bformat( @@ -375,9 +408,10 @@ Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_, 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), + cite_labels_valid_(false), inset(0), preview_loader_(0), cloned_buffer_(cloned_buffer), clone_list_(0), - doing_export(false), parent_buffer(0) + doing_export(false), parent_buffer(0), + word_count_(0), char_count_(0), blank_count_(0) { if (!cloned_buffer_) { temppath = createBufferTmpDir(); @@ -397,6 +431,10 @@ 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_; } @@ -415,7 +453,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()); } @@ -450,13 +487,17 @@ Buffer::~Buffer() // if we're the master buffer, then we should get rid of the list // of clones if (!parent()) { - // if this is not empty, we have leaked something. worse, one of the - // children still has a reference to this list. - LASSERT(d->clone_list_->empty(), /* */); + // If this is not empty, we have leaked something. Worse, one of the + // children still has a reference to this list. But we will try to + // continue, rather than shut down. + LATTEST(d->clone_list_->empty()); list::iterator it = find(cloned_buffers.begin(), cloned_buffers.end(), d->clone_list_); - LASSERT(it != cloned_buffers.end(), /* */); - cloned_buffers.erase(it); + if (it == cloned_buffers.end()) { + // We will leak in this case, but it is safe to continue. + LATTEST(false); + } else + cloned_buffers.erase(it); delete d->clone_list_; } // FIXME Do we really need to do this right before we delete d? @@ -520,11 +561,21 @@ void Buffer::cloneWithChildren(BufferMap & bufmap, CloneList * clones) const return; Buffer * buffer_clone = new Buffer(fileName().absFileName(), false, this); + + // The clone needs its own DocumentClass, since running updateBuffer() will + // modify it, and we would otherwise be sharing it with the original Buffer. + buffer_clone->params().makeDocumentClass(true); + ErrorList el; + cap::switchBetweenClasses( + params().documentClassPtr(), buffer_clone->params().documentClassPtr(), + static_cast(buffer_clone->inset()), el); + bufmap[this] = buffer_clone; clones->insert(buffer_clone); buffer_clone->d->clone_list_ = clones; 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. @@ -534,7 +585,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); @@ -558,8 +609,18 @@ Buffer * Buffer::cloneBufferOnly() const { cloned_buffers.push_back(new CloneList); CloneList * clones = cloned_buffers.back(); Buffer * buffer_clone = new Buffer(fileName().absFileName(), false, this); + + // The clone needs its own DocumentClass, since running updateBuffer() will + // modify it, and we would otherwise be sharing it with the original Buffer. + buffer_clone->params().makeDocumentClass(true); + ErrorList el; + cap::switchBetweenClasses( + params().documentClassPtr(), buffer_clone->params().documentClassPtr(), + static_cast(buffer_clone->inset()), el); + clones->insert(buffer_clone); buffer_clone->d->clone_list_ = clones; + // we won't be cloning the children buffer_clone->d->children_positions.clear(); return buffer_clone; @@ -581,7 +642,7 @@ void Buffer::changed(bool update_metrics) const frontend::WorkAreaManager & Buffer::workAreaManager() const { - LASSERT(d->wa_, /**/); + LBUFERR(d->wa_); return *d->wa_; } @@ -610,6 +671,28 @@ BufferParams const & Buffer::params() const } +BufferParams const & Buffer::masterParams() const +{ + if (masterBuffer() == this) + return params(); + + BufferParams & mparams = const_cast(masterBuffer())->params(); + // Copy child authors to the params. We need those pointers. + AuthorList const & child_authors = params().authors(); + AuthorList::Authors::const_iterator it = child_authors.begin(); + for (; it != child_authors.end(); ++it) + mparams.authors().record(*it); + return mparams; +} + + +double Buffer::fontScalingFactor() const +{ + return isExporting() ? 75.0 * params().html_math_img_scale + : 0.01 * lyxrc.dpi * lyxrc.zoom * lyxrc.preview_scale_factor * params().display_pixel_ratio; +} + + ParagraphList & Buffer::paragraphs() { return text().paragraphs(); @@ -719,7 +802,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); @@ -810,7 +893,8 @@ int Buffer::readHeader(Lexer & lex) params().html_latex_end.clear(); params().html_math_img_scale = 1.0; params().output_sync_macro.erase(); - params().local_layout.clear(); + params().setLocalLayout(string(), false); + params().setLocalLayout(string(), true); for (int i = 0; i < 4; ++i) { params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i]; @@ -838,12 +922,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: " @@ -886,7 +969,7 @@ bool Buffer::readDocument(Lexer & lex) readHeader(lex); - if (params().outputChanges) { + if (params().output_changes) { bool dvipost = LaTeXFeatures::isAvailable("dvipost"); bool xcolorulem = LaTeXFeatures::isAvailable("ulem") && LaTeXFeatures::isAvailable("xcolor"); @@ -906,7 +989,7 @@ bool Buffer::readDocument(Lexer & lex) } } - if (!params().master.empty()) { + if (!parent() && !params().master.empty()) { FileName const master_file = makeAbsPath(params().master, onlyPath(absFileName())); if (isLyXFileName(master_file.absFileName())) { @@ -940,7 +1023,9 @@ bool Buffer::readDocument(Lexer & lex) params().indiceslist().addDefault(B_("Index")); // read main text + d->old_position = originFilePath(); bool const res = text().read(lex, errorList, d->inset); + d->old_position.clear(); // inform parent buffer about local macros if (parent()) { @@ -957,6 +1042,50 @@ bool Buffer::readDocument(Lexer & lex) } +bool Buffer::importString(string const & format, docstring const & contents, ErrorList & errorList) +{ + Format const * fmt = formats.getFormat(format); + if (!fmt) + return false; + // It is important to use the correct extension here, since some + // converters create a wrong output file otherwise (e.g. html2latex) + TempFile const tempfile("Buffer_importStringXXXXXX." + fmt->extension()); + FileName const name(tempfile.name()); + ofdocstream os(name.toFilesystemEncoding().c_str()); + // Do not convert os implicitly to bool, since that is forbidden in C++11. + bool const success = !(os << contents).fail(); + os.close(); + + bool converted = false; + if (success) { + params().compressed = false; + + // remove dummy empty par + paragraphs().clear(); + + converted = importFile(format, name, errorList); + } + + if (name.exists()) + name.removeFile(); + return converted; +} + + +bool Buffer::importFile(string const & format, FileName const & name, ErrorList & errorList) +{ + if (!theConverters().isReachable(format, "lyx")) + return false; + + TempFile const tempfile("Buffer_importFileXXXXXX.lyx"); + FileName const lyx(tempfile.name()); + if (theConverters().convert(0, name, lyx, name, format, "lyx", errorList)) + return readFile(lyx) == ReadSuccess; + + return false; +} + + bool Buffer::readString(string const & s) { params().compressed = false; @@ -964,25 +1093,25 @@ bool Buffer::readString(string const & s) Lexer lex; istringstream is(s); lex.setStream(is); - FileName const fn = FileName::tempName("Buffer_readString"); + TempFile tempfile("Buffer_readStringXXXXXX.lyx"); + FileName const fn = tempfile.name(); int file_format; - ReadStatus const ret_plf = parseLyXFormat(lex, fn, file_format); - if (ret_plf != ReadSuccess) - return ret_plf; + bool success = parseLyXFormat(lex, fn, file_format) == ReadSuccess; - if (file_format != LYX_FORMAT) { + if (success && file_format != LYX_FORMAT) { // We need to call lyx2lyx, so write the input to a file ofstream os(fn.toFilesystemEncoding().c_str()); os << s; os.close(); // lyxvc in readFile - return readFile(fn) == ReadSuccess; + if (readFile(fn) != ReadSuccess) + success = false; } - - if (readDocument(lex)) - return false; - return true; + else if (success) + if (readDocument(lex)) + success = false; + return success; } @@ -1042,6 +1171,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) @@ -1102,7 +1237,9 @@ Buffer::ReadStatus Buffer::parseLyXFormat(Lexer & lex, Buffer::ReadStatus Buffer::convertLyXFormat(FileName const & fn, FileName & tmpfile, int from_format) { - tmpfile = FileName::tempName("Buffer_convertLyXFormat"); + TempFile tempfile("Buffer_convertLyXFormatXXXXXX.lyx"); + tempfile.setAutoRemove(false); + tmpfile = tempfile.name(); if(tmpfile.empty()) { Alert::error(_("Conversion failed"), bformat(_("%1$s is from a different" @@ -1189,12 +1326,36 @@ bool Buffer::save() const // We don't need autosaves in the immediate future. (Asger) resetAutosaveTimers(); - FileName backupName; - bool madeBackup = false; + // if the file does not yet exist, none of the backup activity + // that follows is necessary + 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")); + bool const symlink = fileName().isSymLink(); + if (!symlink) + tempfile->setAutoRemove(false); + + FileName savefile(tempfile->name()); + LYXERR(Debug::FILES, "Saving to " << savefile.absFileName()); + if (!writeFile(savefile)) + return false; + + // we will set this to false if we fail + bool made_backup = true; - // make a backup if the file already exists - if (lyxrc.make_backup && fileName().exists()) { - backupName = FileName(absFileName() + '~'); + FileName backupName(absFileName() + '~'); + if (lyxrc.make_backup) { if (!lyxrc.backupdir_path.empty()) { string const mangledName = subst(subst(backupName.absFileName(), '/', '!'), ':', '!'); @@ -1202,14 +1363,15 @@ bool Buffer::save() const mangledName)); } + LYXERR(Debug::FILES, "Backing up original file to " << + backupName.absFileName()); // 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); + made_backup = symlink ? + fileName().copyTo(backupName): + fileName().moveTo(backupName); - if (!madeBackup) { + if (!made_backup) { Alert::error(_("Backup failure"), bformat(_("Cannot create backup file %1$s.\n" "Please check whether the directory exists and is writable."), @@ -1218,25 +1380,51 @@ bool Buffer::save() const } } - if (writeFile(d->filename)) { + // Destroy tempfile since it keeps the file locked on windows (bug 9234) + // Only do this if tempfile is not in autoremove mode + if (!symlink) + tempfile.reset(); + // If we have no symlink, we can simply rename the temp file. + // Otherwise, we need to copy it so the symlink stays intact. + if (made_backup && symlink ? savefile.copyTo(fileName(), true) : + savefile.moveTo(fileName())) + { + // saveCheckSum() was already called by writeFile(), but the + // time stamp is invalidated by copying/moving + saveCheckSum(); markClean(); 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 + // 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()))); } else { - // Saving failed, so backup is not backup - if (madeBackup) - backupName.moveTo(d->filename); - return false; + // 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, + // not moved. so the original file is intact. + Alert::error(_("Write failure"), + bformat(_("Cannot move saved file to:\n %1$s.\n" + "But the file has successfully been saved as:\n %2$s."), + from_utf8(fileName().absFileName()), + from_utf8(savefile.absFileName()))); } + return false; } 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; @@ -1338,6 +1526,8 @@ 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 + // Important: Keep the version formatting in sync with lyx2lyx and + // tex2lyx (bug 7951) ofs << "#LyX " << lyx_version_major << "." << lyx_version_minor << " created this file. For more info see http://www.lyx.org/\n" << "\\lyxformat " << LYX_FORMAT << "\n" @@ -1357,7 +1547,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 @@ -1374,7 +1564,7 @@ bool Buffer::write(ostream & ofs) const // how to check if close went ok? // Following is an attempt... (BE 20001011) - // good() returns false if any error occured, including some + // good() returns false if any error occurred, including some // formatting error. // bad() returns true if something bad happened in the buffer, // which should include file system full errors. @@ -1416,7 +1606,6 @@ bool Buffer::makeLaTeXFile(FileName const & fname, if (!openFileWrite(ofs, fname)) return false; - //TexStream ts(ofs.rdbuf(), &texrow()); ErrorList & errorList = d->errorLists["Export"]; errorList.clear(); bool failed_export = false; @@ -1434,13 +1623,12 @@ bool Buffer::makeLaTeXFile(FileName const & fname, writeLaTeXSource(os, original_path, runparams, output); } catch (EncodingException const & e) { - odocstringstream ods; - ods.put(e.failed_char); + docstring const failed(1, e.failed_char); ostringstream oss; oss << "0x" << hex << e.failed_char << dec; docstring msg = bformat(_("Could not find LaTeX command for character '%1$s'" " (code point %2$s)"), - ods.str(), from_utf8(oss.str())); + failed, from_utf8(oss.str())); errorList.push_back(ErrorItem(msg, _("Some characters of your document are probably not " "representable in the chosen encoding.\n" "Changing the document encoding to utf8 could help."), @@ -1468,7 +1656,10 @@ bool Buffer::makeLaTeXFile(FileName const & fname, lyxerr << "File '" << fname << "' was not closed properly." << endl; } - errors("Export"); + if (runparams_in.silent) + errorList.clear(); + else + errors("Export"); return !failed_export; } @@ -1482,6 +1673,11 @@ 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"); + // 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 // file is really independent and no concurring settings from @@ -1490,13 +1686,15 @@ void Buffer::writeLaTeXSource(otexstream & os, d->ignore_parent = true; // Classify the unicode characters appearing in math insets - Encodings::initUnicodeMath(*this); + BufferEncodings::initUnicodeMath(*this); // validate the buffer. LYXERR(Debug::LATEX, " Validating buffer..."); LaTeXFeatures features(*this, params(), runparams); validate(features); - runparams.use_polyglossia = features.usePolyglossia(); + // This is only set once per document (in master) + if (!runparams.is_child) + runparams.use_polyglossia = features.usePolyglossia(); LYXERR(Debug::LATEX, " Buffer validation done."); bool const output_preamble = @@ -1584,7 +1782,7 @@ void Buffer::writeLaTeXSource(otexstream & os, } os << "\\makeatletter\n" << "\\def\\input@path{{" - << docdir << "/}}\n" + << docdir << "}}\n" << "\\makeatother\n"; } } @@ -1598,7 +1796,9 @@ void Buffer::writeLaTeXSource(otexstream & os, runparams.use_babel = params().writeLaTeX(os, features, d->filename.onlyPath()); - runparams.use_japanese = features.isRequired("japanese"); + // Japanese might be required only in some children of a document, + // but once required, we must keep use_japanese true. + runparams.use_japanese |= features.isRequired("japanese"); if (!output_body) { // Restore the parenthood if needed @@ -1689,7 +1889,7 @@ void Buffer::writeDocBookSource(odocstream & os, string const & fname, d->texrow.reset(); 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; @@ -1796,7 +1996,7 @@ void Buffer::writeLyXHTMLSource(odocstream & os, if (output_preamble) { os << "\n" - << "\n" + << "\n" // FIXME Language should be set properly. << "\n" << "\n" @@ -1844,7 +2044,7 @@ void Buffer::writeLyXHTMLSource(odocstream & os, << ";\n"; css << "}\n"; } - + docstring const dstyles = css.str(); if (!dstyles.empty()) { bool written = false; @@ -1879,7 +2079,9 @@ void Buffer::writeLyXHTMLSource(odocstream & os, if (output_body_tag) os << "\n"; XHTMLStream xs(os); - params().documentClass().counters().reset(); + if (output != IncludedFile) + // if we're an included file, the counters are in the master. + params().documentClass().counters().reset(); xhtmlParagraphs(text(), *this, xs, runparams); if (output_body_tag) os << "\n"; @@ -1918,7 +2120,7 @@ int Buffer::runChktex() if (res == -1) { Alert::error(_("chktex failure"), _("Could not run chktex successfully.")); - } else if (res > 0) { + } else { ErrorList & errlist = d->errorLists["ChkTeX"]; errlist.clear(); bufferErrors(terr, errlist); @@ -1926,7 +2128,10 @@ int Buffer::runChktex() setBusy(false); - errors("ChkTeX"); + if (runparams.silent) + d->errorLists["ChkTeX"].clear(); + else + errors("ChkTeX"); return res; } @@ -1958,9 +2163,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"); + TocIterator toc_it = toc->begin(); + TocIterator end = toc->end(); for (; toc_it != end; ++toc_it) { if (toc_it->depth() == 0) list.push_back(toc_it->str()); @@ -2179,57 +2384,74 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag) switch (cmd.action()) { - case LFUN_BUFFER_TOGGLE_READ_ONLY: - flag.setOnOff(isReadonly()); - break; + case LFUN_BUFFER_TOGGLE_READ_ONLY: + flag.setOnOff(isReadonly()); + break; // FIXME: There is need for a command-line import. //case LFUN_BUFFER_IMPORT: - case LFUN_BUFFER_AUTO_SAVE: - break; + case LFUN_BUFFER_AUTO_SAVE: + break; - case LFUN_BUFFER_EXPORT_CUSTOM: - // FIXME: Nothing to check here? - break; + case LFUN_BUFFER_EXPORT_CUSTOM: + // FIXME: Nothing to check here? + break; - case LFUN_BUFFER_EXPORT: { - docstring const arg = cmd.argument(); - if (arg == "custom") { - enable = true; - break; - } - string format = to_utf8(arg); - size_t pos = format.find(' '); - if (pos != string::npos) - format = format.substr(0, pos); - enable = params().isExportable(format); - if (!enable) - flag.message(bformat( - _("Don't know how to export to format: %1$s"), arg)); + case LFUN_BUFFER_EXPORT: { + docstring const arg = cmd.argument(); + if (arg == "custom") { + enable = true; break; } + string format = to_utf8(arg); + size_t pos = format.find(' '); + if (pos != string::npos) + format = format.substr(0, pos); + enable = params().isExportable(format); + if (!enable) + flag.message(bformat( + _("Don't know how to export to format: %1$s"), arg)); + break; + } - case LFUN_BUFFER_CHKTEX: - enable = params().isLatex() && !lyxrc.chktex_command.empty(); - break; + case LFUN_BUFFER_CHKTEX: + enable = params().isLatex() && !lyxrc.chktex_command.empty(); + break; - case LFUN_BUILD_PROGRAM: - enable = params().isExportable("program"); - break; + case LFUN_BUILD_PROGRAM: + enable = params().isExportable("program"); + break; - 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; + case LFUN_BRANCH_ACTIVATE: + case LFUN_BRANCH_DEACTIVATE: + case LFUN_BRANCH_MASTER_ACTIVATE: + case LFUN_BRANCH_MASTER_DEACTIVATE: { + bool const master = (cmd.action() == LFUN_BRANCH_MASTER_ACTIVATE + || cmd.action() == LFUN_BRANCH_MASTER_DEACTIVATE); + BranchList const & branchList = master ? masterBuffer()->params().branchlist() + : params().branchlist(); + docstring const branchName = cmd.argument(); + flag.setEnabled(!branchName.empty() && branchList.find(branchName)); + break; + } - case LFUN_BUFFER_LANGUAGE: - enable = !isReadonly(); - break; + case LFUN_BRANCH_ADD: + case LFUN_BRANCHES_RENAME: + // if no Buffer is present, then of course we won't be called! + break; - default: - return false; + case LFUN_BUFFER_LANGUAGE: + enable = !isReadonly(); + break; + + case LFUN_BUFFER_VIEW_CACHE: + (d->preview_file_).refresh(); + enable = (d->preview_file_).exists(); + break; + + default: + return false; } flag.setEnabled(enable); return true; @@ -2278,9 +2500,13 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) break; } - case LFUN_BUILD_PROGRAM: - doExport("program", true); + case LFUN_BUILD_PROGRAM: { + ExportStatus const status = doExport("program", true); + dr.setError(status != ExportSuccess); + if (status != ExportSuccess) + dr.setMessage(_("Error generating literate programming code.")); break; + } case LFUN_BUFFER_CHKTEX: runChktex(); @@ -2321,7 +2547,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; } @@ -2337,6 +2564,42 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) resetAutosaveTimers(); break; + case LFUN_BRANCH_ACTIVATE: + case LFUN_BRANCH_DEACTIVATE: + case LFUN_BRANCH_MASTER_ACTIVATE: + case LFUN_BRANCH_MASTER_DEACTIVATE: { + bool const master = (func.action() == LFUN_BRANCH_MASTER_ACTIVATE + || func.action() == LFUN_BRANCH_MASTER_DEACTIVATE); + Buffer * buf = master ? const_cast(masterBuffer()) + : this; + + docstring const branch_name = func.argument(); + // the case without a branch name is handled elsewhere + if (branch_name.empty()) { + dispatched = false; + break; + } + Branch * branch = buf->params().branchlist().find(branch_name); + if (!branch) { + LYXERR0("Branch " << branch_name << " does not exist."); + dr.setError(true); + docstring const msg = + bformat(_("Branch \"%1$s\" does not exist."), branch_name); + dr.setMessage(msg); + break; + } + bool const activate = (func.action() == LFUN_BRANCH_ACTIVATE + || func.action() == LFUN_BRANCH_MASTER_ACTIVATE); + if (branch->isSelected() != activate) { + buf->undo().recordUndoBufferParams(CursorData()); + branch->setSelected(activate); + dr.setError(false); + dr.screenUpdate(Update::Force); + dr.forceBufferUpdate(); + } + break; + } + case LFUN_BRANCH_ADD: { docstring branch_name = func.argument(); if (branch_name.empty()) { @@ -2358,6 +2621,7 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) msg += ("\n"); msg += bformat(_("Branch \"%1$s\" already exists."), branch_name); } else { + undo().recordUndoBufferParams(CursorData()); branch_list.add(branch_name); branch = branch_list.find(branch_name); string const x11hexname = X11hexname(branch->color()); @@ -2409,122 +2673,11 @@ 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)) { - showPrintError(absFileName()); - dr.setMessage(_("Error exporting to DVI.")); - break; - } - - // Push directory path. - string const path = temppath(); - // Prevent the compiler from optimizing away p - FileName pp(path); - PathChanger p(pp); - - // there are three cases here: - // 1. we print to a file - // 2. we print directly to a printer - // 3. we print using a spool command (print to file first) - Systemcall one; - int res = 0; - string const dviname = changeExtension(latexName(true), "dvi"); - - if (target == "printer") { - if (!lyxrc.print_spool_command.empty()) { - // case 3: print using a spool - string const psname = changeExtension(dviname,".ps"); - command += ' ' + lyxrc.print_to_file - + quoteName(psname) - + ' ' - + quoteName(dviname); - - string command2 = lyxrc.print_spool_command + ' '; - if (target_name != "default") { - command2 += lyxrc.print_spool_printerprefix - + target_name - + ' '; - } - command2 += quoteName(psname); - // First run dvips. - // If successful, then spool command - res = one.startscript(Systemcall::Wait, command, - 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()); - } - - } else { - // case 1: print to a file - FileName const filename(makeAbsPath(target_name, filePath())); - FileName const dvifile(makeAbsPath(dviname, path)); - if (filename.exists()) { - docstring text = bformat( - _("The file %1$s already exists.\n\n" - "Do you want to overwrite that file?"), - makeDisplayPath(filename.absFileName())); - if (Alert::prompt(_("Overwrite file?"), - text, 0, 1, _("&Overwrite"), _("&Cancel")) != 0) - break; - } - command += ' ' + lyxrc.print_to_file - + quoteName(filename.toFilesystemEncoding()) - + ' ' - + quoteName(dvifile.toFilesystemEncoding()); - // as above.... - Systemcall::Starttype stype = use_gui ? - Systemcall::DontWait : Systemcall::Wait; - res = one.startscript(stype, command, filePath()); - } - - if (res == 0) - dr.setError(false); - else { - dr.setMessage(_("Error running external commands.")); - showPrintError(absFileName()); - } + case LFUN_BUFFER_VIEW_CACHE: + if (!formats.view(*this, d->preview_file_, + d->preview_format_)) + dr.setMessage(_("Error viewing the output file.")); break; - } default: dispatched = false; @@ -2537,8 +2690,8 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) void Buffer::changeLanguage(Language const * from, Language const * to) { - LASSERT(from, /**/); - LASSERT(to, /**/); + LASSERT(from, return); + LASSERT(to, return); for_each(par_iterator_begin(), par_iterator_end(), @@ -2649,7 +2802,7 @@ bool Buffer::isClean() const bool Buffer::isExternallyModified(CheckMethod method) const { - LASSERT(d->filename.exists(), /**/); + LASSERT(d->filename.exists(), return false); // if method == timestamp, check timestamp before checksum return (method == checksum_method || d->timestamp_ != d->filename.lastModified()) @@ -2745,11 +2898,44 @@ string Buffer::absFileName() const string Buffer::filePath() const { - int last = d->filename.onlyPath().absFileName().length() - 1; + string const abs = d->filename.onlyPath().absFileName(); + if (abs.empty()) + return abs; + int last = abs.length() - 1; - return d->filename.onlyPath().absFileName()[last] == '/' - ? d->filename.onlyPath().absFileName() - : d->filename.onlyPath().absFileName() + "/"; + return abs[last] == '/' ? abs : abs + '/'; +} + + +string Buffer::originFilePath() const +{ + if (FileName::isAbsolute(params().origin)) + return params().origin; + + return filePath(); +} + + +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 = "."; } @@ -2899,12 +3085,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; } @@ -2929,13 +3115,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; @@ -3100,7 +3286,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)); @@ -3246,10 +3432,10 @@ void Buffer::listParentMacros(MacroSet & macros, LaTeXFeatures & features) const } -Buffer::References & Buffer::references(docstring const & label) +Buffer::References & Buffer::getReferenceCache(docstring const & label) { if (d->parent()) - return const_cast(masterBuffer())->references(label); + return const_cast(masterBuffer())->getReferenceCache(label); RefCache::iterator it = d->ref_cache_.find(label); if (it != d->ref_cache_.end()) @@ -3265,7 +3451,14 @@ Buffer::References & Buffer::references(docstring const & label) Buffer::References const & Buffer::references(docstring const & label) const { - return const_cast(this)->references(label); + return const_cast(this)->getReferenceCache(label); +} + + +void Buffer::addReference(docstring const & label, Inset * inset, ParIterator it) +{ + References & refs = getReferenceCache(label); + refs.push_back(make_pair(inset, it)); } @@ -3288,12 +3481,9 @@ void Buffer::clearReferenceCache() const } -void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to, - InsetCode code) +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 @@ -3311,22 +3501,21 @@ void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to, string const paramName = "key"; for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) { - if (it->lyxCode() == code) { - InsetCommand * inset = it->asInsetCommand(); - if (!inset) - continue; - docstring const oldValue = inset->getParam(paramName); - if (oldValue == from) - inset->setParam(paramName, to); - } + if (it->lyxCode() != CITE_CODE) + continue; + InsetCommand * inset = it->asInsetCommand(); + docstring const oldValue = inset->getParam(paramName); + if (oldValue == from) + inset->setParam(paramName, to); } } - -void Buffer::getSourceCode(odocstream & os, string const format, +// returns NULL if id-to-row conversion is unsupported +auto_ptr Buffer::getSourceCode(odocstream & os, string const & format, pit_type par_begin, pit_type par_end, OutputWhat output, bool master) const { + auto_ptr texrow(NULL); OutputParams runparams(¶ms().encoding()); runparams.nice = true; runparams.flavor = params().getOutputFlavor(format); @@ -3360,7 +3549,7 @@ void Buffer::getSourceCode(odocstream & os, string const format, setMathFlavor(runparams); xhtmlParagraphs(text(), *this, xs, runparams); } else if (runparams.flavor == OutputParams::TEXT) { - bool dummy; + bool dummy = false; // FIXME Handles only one paragraph, unlike the others. // Probably should have some routine with a signature like them. writePlaintextParagraph(*this, @@ -3380,12 +3569,12 @@ 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(); + texrow.reset(new TexRow()); + texrow->reset(); + texrow->newline(); + texrow->newline(); // latex or literate - otexstream ots(os, texrow); + otexstream ots(os, *texrow); // the real stuff latexParagraphs(*this, text(), ots, runparams); @@ -3408,7 +3597,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()); @@ -3423,15 +3612,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); + texrow.reset(new TexRow()); + texrow->reset(); + texrow->newline(); + texrow->newline(); + otexstream ots(os, *texrow); if (master) runparams.is_child = true; writeLaTeXSource(ots, string(), runparams, output); } } + return texrow; } @@ -3558,11 +3749,13 @@ int AutoSaveBuffer::generateChild() // to fork. But we will do the save // anyway. bool failed = false; - FileName const tmp_ret = FileName::tempName("lyxauto"); + TempFile tempfile("lyxautoXXXXXX.lyx"); + tempfile.setAutoRemove(false); + FileName const tmp_ret = tempfile.name(); if (!tmp_ret.empty()) { - buffer_.writeFile(tmp_ret); - // assume successful write of tmp_ret - if (!tmp_ret.moveTo(fname_)) + if (!buffer_.writeFile(tmp_ret)) + failed = true; + else if (!tmp_ret.moveTo(fname_)) failed = true; } else failed = true; @@ -3642,7 +3835,9 @@ bool Buffer::autoSave() const // If this buffer is cloned, we assume that // we are running in a separate thread already. - FileName const tmp_ret = FileName::tempName("lyxauto"); + TempFile tempfile("lyxautoXXXXXX.lyx"); + tempfile.setAutoRemove(false); + FileName const tmp_ret = tempfile.name(); if (!tmp_ret.empty()) { writeFile(tmp_ret); // assume successful write of tmp_ret @@ -3654,24 +3849,6 @@ bool Buffer::autoSave() const } -// helper class, to guarantee this gets reset properly -class Buffer::MarkAsExporting { -public: - MarkAsExporting(Buffer const * buf) : buf_(buf) - { - LASSERT(buf_, /* */); - buf_->setExportStatus(true); - } - ~MarkAsExporting() - { - buf_->setExportStatus(false); - } -private: - Buffer const * const buf_; -}; - - - void Buffer::setExportStatus(bool e) const { d->doing_export = e; @@ -3783,7 +3960,7 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir Graph::EdgePath::const_iterator it = path.begin(); Graph::EdgePath::const_iterator en = path.end(); for (; it != en; ++it) - if (theConverters().get(*it).nice) { + if (theConverters().get(*it).nice()) { need_nice_file = true; break; } @@ -3860,17 +4037,21 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir // Emit the signal to show the error list or copy it back to the // cloned Buffer so that it can be emitted afterwards. if (format != backend_format) { - if (d->cloned_buffer_) { + if (runparams.silent) + error_list.clear(); + else if (d->cloned_buffer_) d->cloned_buffer_->d->errorLists[error_type] = d->errorLists[error_type]; - } else + else errors(error_type); // also to the children, in case of master-buffer-view ListOfBuffers clist = getDescendents(); ListOfBuffers::const_iterator cit = clist.begin(); ListOfBuffers::const_iterator const cen = clist.end(); for (; cit != cen; ++cit) { - if (d->cloned_buffer_) { + if (runparams.silent) + (*cit)->d->errorLists[error_type].clear(); + else if (d->cloned_buffer_) { // Enable reverse search by copying back the // texrow object to the cloned buffer. // FIXME: this is not thread safe. @@ -3892,12 +4073,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()) @@ -3962,7 +4141,7 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir formats.prettyName(format))); } - return ExportSuccess; + return success ? ExportSuccess : ExportConverterError; } @@ -3974,6 +4153,7 @@ Buffer::ExportStatus Buffer::preview(string const & format) const return preview(format, update_unincluded); } + Buffer::ExportStatus Buffer::preview(string const & format, bool includeall) const { MarkAsExporting exporting(this); @@ -3986,11 +4166,28 @@ 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); + + LATTEST (isClone()); + d->cloned_buffer_->d->preview_file_ = previewFile; + d->cloned_buffer_->d->preview_format_ = format; + d->cloned_buffer_->d->preview_error_ = (status != ExportSuccess); + if (status != ExportSuccess) return status; - if (!formats.view(*this, FileName(result_file), format)) - return PreviewError; - return PreviewSuccess; + 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; + } } @@ -4204,13 +4401,17 @@ void Buffer::setBuffersForInsets() const void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const { + LBUFERR(!text().paragraphs().empty()); + // Use the master text class also for child documents Buffer const * const master = masterBuffer(); DocumentClass const & textclass = master->params().documentClass(); // do this only if we are the top-level Buffer - if (master == this) + if (master == this) { + textclass.counters().reset(from_ascii("bibitem")); 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. @@ -4221,9 +4422,15 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const if (master != this) { bufToUpdate.insert(this); master->updateBuffer(UpdateMaster, utype); - // Do this here in case the master has no gui associated with it. Then, - // the TocModel is not updated and TocModel::toc_ is invalid (bug 5699). - if (!master->d->gui_) + // 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 + // window, that TOC is not updated. So some more general solution is needed at + // some point. + if (master->d->gui_ != d->gui_) structureChanged(); // was buf referenced from the master (i.e. not in bufToUpdate anymore)? @@ -4244,8 +4451,6 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const Buffer & cbuf = const_cast(*this); - LASSERT(!text().paragraphs().empty(), /**/); - // do the real work ParIterator parit = cbuf.par_iterator_begin(); updateBuffer(parit, utype); @@ -4256,7 +4461,8 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const d->bibinfo_cache_valid_ = true; d->cite_labels_valid_ = true; - cbuf.tocBackend().update(); + /// FIXME: Perf + cbuf.tocBackend().update(utype == OutputUpdate); if (scope == UpdateMaster) cbuf.structureChanged(); } @@ -4317,7 +4523,7 @@ static depth_type getItemDepth(ParIterator const & it) static bool needEnumCounterReset(ParIterator const & it) { Paragraph const & par = *it; - LASSERT(par.layout().labeltype == LABEL_ENUMERATE, /**/); + LASSERT(par.layout().labeltype == LABEL_ENUMERATE, return false); depth_type const cur_depth = par.getDepth(); ParIterator prev_it = it; while (prev_it.pit()) { @@ -4364,17 +4570,6 @@ 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()))) { - if (counters.hasCounter(layout.counter)) - counters.step(layout.counter, utype); - par.params().labelString(par.expandLabel(layout, bp)); - } else - par.params().labelString(docstring()); - break; - case LABEL_ITEMIZE: { // At some point of time we should do something more // clever here, like: @@ -4454,19 +4649,34 @@ void Buffer::Impl::setLabel(ParIterator & it, UpdateType utype) const par.params().labelString(docstring()); break; + case LABEL_ABOVE: + case LABEL_CENTERED: + case LABEL_STATIC: { + docstring const & lcounter = layout.counter; + if (!lcounter.empty()) { + if (layout.toclevel <= bp.secnumdepth + && (layout.latextype != LATEX_ENVIRONMENT + || it.text()->isFirstInSequence(it.pit()))) { + if (counters.hasCounter(lcounter)) + counters.step(lcounter, utype); + par.params().labelString(par.expandLabel(layout, bp)); + } else + par.params().labelString(docstring()); + } else + par.params().labelString(par.expandLabel(layout, bp)); + break; + } + case LABEL_MANUAL: - case LABEL_TOP_ENVIRONMENT: - case LABEL_CENTERED_TOP_ENVIRONMENT: - case LABEL_STATIC: case LABEL_BIBLIO: par.params().labelString(par.expandLabel(layout, bp)); - break; } } void Buffer::updateBuffer(ParIterator & parit, UpdateType utype) const { + // LASSERT: Is it safe to continue here, or should we just return? LASSERT(parit.pit() == 0, /**/); // Set the position of the text in the buffer to be able @@ -4523,13 +4733,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; @@ -4539,7 +4749,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) { @@ -4557,16 +4766,16 @@ void Buffer::Impl::updateStatistics(DocIterator & from, DocIterator & to, bool s word_count_ = 0; char_count_ = 0; blank_count_ = 0; - + for (DocIterator dit = from ; dit != to && !dit.atEnd(); ) { if (!dit.inTexted()) { dit.forwardPos(); continue; } - + Paragraph const & par = dit.paragraph(); pos_type const pos = dit.pos(); - + // Copied and adapted from isWordSeparator() in Paragraph if (pos == dit.lastpos()) { inword = false; @@ -4580,7 +4789,7 @@ void Buffer::Impl::updateStatistics(DocIterator & from, DocIterator & to, bool s break; continue; } else if (!par.isDeleted(pos)) { - if (par.isWordSeparator(pos)) + if (par.isWordSeparator(pos)) inword = false; else if (!inword) { ++word_count_; @@ -4622,7 +4831,7 @@ int Buffer::charCount(bool with_blanks) const } -Buffer::ReadStatus Buffer::reload(bool clearUndo) +Buffer::ReadStatus Buffer::reload() { setBusy(true); // c.f. bug http://www.lyx.org/trac/ticket/6587 @@ -4640,8 +4849,7 @@ Buffer::ReadStatus Buffer::reload(bool clearUndo) updateTitles(); markClean(); message(bformat(_("Document %1$s reloaded."), disp_fn)); - if (clearUndo) - d->undo_.clear(); + d->undo_.clear(); } else { message(bformat(_("Could not reload document %1$s."), disp_fn)); } @@ -4658,6 +4866,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(); @@ -4675,22 +4885,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(); @@ -4710,11 +4917,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); } @@ -4744,4 +4946,23 @@ void Buffer::checkMasterBuffer() setParent(0); } + +string Buffer::includedFilePath(string const & name, string const & ext) const +{ + bool isabsolute = FileName::isAbsolute(name); + // old_position already contains a trailing path separator + string const absname = isabsolute ? name : d->old_position + name; + + if (d->old_position.empty() + || equivalent(FileName(d->old_position), FileName(filePath())) + || !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()))); +} + } // namespace lyx