]> git.lyx.org Git - lyx.git/blobdiff - src/buffer.C
the convert patch
[lyx.git] / src / buffer.C
index e28f687035a8fdbf22b150f7f330bc80f2fd8df8..1d6c5fa04fe9594f86d2f366deaee1c537861d5b 100644 (file)
 #include "Bullet.h"
 #include "Chktex.h"
 #include "debug.h"
+#include "encoding.h"
 #include "errorlist.h"
 #include "exporter.h"
 #include "format.h"
 #include "funcrequest.h"
 #include "gettext.h"
-#include "iterators.h"
+#include "insetiterator.h"
 #include "language.h"
 #include "LaTeX.h"
 #include "LaTeXFeatures.h"
@@ -34,6 +35,7 @@
 #include "lyxtext.h"
 #include "lyxrc.h"
 #include "lyxvc.h"
+#include "lyx_main.h"
 #include "messages.h"
 #include "output.h"
 #include "output_docbook.h"
@@ -42,7 +44,7 @@
 #include "paragraph.h"
 #include "paragraph_funcs.h"
 #include "ParagraphParameters.h"
-#include "PosIterator.h"
+#include "pariterator.h"
 #include "sgml.h"
 #include "texrow.h"
 #include "undo.h"
 #include "insets/insetinclude.h"
 #include "insets/insettext.h"
 
+#include "mathed/math_macrotemplate.h"
+#include "mathed/math_macrotable.h"
+#include "mathed/math_support.h"
+
 #include "frontends/Alert.h"
 
 #include "graphics/Previews.h"
 
 #include "support/FileInfo.h"
 #include "support/filetools.h"
-#include "support/gzstream.h"
+#ifdef USE_COMPRESSION
+# include "support/gzstream.h"
+#endif
 #include "support/lyxlib.h"
 #include "support/os.h"
 #include "support/path.h"
 #include "support/textutils.h"
-#include "support/tostr.h"
+#include "support/convert.h"
 
 #include <boost/bind.hpp>
 
-#include "support/std_sstream.h"
+#include <utime.h>
 
 #include <iomanip>
 #include <stack>
+#include <sstream>
+#include <fstream>
 
-#include <utime.h>
-
-#ifdef HAVE_LOCALE
-#endif
 
 using lyx::pos_type;
+using lyx::pit_type;
 
 using lyx::support::AddName;
-using lyx::support::atoi;
 using lyx::support::bformat;
 using lyx::support::ChangeExtension;
 using lyx::support::cmd_ret;
-using lyx::support::CreateBufferTmpDir;
+using lyx::support::createBufferTmpDir;
 using lyx::support::destroyDir;
 using lyx::support::FileInfo;
 using lyx::support::FileInfo;
-using lyx::support::getExtFromContents;
+using lyx::support::getFormatFromContents;
 using lyx::support::IsDirWriteable;
 using lyx::support::IsFileWriteable;
 using lyx::support::LibFileSearch;
@@ -105,7 +111,6 @@ using lyx::support::removeAutosaveFile;
 using lyx::support::rename;
 using lyx::support::RunCommand;
 using lyx::support::split;
-using lyx::support::strToInt;
 using lyx::support::subst;
 using lyx::support::tempName;
 using lyx::support::trim;
@@ -118,6 +123,7 @@ using std::make_pair;
 
 using std::ifstream;
 using std::ios;
+using std::map;
 using std::ostream;
 using std::ostringstream;
 using std::ofstream;
@@ -132,15 +138,16 @@ extern BufferList bufferlist;
 
 namespace {
 
-const int LYX_FORMAT = 226;
+int const LYX_FORMAT = 240;
 
 } // namespace anon
 
 
 typedef std::map<string, bool> DepClean;
 
-struct Buffer::Impl
+class Buffer::Impl
 {
+public:
        Impl(Buffer & parent, string const & file, bool readonly);
 
        limited_stack<Undo> undostack;
@@ -148,19 +155,18 @@ struct Buffer::Impl
        BufferParams params;
        LyXVC lyxvc;
        string temppath;
-       bool nicefile;
        TexRow texrow;
 
-       /// need to regenerate .tex ?
+       /// need to regenerate .tex?
        DepClean dep_clean;
 
-       /// is save needed
+       /// is save needed?
        mutable bool lyx_clean;
 
-       /// is autosave needed
+       /// is autosave needed?
        mutable bool bak_clean;
 
-       /// is this a unnamed file (New...)
+       /// is this a unnamed file (New...)?
        bool unnamed;
 
        /// buffer is r/o
@@ -174,26 +180,30 @@ struct Buffer::Impl
 
        boost::scoped_ptr<Messages> messages;
 
-       /** set to true only when the file is fully loaded.
+       /** Set to true only when the file is fully loaded.
         *  Used to prevent the premature generation of previews
         *  and by the citation inset.
         */
        bool file_fully_loaded;
 
-       /// our Text
-       LyXText text;
+       /// our LyXText that should be wrapped in an InsetText
+       InsetText inset;
+
+       ///
+       MacroTable macros;
 };
 
 
 Buffer::Impl::Impl(Buffer & parent, string const & file, bool readonly_)
-       : nicefile(true),
-         lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_),
+       : lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_),
          filename(file), filepath(OnlyPath(file)), file_fully_loaded(false),
