]> git.lyx.org Git - lyx.git/blobdiff - src/Buffer.cpp
Extend the fix for #5760 to the cygwin xcb backend.
[lyx.git] / src / Buffer.cpp
index b531127c9c935c00e967eeb528bc614265b64feb..2a67292c37ae9d1ef7d8c291efb9387d1f8fc3ab 100644 (file)
@@ -97,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"
@@ -132,14 +133,6 @@ int const LYX_FORMAT = LYX_FORMAT_LYX;
 typedef map<string, bool> DepClean;
 typedef map<docstring, pair<InsetLabel const *, Buffer::References> > 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
 
 
@@ -218,7 +211,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,
@@ -229,13 +228,29 @@ 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;
        /// 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<string, ErrorList> errorLists;
 
@@ -273,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
@@ -353,13 +377,20 @@ private:
 /// Creates the per buffer temporary directory
 static FileName createBufferTmpDir()
 {
-       // FIXME THREAD
-       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 +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();
@@ -399,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_;
 }
 
 
@@ -417,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());
 }
 
@@ -550,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<Buffer *>(it->second.second);
+               Buffer * child = const_cast<Buffer *>(it->second.buffer);
 
                child->cloneWithChildren(bufmap, clones);
                BufferMap::iterator const bit = bufmap.find(child);
@@ -645,12 +680,19 @@ BufferParams const & Buffer::masterParams() const
        // 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++)
+       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();
@@ -760,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);
@@ -880,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: "
@@ -982,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()) {
@@ -1050,7 +1093,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;
@@ -1067,8 +1111,6 @@ bool Buffer::readString(string const & s)
        else if (success)
                if (readDocument(lex))
                        success = false;
-       if (fn.exists())
-               fn.removeFile();
        return success;
 }
 
@@ -1129,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)
@@ -1189,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_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"
@@ -1278,36 +1328,34 @@ bool Buffer::save() const
 
        // if the file does not yet exist, none of the backup activity
        // that follows is necessary
-       if (!fileName().exists())
-               return writeFile(fileName());
+  if (!fileName().exists()) {
+               if (!writeFile(fileName()))
+      return false;
+    markClean();
+    return true;
+  }
 
        // we first write the file to a new name, then move it to its
        // proper location once that has been done successfully. that
        // way we preserve the original file if something goes wrong.
-       string const savepath = fileName().onlyPath().absFileName();
-       string savename = "tmp-" + fileName().onlyFileName();
-       FileName savefile(addName(savepath, savename));
-       while (savefile.exists()) {
-               if (savefile.absFileName().size() > 1024) {
-                       Alert::error(_("Write failure"),
-                                    bformat(_("Cannot find temporary filename for:\n  %1$s.\n"
-                                        "Even %2$s exists!"),
-                                            from_utf8(fileName().absFileName()),
-                                from_utf8(savefile.absFileName())));
-                       return false;
-               }
-               savename = "tmp-" + savename;
-               savefile.set(addName(savepath, savename));
-       }
-
+       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;
 
        // we will set this to false if we fail
        bool made_backup = true;
+
+       FileName backupName(absFileName() + '~');
        if (lyxrc.make_backup) {
-               FileName backupName(absFileName() + '~');
                if (!lyxrc.backupdir_path.empty()) {
                        string const mangledName =
                                subst(subst(backupName.absFileName(), '/', '!'), ':', '!');
@@ -1315,9 +1363,11 @@ 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.
-               made_backup = fileName().isSymLink() ?
+               made_backup = symlink ?
                        fileName().copyTo(backupName):
                        fileName().moveTo(backupName);
 
@@ -1329,17 +1379,46 @@ bool Buffer::save() const
                        //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
                }
        }
-       
-       if (made_backup && savefile.moveTo(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
-       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())));
+       // 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 {
+               // 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;
 }
 
@@ -1468,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
@@ -1485,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.
@@ -1544,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."),
@@ -1595,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
@@ -1806,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;
@@ -2355,7 +2438,6 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag)
 
        case LFUN_BRANCH_ADD:
        case LFUN_BRANCHES_RENAME:
-       case LFUN_BUFFER_PRINT:
                // if no Buffer is present, then of course we won't be called!
                break;
 
@@ -2363,6 +2445,10 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                enable = !isReadonly();
                break;
 
+       case LFUN_BUFFER_VIEW_CACHE:
+               enable = (d->preview_file_).exists();
+               break;
+
        default:
                return false;
        }
@@ -2460,7 +2546,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;
        }
 
@@ -2503,7 +2590,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);
@@ -2533,7 +2620,7 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr)
                                        msg += ("\n");
                                msg += bformat(_("Branch \"%1$s\" already exists."), branch_name);
                        } else {
-                               undo().recordUndoFullDocument(CursorData());
+                               undo().recordUndoBufferParams(CursorData());
                                branch_list.add(branch_name);
                                branch = branch_list.find(branch_name);
                                string const x11hexname = X11hexname(branch->color());
@@ -2585,122 +2672,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) != ExportSuccess) {
-                       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;
@@ -2930,6 +2906,38 @@ string Buffer::filePath() const
 }
 
 
+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 = ".";
+}
+
+
 bool Buffer::isReadonly() const
 {
        return d->read_only;
@@ -3076,12 +3084,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;
                                }
 
@@ -3106,13 +3114,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;
@@ -3277,7 +3285,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<Buffer *>(owner_), it));
@@ -3502,7 +3510,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
 {
@@ -3539,7 +3547,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,
@@ -3587,7 +3595,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());
@@ -3737,11 +3745,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;
@@ -3821,7 +3831,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
@@ -3944,7 +3956,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;
                        }
@@ -4057,12 +4069,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())
@@ -4127,7 +4137,7 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir
                        formats.prettyName(format)));
        }
 
-       return ExportSuccess;
+       return success ? ExportSuccess : ExportConverterError;
 }
 
 
@@ -4139,6 +4149,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);
@@ -4151,11 +4162,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;
+       }
 }
 
 
@@ -4390,9 +4418,9 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const
                if (master != this) {
                        bufToUpdate.insert(this);
                        master->updateBuffer(UpdateMaster, utype);
-                       // If the master buffer has no gui associated with it, then the TocModel is 
-                       // not updated during the updateBuffer call and TocModel::toc_ is invalid 
-                       // (bug 5699). The same happens if the master buffer is open in a different 
+                       // If the master buffer has no gui associated with it, then the TocModel is
+                       // not updated during the updateBuffer call and TocModel::toc_ is invalid
+                       // (bug 5699). The same happens if the master buffer is open in a different
                        // window. This test catches both possibilities.
                        // See: http://marc.info/?l=lyx-devel&m=138590578911716&w=2
                        // There remains a problem here: If there is another child open in yet a third
@@ -4700,13 +4728,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;
@@ -4716,7 +4744,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) {
@@ -4834,6 +4861,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();
@@ -4851,22 +4880,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();
@@ -4886,11 +4912,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);
        }
@@ -4920,4 +4941,22 @@ 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() || d->old_position == 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