]> git.lyx.org Git - lyx.git/blobdiff - src/Buffer.cpp
Fix setting Interlingua as GUI language
[lyx.git] / src / Buffer.cpp
index ceb86b91e5d7a0fd3706f3e55fe3a5c44a4d255d..54da9b24ea394bc0f7dbfa3f5b685bc4d00565a6 100644 (file)
@@ -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<DocIterator, MacroData> ScopeMacro;
+       struct ScopeMacro {
+               ScopeMacro() {}
+               ScopeMacro(DocIterator const & s, MacroData const & m)
+                       : scope(s), macro(m) {}
+               DocIterator scope;
+               MacroData macro;
+       };
        typedef map<DocIterator, ScopeMacro> PositionScopeMacroMap;
        typedef map<docstring, PositionScopeMacroMap> NamePositionScopeMacroMap;
        /// map from the macro name to the position map,
@@ -230,7 +236,13 @@ public:
 
        /// positions of child buffers in the buffer
        typedef map<Buffer const * const, DocIterator> BufferPositionMap;
-       typedef pair<DocIterator, Buffer const *> ScopeBuffer;
+       struct ScopeBuffer {
+               ScopeBuffer() {}
+               ScopeBuffer(DocIterator const & s,Buffer const * b)
+                       : scope(s), buffer(b) {}
+               DocIterator scope;
+               Buffer const * buffer;
+       };
        typedef map<DocIterator, ScopeBuffer> 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<string>(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<string>(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<Buffer *>(it->second.second);
+               Buffer * child = const_cast<Buffer *>(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<Buffer *>(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>
+               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_;