-               text(0, 0)
+               inset(params)
 {
        lyxvc.buffer(&parent);
-       if (readonly_ || lyxrc.use_tempdir)
-               temppath = CreateBufferTmpDir();
+       temppath = createBufferTmpDir();
+       // FIXME: And now do something if temppath == string(), because we
+       // assume from now on that temppath points to a valid temp dir.
+       // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg67406.html
 }
 
 
@@ -224,7 +234,13 @@ Buffer::~Buffer()
 
 LyXText & Buffer::text() const
 {
-       return const_cast<LyXText &>(pimpl_->text);
+       return const_cast<LyXText &>(pimpl_->inset.text_);
+}
+
+
+InsetBase & Buffer::inset() const
+{
+       return const_cast<InsetText &>(pimpl_->inset);
 }
 
 
@@ -266,13 +282,13 @@ BufferParams const & Buffer::params() const
 
 ParagraphList & Buffer::paragraphs()
 {
-       return pimpl_->text.paragraphs();
+       return text().paragraphs();
 }
 
 
 ParagraphList const & Buffer::paragraphs() const
 {
-       return pimpl_->text.paragraphs();
+       return text().paragraphs();
 }
 
 
@@ -294,18 +310,6 @@ string const & Buffer::temppath() const
 }
 
 
-bool & Buffer::niceFile()
-{
-       return pimpl_->nicefile;
-}
-
-
-bool Buffer::niceFile() const
-{
-       return pimpl_->nicefile;
-}
-
-
 TexRow & Buffer::texrow()
 {
        return pimpl_->texrow;
@@ -318,7 +322,7 @@ TexRow const & Buffer::texrow() const
 }
 
 
