X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBuffer.cpp;h=a20b4da3db5abdc18e77603eb21493e267e159b9;hb=b4fad55fd732c097ac44cf7a3428281376cc5dcc;hp=60dcc51ce309d7b7c37e6284c6d412886a8107b8;hpb=494ce6647a94c6fa6b8ae65dd45a5ab8c1742817;p=lyx.git diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 60dcc51ce3..a20b4da3db 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -91,6 +91,7 @@ #include "support/debug.h" #include "support/docstring_list.h" #include "support/ExceptionMessage.h" +#include "support/FileMonitor.h" #include "support/FileName.h" #include "support/FileNameList.h" #include "support/filetools.h" @@ -263,9 +264,8 @@ public: /// Container for all sort of Buffer dependant errors. map errorLists; - /// timestamp and checksum used to test if the file has been externally - /// modified. (Used to properly enable 'File->Revert to saved', bug 4114). - time_t timestamp_; + /// checksum used to test if the file has been externally modified. Used to + /// double check whether the file had been externally modified when saving. unsigned long checksum_; /// @@ -376,6 +376,18 @@ public: // display the review toolbar, for instance) mutable bool tracked_changes_present_; + // Make sure the file monitor monitors the good file. + void refreshFileMonitor(); + + /// has it been notified of an external modification? + bool isExternallyModified() const { return externally_modified_; } + + /// Notify or clear of external modification + void fileExternallyModified(bool modified) const; + + /// Block notifications of external modifications + FileMonitorBlocker blockFileMonitor() { return file_monitor_->block(10); } + private: /// So we can force access via the accessors. mutable Buffer const * parent_buffer; @@ -384,6 +396,10 @@ private: int char_count_; int blank_count_; + /// has been externally modified? Can be reset by the user. + mutable bool externally_modified_; + + FileMonitorPtr file_monitor_; }; @@ -419,14 +435,16 @@ Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_, : owner_(owner), lyx_clean(true), bak_clean(true), unnamed(false), internal_buffer(false), read_only(readonly_), filename(file), file_fully_loaded(false), file_format(LYX_FORMAT), need_format_backup(false), - ignore_parent(false), toc_backend(owner), macro_lock(false), timestamp_(0), + ignore_parent(false), toc_backend(owner), macro_lock(false), checksum_(0), wa_(0), gui_(0), undo_(*owner), bibinfo_cache_valid_(false), bibfile_cache_valid_(false), cite_labels_valid_(false), preview_error_(false), inset(0), preview_loader_(0), cloned_buffer_(cloned_buffer), clone_list_(0), doing_export(false), tracked_changes_present_(0), parent_buffer(0), - word_count_(0), char_count_(0), blank_count_(0) + word_count_(0), char_count_(0), blank_count_(0), + externally_modified_(false) { + refreshFileMonitor(); if (!cloned_buffer_) { temppath = createBufferTmpDir(); lyxvc.setBuffer(owner_); @@ -816,7 +834,7 @@ string Buffer::logName(LogType * type) const FileName const bname( addName(path, onlyFileName( changeExtension(filename, - formats.extension(params().bufferFormat()) + ".out")))); + theFormats().extension(params().bufferFormat()) + ".out")))); // Also consider the master buffer log file FileName masterfname = fname; @@ -864,6 +882,7 @@ void Buffer::setFileName(FileName const & fname) { bool const changed = fname != d->filename; d->filename = fname; + d->refreshFileMonitor(); if (changed) lyxvc().file_found_hook(fname); setReadonly(d->filename.isReadOnly()); @@ -1066,7 +1085,7 @@ bool Buffer::readDocument(Lexer & lex) bool Buffer::importString(string const & format, docstring const & contents, ErrorList & errorList) { - Format const * fmt = formats.getFormat(format); + Format const * fmt = theFormats().getFormat(format); if (!fmt) return false; // It is important to use the correct extension here, since some @@ -1180,7 +1199,7 @@ Buffer::ReadStatus Buffer::readFile(FileName const & fn) d->file_fully_loaded = true; d->read_only = !d->filename.isWritable(); - params().compressed = formats.isZippedFile(d->filename); + params().compressed = theFormats().isZippedFile(d->filename); saveCheckSum(); return ReadSuccess; } @@ -1360,6 +1379,7 @@ FileName Buffer::getBackupName() const { // Should probably be moved to somewhere else: BufferView? GuiView? bool Buffer::save() const { + FileMonitorBlocker block = d->blockFileMonitor(); docstring const file = makeDisplayPath(absFileName(), 20); d->filename.refresh(); @@ -1375,7 +1395,7 @@ bool Buffer::save() const } // ask if the disk file has been externally modified (use checksum method) - if (fileName().exists() && isExternallyModified(checksum_method)) { + if (fileName().exists() && isChecksumModified()) { docstring text = bformat(_("Document %1$s has been externally modified. " "Are you sure you want to overwrite this file?"), file); @@ -1390,12 +1410,12 @@ bool Buffer::save() const // if the file does not yet exist, none of the backup activity // that follows is necessary - if (!fileName().exists()) { + if (!fileName().exists()) { if (!writeFile(fileName())) - return false; - markClean(); - return true; - } + 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 @@ -1721,6 +1741,7 @@ bool Buffer::makeLaTeXFile(FileName const & fname, catch (exception const & e) { errorList.push_back(ErrorItem(_("conversion failed"), _(e.what()))); + lyxerr << e.what() << endl; failed_export = true; } catch (...) { @@ -2515,7 +2536,7 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag) switch (cmd.action()) { case LFUN_BUFFER_TOGGLE_READ_ONLY: - flag.setOnOff(isReadonly()); + flag.setOnOff(hasReadonlyFlag()); break; // FIXME: There is need for a command-line import. @@ -2534,7 +2555,7 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag) enable = true; break; } - string format = (arg.empty() || arg == "*") ? + string format = (arg.empty() || arg == "default") ? params().getDefaultOutputFormat() : to_utf8(arg); size_t pos = format.find(' '); if (pos != string::npos) @@ -2639,11 +2660,11 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) dr.setMessage(log); } else - setReadonly(!isReadonly()); + setReadonly(!hasReadonlyFlag()); break; case LFUN_BUFFER_EXPORT: { - string const format = (argument.empty() || argument == "*") ? + string const format = (argument.empty() || argument == "default") ? params().getDefaultOutputFormat() : argument; ExportStatus const status = doExport(format, false); dr.setError(status != ExportSuccess); @@ -2668,7 +2689,7 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) case LFUN_BUFFER_EXPORT_CUSTOM: { string format_name; string command = split(argument, format_name, ' '); - Format const * format = formats.getFormat(format_name); + Format const * format = theFormats().getFormat(format_name); if (!format) { lyxerr << "Format \"" << format_name << "\" not recognized!" @@ -2827,7 +2848,7 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) } case LFUN_BUFFER_VIEW_CACHE: - if (!formats.view(*this, d->preview_file_, + if (!theFormats().view(*this, d->preview_file_, d->preview_format_)) dr.setMessage(_("Error viewing the output file.")); break; @@ -2995,29 +3016,19 @@ bool Buffer::isClean() const } -bool Buffer::isExternallyModified(CheckMethod method) const +bool Buffer::isChecksumModified() const { LASSERT(d->filename.exists(), return false); - // if method == timestamp, check timestamp before checksum - return (method == checksum_method - || d->timestamp_ != d->filename.lastModified()) - && d->checksum_ != d->filename.checksum(); + return d->checksum_ != d->filename.checksum(); } void Buffer::saveCheckSum() const { FileName const & file = d->filename; - file.refresh(); - if (file.exists()) { - d->timestamp_ = file.lastModified(); - d->checksum_ = file.checksum(); - } else { - // in the case of save to a new file. - d->timestamp_ = 0; - d->checksum_ = 0; - } + d->checksum_ = file.exists() ? file.checksum() + : 0; // in the case of save to a new file. } @@ -3031,6 +3042,7 @@ void Buffer::markClean() const // autosave d->bak_clean = true; d->undo_.markDirty(); + clearExternalModification(); } @@ -3251,12 +3263,18 @@ void Buffer::setLayoutPos(string const & path) } -bool Buffer::isReadonly() const +bool Buffer::hasReadonlyFlag() const { return d->read_only; } +bool Buffer::isReadonly() const +{ + return hasReadonlyFlag() || notifiesExternalModification(); +} + + void Buffer::setParent(Buffer const * buffer) { // Avoids recursive include. @@ -4133,7 +4151,7 @@ void Buffer::moveAutosaveFile(support::FileName const & oldauto) const bool Buffer::autoSave() const { Buffer const * buf = d->cloned_buffer_ ? d->cloned_buffer_ : this; - if (buf->d->bak_clean || isReadonly()) + if (buf->d->bak_clean || hasReadonlyFlag()) return true; message(_("Autosaving current document...")); @@ -4231,6 +4249,8 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir if (pos != string::npos) { dest_filename = target.substr(pos + 1, target.length() - pos - 1); format = target.substr(0, pos); + if (format == "default") + format = params().getDefaultOutputFormat(); runparams.export_folder = FileName(dest_filename).onlyPath().realPath(); FileName(dest_filename).onlyPath().createPath(); LYXERR(Debug::FILES, "format=" << format << ", dest_filename=" << dest_filename << ", export_folder=" << runparams.export_folder); @@ -4261,7 +4281,7 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir // file (not for previewing). Alert::error(_("Couldn't export file"), bformat( _("No information for exporting the format %1$s."), - formats.prettyName(format))); + theFormats().prettyName(format))); } return ExportNoPathToFormat; } @@ -4291,7 +4311,7 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir string filename = latexName(false); filename = addName(temppath(), filename); filename = changeExtension(filename, - formats.extension(backend_format)); + theFormats().extension(backend_format)); LYXERR(Debug::FILES, "filename=" << filename); // Plain text backend @@ -4337,7 +4357,7 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir string const error_type = (format == "program") ? "Build" : params().bufferFormat(); ErrorList & error_list = d->errorLists[error_type]; - string const ext = formats.extension(format); + string const ext = theFormats().extension(format); FileName const tmp_result_file(changeExtension(filename, ext)); bool const success = converters.convert(this, FileName(filename), tmp_result_file, FileName(absFileName()), backend_format, format, @@ -4405,7 +4425,7 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir vector::const_iterator it = files.begin(); vector::const_iterator const en = files.end(); for (; it != en && status != CANCEL; ++it) { - string const fmt = formats.getFormatFromFile(it->sourceName); + string const fmt = theFormats().getFormatFromFile(it->sourceName); string fixedName = it->exportName; if (!runparams.export_folder.empty()) { // Relative pathnames starting with ../ will be sanitized @@ -4441,13 +4461,13 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir } else { message(bformat(_("Document exported as %1$s " "to file `%2$s'"), - formats.prettyName(format), + theFormats().prettyName(format), makeDisplayPath(result_file))); } } else { // This must be a dummy converter like fax (bug 1888) message(bformat(_("Document exported as %1$s"), - formats.prettyName(format))); + theFormats().prettyName(format))); } return success ? ExportSuccess : ExportConverterError; @@ -4486,7 +4506,7 @@ Buffer::ExportStatus Buffer::preview(string const & format, bool includeall) con return status; if (previewFile.exists()) - return formats.view(*this, previewFile, format) ? + return theFormats().view(*this, previewFile, format) ? PreviewSuccess : PreviewError; // Successful export but no output file? @@ -4528,7 +4548,7 @@ Buffer::ReadStatus Buffer::loadEmergency() ReadStatus const ret_llf = loadThisLyXFile(emergencyFile); bool const success = (ret_llf == ReadSuccess); if (success) { - if (isReadonly()) { + if (hasReadonlyFlag()) { Alert::warning(_("File is read-only"), bformat(_("An emergency file is successfully loaded, " "but the original file %1$s is marked read-only. " @@ -4591,7 +4611,7 @@ Buffer::ReadStatus Buffer::loadAutosave() ReadStatus const ret_llf = loadThisLyXFile(autosaveFile); // the file is not saved if we load the autosave file. if (ret_llf == ReadSuccess) { - if (isReadonly()) { + if (hasReadonlyFlag()) { Alert::warning(_("File is read-only"), bformat(_("A backup file is successfully loaded, " "but the original file %1$s is marked read-only. " @@ -5296,5 +5316,41 @@ void Buffer::updateChangesPresent() const } +void Buffer::Impl::refreshFileMonitor() +{ + if (file_monitor_ && file_monitor_->filename() == filename.absFileName()) + return file_monitor_->refresh(); + + // The previous file monitor is invalid + // This also destroys the previous file monitor and all its connections + file_monitor_ = FileSystemWatcher::monitor(filename); + fileExternallyModified(false); + // file_monitor_ will be destroyed with *this, so it is not going to call a + // destroyed object method. + file_monitor_->connect([this](){ fileExternallyModified(true); }); +} + + +void Buffer::Impl::fileExternallyModified(bool modified) const +{ + if (modified) + lyx_clean = bak_clean = false; + externally_modified_ = modified; + if (wa_) + wa_->updateTitles(); +} + + +bool Buffer::notifiesExternalModification() const +{ + return d->isExternallyModified(); +} + + +void Buffer::clearExternalModification() const +{ + d->fileExternallyModified(false); +} + } // namespace lyx