X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBuffer.cpp;h=54da9b24ea394bc0f7dbfa3f5b685bc4d00565a6;hb=8aa2d6b18415659e147bfb4039e371a3277226d5;hp=ceb86b91e5d7a0fd3706f3e55fe3a5c44a4d255d;hpb=b89ef8a5b31a02240f90a6d6252b1b680e1e1ac8;p=lyx.git diff --git a/src/Buffer.cpp b/src/Buffer.cpp index ceb86b91e5..54da9b24ea 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -58,7 +58,6 @@ #include "SpellChecker.h" #include "sgml.h" #include "TexRow.h" -#include "TexStream.h" #include "Text.h" #include "TextClass.h" #include "TocBackend.h" @@ -98,6 +97,7 @@ #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" @@ -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,7 +236,13 @@ 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; @@ -323,7 +335,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 @@ -354,12 +366,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( @@ -377,9 +397,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(); @@ -529,7 +550,7 @@ void Buffer::cloneWithChildren(BufferMap & bufmap, CloneList * clones) const // 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(); + buffer_clone->params().makeDocumentClass(true); ErrorList el; cap::switchBetweenClasses( params().documentClassPtr(), buffer_clone->params().documentClassPtr(), @@ -550,7 +571,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); @@ -577,7 +598,7 @@ Buffer * Buffer::cloneBufferOnly() const { // 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(); + buffer_clone->params().makeDocumentClass(true); ErrorList el; cap::switchBetweenClasses( params().documentClassPtr(), buffer_clone->params().documentClassPtr(), @@ -636,6 +657,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(); @@ -745,7 +788,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); @@ -913,7 +956,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"); @@ -933,7 +976,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())) { @@ -994,7 +1037,8 @@ bool Buffer::importString(string const & format, docstring const & contents, Err TempFile const tempfile("Buffer_importStringXXXXXX." + fmt->extension()); FileName const name(tempfile.name()); ofdocstream os(name.toFilesystemEncoding().c_str()); - bool const success = (os << contents); + // 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; @@ -1034,7 +1078,8 @@ 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; bool success = parseLyXFormat(lex, fn, file_format) == ReadSuccess; @@ -1051,8 +1096,6 @@ bool Buffer::readString(string const & s) else if (success) if (readDocument(lex)) success = false; - if (fn.exists()) - fn.removeFile(); return success; } @@ -1173,7 +1216,9 @@ Buffer::ReadStatus Buffer::parseLyXFormat(Lexer & lex, Buffer::ReadStatus Buffer::convertLyXFormat(FileName const & fn, FileName & tmpfile, int from_format) { - tmpfile = FileName::tempName("Buffer_convertLyXFormatXXXXXX.lyx"); + 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" @@ -1260,12 +1305,32 @@ 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()) + return writeFile(fileName()); + + // 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; - // make a backup if the file already exists - if (lyxrc.make_backup && fileName().exists()) { - backupName = FileName(absFileName() + '~'); + // 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(), '/', '!'), ':', '!'); @@ -1273,14 +1338,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."), @@ -1289,15 +1355,46 @@ 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; } @@ -1442,7 +1539,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. @@ -1484,7 +1581,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; @@ -1502,13 +1598,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."), @@ -1536,7 +1631,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; } @@ -1550,6 +1648,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 @@ -1761,7 +1864,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; @@ -1916,7 +2019,7 @@ void Buffer::writeLyXHTMLSource(odocstream & os, << ";\n"; css << "}\n"; } - + docstring const dstyles = css.str(); if (!dstyles.empty()) { bool written = false; @@ -2000,7 +2103,10 @@ int Buffer::runChktex() setBusy(false); - errors("ChkTeX"); + if (runparams.silent) + d->errorLists["ChkTeX"].clear(); + else + errors("ChkTeX"); return res; } @@ -2365,9 +2471,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(); @@ -2451,7 +2561,7 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) bool const activate = (func.action() == LFUN_BRANCH_ACTIVATE || func.action() == LFUN_BRANCH_MASTER_ACTIVATE); if (branch->isSelected() != activate) { - buf->undo().recordUndoFullDocument(CursorData()); + buf->undo().recordUndoBufferParams(CursorData()); branch->setSelected(activate); dr.setError(false); dr.screenUpdate(Update::Force); @@ -2481,6 +2591,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()); @@ -2556,7 +2667,7 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) break; } - if (!doExport("dvi", true)) { + if (doExport("dvi", true) != ExportSuccess) { showPrintError(absFileName()); dr.setMessage(_("Error exporting to DVI.")); break; @@ -3023,12 +3134,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; } @@ -3053,13 +3164,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; @@ -3439,7 +3550,7 @@ 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() != CITE_CODE) + if (it->lyxCode() != CITE_CODE) continue; InsetCommand * inset = it->asInsetCommand(); docstring const oldValue = inset->getParam(paramName); @@ -3449,7 +3560,7 @@ void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to) } -void Buffer::getSourceCode(odocstream & os, string const format, +void Buffer::getSourceCode(odocstream & os, string const & format, pit_type par_begin, pit_type par_end, OutputWhat output, bool master) const { @@ -3486,7 +3597,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, @@ -3684,11 +3795,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; @@ -3768,7 +3881,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 @@ -3891,7 +4006,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; } @@ -3968,17 +4083,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. @@ -4082,6 +4201,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); @@ -4333,9 +4453,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)? @@ -4671,16 +4797,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; @@ -4694,7 +4820,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_;