-string const Buffer::getLatexName(bool no_path) const
+string const Buffer::getLatexName(bool const no_path) const
 {
        string const name = ChangeExtension(MakeLatexName(fileName()), ".tex");
        return no_path ? OnlyFilename(name) : name;
@@ -332,10 +336,7 @@ pair<Buffer::LogType, string> const Buffer::getLogName() const
        if (filename.empty())
                return make_pair(Buffer::latexlog, string());
 
-       string path = OnlyPath(filename);
-
-       if (lyxrc.use_tempdir || !IsDirWriteable(path))
-               path = temppath();
+       string const path = temppath();
 
        string const fname = AddName(path,
                                     OnlyFilename(ChangeExtension(filename,
@@ -360,7 +361,7 @@ pair<Buffer::LogType, string> const Buffer::getLogName() const
 }
 
 
-void Buffer::setReadonly(bool flag)
+void Buffer::setReadonly(bool const flag)
 {
        if (pimpl_->read_only != flag) {
                pimpl_->read_only = flag;
@@ -394,9 +395,11 @@ void unknownClass(string const & unknown)
 int Buffer::readHeader(LyXLex & lex)
 {
        int unknown_tokens = 0;
+       int line = -1;
+       int begin_header_line = -1;
 
        while (lex.isOK()) {
-               lex.nextToken();
+               lex.next();
                string const token = lex.getString();
 
                if (token.empty())
@@ -405,13 +408,18 @@ int Buffer::readHeader(LyXLex & lex)
                if (token == "\\end_header")
                        break;
 
+               ++line;
+               if (token == "\\begin_header") {
+                       begin_header_line = line;
+                       continue;
+               }
+
                lyxerr[Debug::PARSER] << "Handling header token: `"
                                      << token << '\'' << endl;
 
-
                string unknown = params().readToken(lex, token);
                if (!unknown.empty()) {
-                       if (unknown[0] != '\\') {
+                       if (unknown[0] != '\\' && token == "\\textclass") {
                                unknownClass(unknown);
                        } else {
                                ++unknown_tokens;
@@ -424,21 +432,25 @@ int Buffer::readHeader(LyXLex & lex)
                        }
                }
        }
+       if (begin_header_line) {
+               string const s = _("\\begin_header is missing");
+               error(ErrorItem(_("Header error"), s, -1, 0, 0));
+       }
        return unknown_tokens;
 }
 
 
-// candidate for move to BufferView
-// (at least some parts in the beginning of the func)
-//
 // Uwe C. Schroeder
 // changed to be public and have one parameter
-// if par = 0 normal behavior
-// else insert behavior
 // Returns false if "\end_document" is not read (Asger)
-bool Buffer::readBody(LyXLex & lex)
+bool Buffer::readDocument(LyXLex & lex)
 {
-       bool the_end_read = false;
+       lex.next();
+       string const token = lex.getString();
+       if (token != "\\begin_document") {
+               string const s = _("\\begin_document is missing");
+               error(ErrorItem(_("Header error"), s, -1, 0, 0));
+       }
 
        if (paragraphs().empty()) {
                readHeader(lex);
@@ -458,30 +470,26 @@ bool Buffer::readBody(LyXLex & lex)
                tmpbuf.readHeader(lex);
        }
 
-       if (text().read(*this, lex))
-               the_end_read = true;
-
-       return the_end_read;
+       return text().read(*this, lex);
 }
 
 
 // needed to insert the selection
-void Buffer::insertStringAsLines(ParagraphList::iterator & par, pos_type & pos,
-                                LyXFont const & fn, string const & str)
+void Buffer::insertStringAsLines(ParagraphList & pars,
+       pit_type & par, pos_type & pos,
+       LyXFont const & fn, string const & str, bool autobreakrows)
 {
-       LyXLayout_ptr const & layout = par->layout();
+       LyXLayout_ptr const & layout = pars[par].layout();
 
        LyXFont font = fn;
 
-       par->checkInsertChar(font);
+       pars[par].checkInsertChar(font);
        // insert the string, don't insert doublespace
        bool space_inserted = true;
-       bool autobreakrows = !par->inInset() ||
-               static_cast<InsetText *>(par->inInset())->getAutoBreakRows();
-       for(string::const_iterator cit = str.begin();
+       for (string::const_iterator cit = str.begin();
            cit != str.end(); ++cit) {
                if (*cit == '\n') {
-                       if (autobreakrows && (!par->empty() || par->allowEmpty())) {
+                       if (autobreakrows && (!pars[par].empty() || pars[par].allowEmpty())) {
                                breakParagraph(params(), paragraphs(), par, pos,
                                               layout->isEnvironment());
                                ++par;
@@ -492,18 +500,18 @@ void Buffer::insertStringAsLines(ParagraphList::iterator & par, pos_type & pos,
                        }
                        // do not insert consecutive spaces if !free_spacing
                } else if ((*cit == ' ' || *cit == '\t') &&
-                          space_inserted && !par->isFreeSpacing()) {
+                          space_inserted && !pars[par].isFreeSpacing()) {
                        continue;
                } else if (*cit == '\t') {
-                       if (!par->isFreeSpacing()) {
+                       if (!pars[par].isFreeSpacing()) {
                                // tabs are like spaces here
-                               par->insertChar(pos, ' ', font);
+                               pars[par].insertChar(pos, ' ', font);
                                ++pos;
                                space_inserted = true;
                        } else {
-                               const pos_type nb = 8 - pos % 8;
-                               for (pos_type a = 0; a < nb ; ++a) {
-                                       par->insertChar(pos, ' ', font);
+                               const pos_type n = 8 - pos % 8;
+                               for (pos_type i = 0; i < n; ++i) {
+                                       pars[par].insertChar(pos, ' ', font);
                                        ++pos;
                                }
                                space_inserted = true;
@@ -513,7 +521,7 @@ void Buffer::insertStringAsLines(ParagraphList::iterator & par, pos_type & pos,
                        continue;
                } else {
                        // just insert the character
-                       par->insertChar(pos, *cit, font);
+                       pars[par].insertChar(pos, *cit, font);
                        ++pos;
                        space_inserted = (*cit == ' ');
                }
@@ -525,12 +533,14 @@ void Buffer::insertStringAsLines(ParagraphList::iterator & par, pos_type & pos,
 bool Buffer::readFile(string const & filename)
 {
        // Check if the file is compressed.
-       string const format = getExtFromContents(filename);
+       string const format = getFormatFromContents(filename);
        if (format == "gzip" || format == "zip" || format == "compress") {
                params().compressed = true;
        }
 
-       bool ret = readFile(filename, paragraphs().begin());
+       // remove dummy empty par
+       paragraphs().clear();
+       bool ret = readFile(filename, paragraphs().size());
 
        // After we have read a file, we must ensure that the buffer
        // language is set and used in the gui.
@@ -541,11 +551,10 @@ bool Buffer::readFile(string const & filename)
 }
 
 
-bool Buffer::readFile(string const & filename, ParagraphList::iterator pit)
+bool Buffer::readFile(string const & filename, pit_type const pit)
 {
        LyXLex lex(0, 0);
        lex.setFile(filename);
-
        return readFile(lex, filename, pit);
 }
 
@@ -556,14 +565,13 @@ bool Buffer::fully_loaded() const
 }
 
 
-void Buffer::fully_loaded(bool value)
+void Buffer::fully_loaded(bool const value)
 {
        pimpl_->file_fully_loaded = value;
 }
 
 
-bool Buffer::readFile(LyXLex & lex, string const & filename,
-                     ParagraphList::iterator pit)
+bool Buffer::readFile(LyXLex & lex, string const & filename, pit_type const pit)
 {
        BOOST_ASSERT(!filename.empty());
 
@@ -592,7 +600,7 @@ bool Buffer::readFile(LyXLex & lex, string const & filename,
                return false;
        }
 
-       lex.eatLine();
+       lex.next();
        string tmp_format = lex.getString();
        //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
        // if present remove ".," from string.
@@ -600,18 +608,22 @@ bool Buffer::readFile(LyXLex & lex, string const & filename,
        //lyxerr << "           dot found at " << dot << endl;
        if (dot != string::npos)
                        tmp_format.erase(dot, 1);
-       int file_format = strToInt(tmp_format);
+       int const file_format = convert<int>(tmp_format);
        //lyxerr << "format: " << file_format << endl;
 
-       if (file_format > LYX_FORMAT) {
-               Alert::warning(_("Document format failure"),
-                              bformat(_("%1$s was created with a newer"
-                                        " version of LyX. This is likely to"
-                                        " cause problems."),
-                                        filename));
-       } else if (file_format < LYX_FORMAT) {
+       if (file_format != LYX_FORMAT) {
                string const tmpfile = tempName();
-               string command = LibFileSearch("lyx2lyx", "lyx2lyx");
+               if (tmpfile.empty()) {
+                       Alert::error(_("Conversion failed"),
+                                    bformat(_("%1$s is from an earlier"
+                                             " version of LyX, but a temporary"
+                                             " file for converting it could"
+                                             " not be created."),
+                                             filename));
+                       return false;
+               }
+               string command =
+                       "python " + LibFileSearch("lyx2lyx", "lyx2lyx");
                if (command.empty()) {
                        Alert::error(_("Conversion script not found"),
                                     bformat(_("%1$s is from an earlier"
@@ -622,7 +634,7 @@ bool Buffer::readFile(LyXLex & lex, string const & filename,
                        return false;
                }
                command += " -t"
-                       + tostr(LYX_FORMAT)
+                       + convert<string>(LYX_FORMAT)
                        + " -o " + tmpfile + ' '
                        + QuoteName(filename);
                lyxerr[Debug::INFO] << "Running '"
@@ -637,22 +649,25 @@ bool Buffer::readFile(LyXLex & lex, string const & filename,
                                              filename));
                        return false;
                } else {
-                       bool ret = readFile(tmpfile, pit);
+                       bool const ret = readFile(tmpfile, pit);
                        // Do stuff with tmpfile name and buffer name here.
                        return ret;
                }
 
        }
 
-       bool the_end = readBody(lex);
-       params().setPaperStuff();
-
-       if (!the_end) {
+       if (readDocument(lex)) {
                Alert::error(_("Document format failure"),
                             bformat(_("%1$s ended unexpectedly, which means"
                                       " that it is probably corrupted."),
                                       filename));
        }
+
+       //lyxerr << "removing " << MacroTable::localMacros().size()
+       //      << " temporary macro entries" << endl;
+       //MacroTable::localMacros().clear();
+       params().setPaperStuff();
+
        pimpl_->file_fully_loaded = true;
        return true;
 }
@@ -670,7 +685,7 @@ bool Buffer::save() const
                s = fileName() + '~';
                if (!lyxrc.backupdir_path.empty())
                        s = AddName(lyxrc.backupdir_path,
-                                   subst(os::slashify_path(s),'/','!'));
+                                   subst(os::internal_path(s),'/','!'));
 
                // Rename is the wrong way of making a backup,
                // this is the correct way.
@@ -725,9 +740,8 @@ bool Buffer::save() const
                removeAutosaveFile(fileName());
        } else {
                // Saving failed, so backup is not backup
-               if (lyxrc.make_backup) {
+               if (lyxrc.make_backup)
                        rename(s, fileName());
-               }
                return false;
        }
        return true;
@@ -736,25 +750,25 @@ bool Buffer::save() const
 
 bool Buffer::writeFile(string const & fname) const
 {
-       if (pimpl_->read_only && (fname == fileName())) {
+       if (pimpl_->read_only && fname == fileName())
                return false;
-       }
 
        FileInfo finfo(fname);
-       if (finfo.exist() && !finfo.writable()) {
+       if (finfo.exist() && !finfo.writable())
                return false;
-       }
 
-       bool retval;
+       bool retval = false;
 
        if (params().compressed) {
+#ifdef USE_COMPRESSION
                gz::ogzstream ofs(fname.c_str());
-
                if (!ofs)
                        return false;
 
                retval = do_writeFile(ofs);
-
+#else
+               return false;
+#endif
        } else {
                ofstream ofs(fname.c_str());
                if (!ofs)
@@ -769,7 +783,6 @@ bool Buffer::writeFile(string const & fname) const
 
 bool Buffer::do_writeFile(ostream & ofs) const
 {
-
 #ifdef HAVE_LOCALE
        // Use the standard "C" locale for file output.
        ofs.imbue(std::locale::classic());
@@ -780,18 +793,21 @@ bool Buffer::do_writeFile(ostream & ofs) const
        // write out a comment in the top of the file
        ofs << "#LyX " << lyx_version
            << " created this file. For more info see http://www.lyx.org/\n"
-           << "\\lyxformat " << LYX_FORMAT << "\n";
+           << "\\lyxformat " << LYX_FORMAT << "\n"
+           << "\\begin_document\n";
 
        // now write out the buffer parameters.
+       ofs << "\\begin_header\n";
        params().writeFile(ofs);
-
        ofs << "\\end_header\n";
 
        // write the text
+       ofs << "\n\\begin_body\n";
        text().write(*this, ofs);
+       ofs << "\n\\end_body\n";
 
        // Write marker that shows file is complete
-       ofs << "\n\\end_document" << endl;
+       ofs << "\\end_document" << endl;
 
        // Shouldn't really be needed....
        //ofs.close();
@@ -805,16 +821,9 @@ bool Buffer::do_writeFile(ostream & ofs) const
        //        which should include file system full errors.
 
        bool status = true;
-       if (!ofs.good()) {
+       if (!ofs) {
                status = false;
-#if 0
-               if (ofs.bad()) {
-                       lyxerr << "Buffer::writeFile: BAD ERROR!" << endl;
-               } else {
-                       lyxerr << "Buffer::writeFile: NOT SO BAD ERROR!"
-                              << endl;
-               }
-#endif
+               lyxerr << "File was not closed properly." << endl;
        }
 
        return status;
@@ -836,27 +845,26 @@ void Buffer::makeLaTeXFile(string const & fname,
                      runparams, output_preamble, output_body);
 
        ofs.close();
-       if (ofs.fail()) {
-               lyxerr << "File was not closed properly." << endl;
-       }
+       if (ofs.fail())
+               lyxerr << "File '" << fname << "' was not closed properly." << endl;
 }
 
 
 void Buffer::makeLaTeXFile(ostream & os,
                           string const & original_path,
                           OutputParams const & runparams_in,
-                          bool output_preamble, bool output_body)
+                          bool const output_preamble, bool const output_body)
 {
        OutputParams runparams = runparams_in;
-       niceFile() = runparams.nice; // this will be used by Insetincludes.
 
        // validate the buffer.
        lyxerr[Debug::LATEX] << "  Validating buffer..." << endl;
-       LaTeXFeatures features(*this, params());
+       LaTeXFeatures features(*this, params(), runparams.nice);
        validate(features);
        lyxerr[Debug::LATEX] << "  Buffer validation done." << endl;
 
        texrow().reset();
+
        // The starting paragraph of the coming rows is the
        // first paragraph of the document. (Asger)
        texrow().start(paragraphs().begin()->id(), 0);
@@ -877,6 +885,9 @@ void Buffer::makeLaTeXFile(ostream & os,
        // input@path is set when the actual parameter
        // original_path is set. This is done for usual tex-file, but not
        // for nice-latex-file. (Matthias 250696)
+       // Note that input@path is only needed for something the user does
+       // in the preamble, included .tex files or ERT, files included by
+       // LyX work without it.
        if (output_preamble) {
                if (!runparams.nice) {
                        // code for usual, NOT nice-latex-file
@@ -915,8 +926,22 @@ void Buffer::makeLaTeXFile(ostream & os,
                texrow().newline();
        }
 
+       // if we are doing a real file with body, even if this is the
+       // child of some other buffer, let's cut the link here.
+       // This happens for example if only a child document is printed.
+       string save_parentname;
+       if (output_preamble) {
+               save_parentname = params().parentname;
+               params().parentname.erase();
+       }
+
+       // the real stuff
        latexParagraphs(*this, paragraphs(), os, texrow(), runparams);
 
+       // Restore the parenthood if needed
+       if (output_preamble)
+               params().parentname = save_parentname;
+
        // add this just in case after all the paragraphs
        os << endl;
        texrow().newline();
@@ -944,9 +969,6 @@ void Buffer::makeLaTeXFile(ostream & os,
        lyxerr[Debug::INFO] << "Finished making LaTeX file." << endl;
        lyxerr[Debug::INFO] << "Row count was " << texrow().rows() - 1
                            << '.' << endl;
-
-       // we want this to be true outside previews (for insetexternal)
-       niceFile() = true;
 }
 
 
@@ -985,23 +1007,20 @@ bool Buffer::isSGML() const
 
 void Buffer::makeLinuxDocFile(string const & fname,
                              OutputParams const & runparams,
-                             bool body_only )
+                             bool const body_only)
 {
        ofstream ofs;
        if (!openFileWrite(ofs, fname))
                return;
 
-       niceFile() = runparams.nice; // this will be used by included files.
-
-       LaTeXFeatures features(*this, params());
-
+       LaTeXFeatures features(*this, params(), runparams.nice);
        validate(features);
 
        texrow().reset();
 
        LyXTextClass const & tclass = params().getLyXTextClass();
 
-       string top_element = tclass.latexname();
+       string const & top_element = tclass.latexname();
 
        if (!body_only) {
                ofs << tclass.class_header();
@@ -1018,12 +1037,12 @@ void Buffer::makeLinuxDocFile(string const & fname,
                ofs << ">\n\n";
 
                if (params().options.empty())
-                       sgml::openTag(ofs, 0, false, top_element);
+                       sgml::openTag(ofs, top_element);
                else {
                        string top = top_element;
                        top += ' ';
                        top += params().options;
-                       sgml::openTag(ofs, 0, false, top);
+                       sgml::openTag(ofs, top);
                }
        }
 
@@ -1035,39 +1054,53 @@ void Buffer::makeLinuxDocFile(string const & fname,
 
        if (!body_only) {
                ofs << "\n\n";
-               sgml::closeTag(ofs, 0, false, top_element);
+               sgml::closeTag(ofs, top_element);
        }
 
        ofs.close();
-       // How to check for successful close
-
-       // we want this to be true outside previews (for insetexternal)
-       niceFile() = true;
+       if (ofs.fail())
+               lyxerr << "File '" << fname << "' was not closed properly." << endl;
 }
 
 
 void Buffer::makeDocBookFile(string const & fname,
                             OutputParams const & runparams,
-                            bool only_body)
+                            bool const only_body)
 {
        ofstream ofs;
        if (!openFileWrite(ofs, fname))
                return;
 
-       niceFile() = runparams.nice; // this will be used by Insetincludes.
-
-       LaTeXFeatures features(*this, params());
+       LaTeXFeatures features(*this, params(), runparams.nice);
        validate(features);
 
        texrow().reset();
 
        LyXTextClass const & tclass = params().getLyXTextClass();
-       string top_element = tclass.latexname();
+       string const & top_element = tclass.latexname();
 
        if (!only_body) {
-               ofs << subst(tclass.class_header(), "#", top_element);
+               if (runparams.flavor == OutputParams::XML)
+                       ofs << "<?xml version=\"1.0\" encoding=\""
+                           << params().language->encoding()->Name() << "\"?>\n";
+
+               ofs << "<!DOCTYPE " << top_element << " ";
+
+               if (! tclass.class_header().empty()) ofs << tclass.class_header();
+               else if (runparams.flavor == OutputParams::XML)
+                       ofs << "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
+                           << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
+               else
+                       ofs << " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
 
                string preamble = params().preamble;
+               if (runparams.flavor != OutputParams::XML ) {
+                       preamble += "<!ENTITY % output.print.png \"IGNORE\">\n";
+                       preamble += "<!ENTITY % output.print.pdf \"IGNORE\">\n";
+                       preamble += "<!ENTITY % output.print.eps \"IGNORE\">\n";
+                       preamble += "<!ENTITY % output.print.bmp \"IGNORE\">\n";
+               }
+
                string const name = runparams.nice ? ChangeExtension(pimpl_->filename, ".sgml")
                         : fname;
                preamble += features.getIncludedFiles(name);
@@ -1081,29 +1114,31 @@ void Buffer::makeDocBookFile(string const & fname,
 
        string top = top_element;
        top += " lang=\"";
-       top += params().language->code();
+       if (runparams.flavor == OutputParams::XML)
+               top += params().language->code();
+       else
+               top += params().language->code().substr(0,2);
        top += '"';
 
        if (!params().options.empty()) {
                top += ' ';
                top += params().options;
        }
-       sgml::openTag(ofs, 0, false, top);
 
-       ofs << "<!-- SGML/XML file was created by LyX " << lyx_version
+       ofs << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
+           << " file was created by LyX " << lyx_version
            << "\n  See http://www.lyx.org/ for more information -->\n";
 
        params().getLyXTextClass().counters().reset();
-       docbookParagraphs(*this, paragraphs(), ofs, runparams);
 
-       ofs << "\n\n";
-       sgml::closeTag(ofs, 0, false, top_element);
+       sgml::openTag(ofs, top);
+       ofs << '\n';
+       docbookParagraphs(paragraphs(), *this, ofs, runparams);
+       sgml::closeTag(ofs, top_element);
 
        ofs.close();
-       // How to check for successful close
-
-       // we want this to be true outside previews (for insetexternal)
-       niceFile() = true;
+       if (ofs.fail())
+               lyxerr << "File '" << fname << "' was not closed properly." << endl;
 }
 
 
@@ -1115,12 +1150,8 @@ int Buffer::runChktex()
 
        // get LaTeX-Filename
        string const name = getLatexName();
-       string path = filePath();
-
-       string const org_path = path;
-       if (lyxrc.use_tempdir || !IsDirWriteable(path)) {
-               path = temppath();
-       }
+       string const path = temppath();
+       string const org_path = filePath();
 
        Path p(path); // path to LaTeX file
        message(_("Running chktex..."));
@@ -1133,7 +1164,7 @@ int Buffer::runChktex()
 
        TeXErrors terr;
        Chktex chktex(lyxrc.chktex_command, name, filePath());
-       int res = chktex.run(terr); // run chktex
+       int const res = chktex.run(terr); // run chktex
 
        if (res == -1) {
                Alert::error(_("chktex failure"),
@@ -1153,7 +1184,8 @@ void Buffer::validate(LaTeXFeatures & features) const
 {
        LyXTextClass const & tclass = params().getLyXTextClass();
 
-       if (params().tracking_changes) {
+       if (features.isAvailable("dvipost") && params().tracking_changes
+               && params().output_changes) {
                features.require("dvipost");
                features.require("color");
        }
@@ -1196,52 +1228,48 @@ void Buffer::validate(LaTeXFeatures & features) const
 }
 
 
-void Buffer::getLabelList(std::vector<string> & list) const
+void Buffer::getLabelList(vector<string> & list) const
 {
        /// if this is a child document and the parent is already loaded
        /// Use the parent's list instead  [ale990407]
-       if (!params().parentname.empty()
-           && bufferlist.exists(params().parentname)) {
-               Buffer const * tmp = bufferlist.getBuffer(params().parentname);
-               if (tmp) {
-                       tmp->getLabelList(list);
-                       return;
-               }
+       Buffer const * tmp = getMasterBuffer();
+       if (!tmp) {
+               lyxerr << "getMasterBuffer() failed!" << endl;
+               BOOST_ASSERT(tmp);
        }
-
-       for (inset_iterator it = inset_const_iterator_begin();
-            it != inset_const_iterator_end(); ++it) {
-               it->getLabelList(*this, list);
+       if (tmp != this) {
+               tmp->getLabelList(list);
+               return;
        }
+
+       for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
+               it.nextInset()->getLabelList(*this, list);
 }
 
 
 // This is also a buffer property (ale)
-void Buffer::fillWithBibKeys(std::vector<std::pair<string, string> > & keys)
+void Buffer::fillWithBibKeys(vector<pair<string, string> > & keys)
        const
 {
        /// if this is a child document and the parent is already loaded
        /// use the parent's list instead  [ale990412]
-       if (!params().parentname.empty() &&
-           bufferlist.exists(params().parentname)) {
-               Buffer const * tmp = bufferlist.getBuffer(params().parentname);
-               if (tmp) {
-                       tmp->fillWithBibKeys(keys);
-                       return;
-               }
+       Buffer const * tmp = getMasterBuffer();
+       BOOST_ASSERT(tmp);
+       if (tmp != this) {
+               tmp->fillWithBibKeys(keys);
+               return;
        }
 
-       for (inset_iterator it = inset_const_iterator_begin();
-               it != inset_const_iterator_end(); ++it) {
-               if (it->lyxCode() == InsetOld::BIBTEX_CODE) {
+       for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
+               if (it->lyxCode() == InsetBase::BIBTEX_CODE) {
                        InsetBibtex const & inset =
                                dynamic_cast<InsetBibtex const &>(*it);
                        inset.fillWithBibKeys(*this, keys);
-               } else if (it->lyxCode() == InsetOld::INCLUDE_CODE) {
+               } else if (it->lyxCode() == InsetBase::INCLUDE_CODE) {
                        InsetInclude const & inset =
                                dynamic_cast<InsetInclude const &>(*it);
                        inset.fillWithBibKeys(*this, keys);
-               } else if (it->lyxCode() == InsetOld::BIBITEM_CODE) {
+               } else if (it->lyxCode() == InsetBase::BIBITEM_CODE) {
                        InsetBibitem const & inset =
                                dynamic_cast<InsetBibitem const &>(*it);
                        string const key = inset.getContents();
@@ -1256,7 +1284,7 @@ void Buffer::fillWithBibKeys(std::vector<std::pair<string, string> > & keys)
 
 bool Buffer::isDepClean(string const & name) const
 {
-       DepClean::const_iterator it = pimpl_->dep_clean.find(name);
+       DepClean::const_iterator const it = pimpl_->dep_clean.find(name);
        if (it == pimpl_->dep_clean.end())
                return true;
        return it->second;
@@ -1296,27 +1324,32 @@ bool Buffer::dispatch(FuncRequest const & func, bool * result)
 
 void Buffer::changeLanguage(Language const * from, Language const * to)
 {
+       BOOST_ASSERT(from);
+       BOOST_ASSERT(to);
+
        lyxerr << "Changing Language!" << endl;
 
        // Take care of l10n/i18n
        updateDocLang(to);
 
-       ParIterator end = par_iterator_end();
-       for (ParIterator it = par_iterator_begin(); it != end; ++it)
-               it->changeLanguage(params(), from, to);
+       for_each(par_iterator_begin(),
+                par_iterator_end(),
+                bind(&Paragraph::changeLanguage, _1, params(), from, to));
 }
 
 
 void Buffer::updateDocLang(Language const * nlang)
 {
+       BOOST_ASSERT(nlang);
+
        pimpl_->messages.reset(new Messages(nlang->code()));
 }
 
 
-bool Buffer::isMultiLingual()
+bool Buffer::isMultiLingual() const
 {
-       ParIterator end = par_iterator_end();
-       for (ParIterator it = par_iterator_begin(); it != end; ++it)
+       ParConstIterator end = par_iterator_end();
+       for (ParConstIterator it = par_iterator_begin(); it != end; ++it)
                if (it->isMultiLingual(params()))
                        return true;
 
@@ -1324,24 +1357,11 @@ bool Buffer::isMultiLingual()
 }
 
 
-void Buffer::inset_iterator::setParagraph()
+ParIterator Buffer::getParFromID(int const id) const
 {
-       while (pit != pend) {
-               it = pit->insetlist.begin();
-               if (it != pit->insetlist.end())
-                       return;
-               ++pit;
-       }
-}
-
-
-ParIterator Buffer::getParFromID(int id) const
-{
-#warning FIXME: const correctness! (Andre)
-       ParIterator it = const_cast<Buffer*>(this)->par_iterator_begin();
-       ParIterator end = const_cast<Buffer*>(this)->par_iterator_end();
+       ParConstIterator it = par_iterator_begin();
+       ParConstIterator const end = par_iterator_end();
 
-#warning FIXME, perhaps this func should return a ParIterator? (Lgb)
        if (id < 0) {
                // John says this is called with id == -1 from undo
                lyxerr << "getParFromID(), id: " << id << endl;
@@ -1356,60 +1376,34 @@ ParIterator Buffer::getParFromID(int id) const
 }
 
 
-bool Buffer::hasParWithID(int id) const
-{
-       ParConstIterator it = par_iterator_begin();
-       ParConstIterator end = par_iterator_end();
-
-       if (id < 0) {
-               // John says this is called with id == -1 from undo
-               lyxerr << "hasParWithID(), id: " << id << endl;
-               return 0;
-       }
-
-       for (; it != end; ++it)
-               if (it->id() == id)
-                       return true;
-
-       return false;
-}
-
-
-PosIterator Buffer::pos_iterator_begin()
+bool Buffer::hasParWithID(int const id) const
 {
-       return PosIterator(&paragraphs(), paragraphs().begin(), 0);
-}
-
-
-PosIterator Buffer::pos_iterator_end()
-{
-       return PosIterator(&paragraphs(), paragraphs().end(), 0);
+       ParConstIterator const it = getParFromID(id);
+       return it != par_iterator_end();
 }
 
 
 ParIterator Buffer::par_iterator_begin()
 {
-       return ParIterator(paragraphs().begin(), paragraphs());
+       return ::par_iterator_begin(inset());
 }
 
 
 ParIterator Buffer::par_iterator_end()
 {
-       return ParIterator(paragraphs().end(), paragraphs());
+       return ::par_iterator_end(inset());
 }
 
 
 ParConstIterator Buffer::par_iterator_begin() const
 {
-       ParagraphList const & pars = paragraphs();
-       return ParConstIterator(const_cast<ParagraphList&>(pars).begin(), pars);
+       return ::par_const_iterator_begin(inset());
 }
 
 
 ParConstIterator Buffer::par_iterator_end() const
 {
-       ParagraphList const & pars = paragraphs();
-       return ParConstIterator(const_cast<ParagraphList&>(pars).end(), pars);
+       return ::par_const_iterator_end(inset());
 }
 
 
@@ -1465,12 +1459,15 @@ void Buffer::setUnnamed(bool flag)
 }
 
 
-bool Buffer::isUnnamed()
+bool Buffer::isUnnamed() const
 {
        return pimpl_->unnamed;
 }
 
 
+#ifdef WITH_WARNINGS
+#warning this function should be moved to buffer_pimpl.C
+#endif
 void Buffer::markDirty()
 {
        if (pimpl_->lyx_clean) {
@@ -1512,99 +1509,57 @@ void Buffer::setParentName(string const & name)
 }
 
 
-Buffer::inset_iterator::inset_iterator()
-       : pit(), pend()
-{}
-
-
-Buffer::inset_iterator::inset_iterator(base_type p, base_type e)
-       : pit(p), pend(e)
-{
-       setParagraph();
-}
-
-
-Buffer::inset_iterator Buffer::inset_iterator_begin()
-{
-       return inset_iterator(paragraphs().begin(), paragraphs().end());
-}
-
-
-Buffer::inset_iterator Buffer::inset_iterator_end()
-{
-       return inset_iterator(paragraphs().end(), paragraphs().end());
-}
-
-
-Buffer::inset_iterator Buffer::inset_const_iterator_begin() const
-{
-       ParagraphList & pars = const_cast<ParagraphList&>(paragraphs());
-       return inset_iterator(pars.begin(), pars.end());
-}
-
-
-Buffer::inset_iterator Buffer::inset_const_iterator_end() const
-{
-       ParagraphList & pars = const_cast<ParagraphList&>(paragraphs());
-       return inset_iterator(pars.end(), pars.end());
-}
-
-
-Buffer::inset_iterator & Buffer::inset_iterator::operator++()
+Buffer const * Buffer::getMasterBuffer() const
 {
-       if (pit != pend) {
-               ++it;
-               if (it == pit->insetlist.end()) {
-                       ++pit;
-                       setParagraph();
-               }
+       if (!params().parentname.empty()
+           && bufferlist.exists(params().parentname)) {
+               Buffer const * buf = bufferlist.getBuffer(params().parentname);
+               if (buf)
+                       return buf->getMasterBuffer();
        }
-       return *this;
-}
-
 
-Buffer::inset_iterator Buffer::inset_iterator::operator++(int)
-{
-       inset_iterator tmp = *this;
-       ++*this;
-       return tmp;
+       return this;
 }
 
 
-Buffer::inset_iterator::reference Buffer::inset_iterator::operator*()
+MacroData const & Buffer::getMacro(std::string const & name) const
 {
-       return *it->inset;
+       return pimpl_->macros.get(name);
 }
 
 
-Buffer::inset_iterator::pointer Buffer::inset_iterator::operator->()
+bool Buffer::hasMacro(string const & name) const
 {
-       return it->inset;
+       return pimpl_->macros.has(name);
 }
 
 
-ParagraphList::iterator Buffer::inset_iterator::getPar() const
+void Buffer::insertMacro(string const & name, MacroData const & data)
 {
-       return pit;
+       MacroTable::globalMacros().insert(name, data);
+       pimpl_->macros.insert(name, data);
 }
 
 
-lyx::pos_type Buffer::inset_iterator::getPos() const
+void Buffer::buildMacros()
 {
-       return it->pos;
-}
+       // Start with global table.
+       pimpl_->macros = MacroTable::globalMacros();
 
-
-bool operator==(Buffer::inset_iterator const & iter1,
-               Buffer::inset_iterator const & iter2)
-{
-       return iter1.pit == iter2.pit
-               && (iter1.pit == iter1.pend || iter1.it == iter2.it);
-}
-
-
-bool operator!=(Buffer::inset_iterator const & iter1,
-               Buffer::inset_iterator const & iter2)
-{
-       return !(iter1 == iter2);
+       // Now add our own.
+       ParagraphList & pars = text().paragraphs();
+       for (size_t i = 0, n = pars.size(); i != n; ++i) {
+               //lyxerr << "searching main par " << i
+               //      << " for macro definitions" << std::endl;
+               InsetList::iterator it = pars[i].insetlist.begin();
+               InsetList::iterator end = pars[i].insetlist.end();
+               for ( ; it != end; ++it) {
+                       //lyxerr << "found inset code " << it->inset->lyxCode() << std::endl;
+                       if (it->inset->lyxCode() == InsetBase::MATHMACRO_CODE) {
+                               MathMacroTemplate & mac
+                                       = static_cast<MathMacroTemplate &>(*it->inset);
+                               insertMacro(mac.name(), mac.asMacroData());
+                       }
+               }
+       }
 }