]> git.lyx.org Git - lyx.git/blobdiff - src/BufferParams.cpp
Implement dynamic quotation marks
[lyx.git] / src / BufferParams.cpp
index 2d113bad4579bbc7851700c03ce4e59048a92c7e..c48e1cfcca971fdcbcd4147a39666035f58b9584 100644 (file)
@@ -38,6 +38,7 @@
 #include "LyXRC.h"
 #include "OutputParams.h"
 #include "Spacing.h"
+#include "texstream.h"
 #include "TexRow.h"
 #include "VSpace.h"
 #include "PDFOptions.h"
@@ -70,8 +71,9 @@ static char const * const string_paragraph_separation[] = {
 };
 
 
-static char const * const string_quotes_language[] = {
-       "english", "swedish", "german", "polish", "french", "danish", ""
+static char const * const string_quotes_style[] = {
+       "english", "swedish", "german", "polish", "swiss", "danish", "plain",
+       "british", "swedishg", "french", "frenchin", "russian", ""
 };
 
 
@@ -126,27 +128,33 @@ ParSepTranslator const & parseptranslator()
 }
 
 
-// Quotes language
-typedef Translator<string, InsetQuotes::QuoteLanguage> QuotesLangTranslator;
+// Quotes style
+typedef Translator<string, InsetQuotesParams::QuoteStyle> QuotesStyleTranslator;
 
 
-QuotesLangTranslator const init_quoteslangtranslator()
+QuotesStyleTranslator const init_quotesstyletranslator()
 {
-       QuotesLangTranslator translator
-               (string_quotes_language[0], InsetQuotes::EnglishQuotes);
-       translator.addPair(string_quotes_language[1], InsetQuotes::SwedishQuotes);
-       translator.addPair(string_quotes_language[2], InsetQuotes::GermanQuotes);
-       translator.addPair(string_quotes_language[3], InsetQuotes::PolishQuotes);
-       translator.addPair(string_quotes_language[4], InsetQuotes::FrenchQuotes);
-       translator.addPair(string_quotes_language[5], InsetQuotes::DanishQuotes);
+       QuotesStyleTranslator translator
+               (string_quotes_style[0], InsetQuotesParams::EnglishQuotes);
+       translator.addPair(string_quotes_style[1], InsetQuotesParams::SwedishQuotes);
+       translator.addPair(string_quotes_style[2], InsetQuotesParams::GermanQuotes);
+       translator.addPair(string_quotes_style[3], InsetQuotesParams::PolishQuotes);
+       translator.addPair(string_quotes_style[4], InsetQuotesParams::SwissQuotes);
+       translator.addPair(string_quotes_style[5], InsetQuotesParams::DanishQuotes);
+       translator.addPair(string_quotes_style[6], InsetQuotesParams::PlainQuotes);
+       translator.addPair(string_quotes_style[7], InsetQuotesParams::BritishQuotes);
+       translator.addPair(string_quotes_style[8], InsetQuotesParams::SwedishGQuotes);
+       translator.addPair(string_quotes_style[9], InsetQuotesParams::FrenchQuotes);
+       translator.addPair(string_quotes_style[10], InsetQuotesParams::FrenchINQuotes);
+       translator.addPair(string_quotes_style[11], InsetQuotesParams::RussianQuotes);
        return translator;
 }
 
 
-QuotesLangTranslator const & quoteslangtranslator()
+QuotesStyleTranslator const & quotesstyletranslator()
 {
-       static QuotesLangTranslator const translator =
-               init_quoteslangtranslator();
+       static QuotesStyleTranslator const translator =
+               init_quotesstyletranslator();
        return translator;
 }
 
@@ -304,6 +312,35 @@ SpaceTranslator const & spacetranslator()
        return translator;
 }
 
+
+bool inSystemDir(FileName const & document_dir, string & system_dir)
+{
+       // A document is assumed to be in a system LyX directory (not
+       // necessarily the system directory of the running instance)
+       // if both "configure.py" and "chkconfig.ltx" are found in
+       // either document_dir/../ or document_dir/../../.
+       // If true, the system directory path is returned in system_dir
+       // with a trailing path separator.
+
+       string const msg = "Checking whether document is in a system dir...";
+
+       string dir = document_dir.absFileName();
+
+       for (int i = 0; i < 2; ++i) {
+               dir = addPath(dir, "..");
+               if (!fileSearch(dir, "configure.py").empty() &&
+                   !fileSearch(dir, "chkconfig.ltx").empty()) {
+                       LYXERR(Debug::FILES, msg << " yes");
+                       system_dir = addPath(FileName(dir).realPath(), "");
+                       return true;
+               }
+       }
+
+       LYXERR(Debug::FILES, msg << " no");
+       system_dir = string();
+       return false;
+}
+
 } // anon namespace
 
 
@@ -325,11 +362,16 @@ public:
        VSpace defskip;
        PDFOptions pdfoptions;
        LayoutFileIndex baseClass_;
+       FormatList exportableFormatList;
+       FormatList viewableFormatList;
+       bool isViewCacheValid;
+       bool isExportCacheValid;
 };
 
 
 BufferParams::Impl::Impl()
-       : defskip(VSpace::MEDSKIP), baseClass_(string(""))
+       : defskip(VSpace::MEDSKIP), baseClass_(string("")),
+         isViewCacheValid(false), isExportCacheValid(false)
 {
        // set initial author
        // FIXME UNICODE
@@ -359,7 +401,8 @@ BufferParams::BufferParams()
        cite_engine_type_ = ENGINE_TYPE_DEFAULT;
        makeDocumentClass();
        paragraph_separation = ParagraphIndentSeparation;
-       quotes_language = InsetQuotes::EnglishQuotes;
+       quotes_style = InsetQuotesParams::EnglishQuotes;
+       dynamic_quotes = false;
        fontsize = "default";
 
        /*  PaperLayout */
@@ -682,8 +725,12 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                origin = lex.getString();
                string const sysdirprefix = "/systemlyxdir/";
                if (prefixIs(origin, sysdirprefix)) {
-                       origin.replace(0, sysdirprefix.length() - 1,
-                               package().system_support().absFileName());
+                       string docsys;
+                       if (inSystemDir(filepath, docsys))
+                               origin.replace(0, sysdirprefix.length() - 1, docsys);
+                       else
+                               origin.replace(0, sysdirprefix.length() - 1,
+                                       package().system_support().absFileName());
                }
        } else if (token == "\\begin_preamble") {
                readPreamble(lex);
@@ -789,10 +836,12 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                if (pimpl_->defskip.kind() == VSpace::DEFSKIP)
                        // that is invalid
                        pimpl_->defskip = VSpace(VSpace::MEDSKIP);
-       } else if (token == "\\quotes_language") {
-               string quotes_lang;
-               lex >> quotes_lang;
-               quotes_language = quoteslangtranslator().find(quotes_lang);
+       } else if (token == "\\quotes_style") {
+               string qstyle;
+               lex >> qstyle;
+               quotes_style = quotesstyletranslator().find(qstyle);
+       } else if (token == "\\dynamic_quotes") {
+               lex >> dynamic_quotes;
        } else if (token == "\\papersize") {
                string ppsize;
                lex >> ppsize;
@@ -1027,8 +1076,10 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const
        // the document directory (must end with a path separator)
        // realPath() is used to resolve symlinks, while addPath(..., "")
        // ensures a trailing path separator.
+       string docsys;
        string filepath = addPath(buf->fileName().onlyPath().realPath(), "");
-       string const sysdir = addPath(package().system_support().realPath(), "");
+       string const sysdir = inSystemDir(FileName(filepath), docsys) ? docsys
+                       : addPath(package().system_support().realPath(), "");
        string const relpath =
                to_utf8(makeRelPath(from_utf8(filepath), from_utf8(sysdir)));
        if (!prefixIs(relpath, "../") && !FileName::isAbsolute(relpath))
@@ -1046,9 +1097,9 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const
        // then the preamble
        if (!preamble.empty()) {
                // remove '\n' from the end of preamble
-               string const tmppreamble = rtrim(preamble, "\n");
+               docstring const tmppreamble = rtrim(preamble, "\n");
                os << "\\begin_preamble\n"
-                  << tmppreamble
+                  << to_utf8(tmppreamble)
                   << "\n\\end_preamble\n";
        }
 
@@ -1099,20 +1150,20 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const
           << convert<string>(maintain_unincluded_children) << '\n';
 
        // local layout information
-       string const local_layout = getLocalLayout(false);
+       docstring const local_layout = getLocalLayout(false);
        if (!local_layout.empty()) {
                // remove '\n' from the end
-               string const tmplocal = rtrim(local_layout, "\n");
+               docstring const tmplocal = rtrim(local_layout, "\n");
                os << "\\begin_local_layout\n"
-                  << tmplocal
+                  << to_utf8(tmplocal)
                   << "\n\\end_local_layout\n";
        }
-       string const forced_local_layout = getLocalLayout(true);
+       docstring const forced_local_layout = getLocalLayout(true);
        if (!forced_local_layout.empty()) {
                // remove '\n' from the end
-               string const tmplocal = rtrim(forced_local_layout, "\n");
+               docstring const tmplocal = rtrim(forced_local_layout, "\n");
                os << "\\begin_forced_local_layout\n"
-                  << tmplocal
+                  << to_utf8(tmplocal)
                   << "\n\\end_forced_local_layout\n";
        }
 
@@ -1258,8 +1309,9 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const
                os << "\n\\paragraph_indentation " << getIndentation().asLyXCommand();
        else
                os << "\n\\defskip " << getDefSkip().asLyXCommand();
-       os << "\n\\quotes_language "
-          << string_quotes_language[quotes_language]
+       os << "\n\\quotes_style "
+          << string_quotes_style[quotes_style]
+          << "\n\\dynamic_quotes " << dynamic_quotes
           << "\n\\papercolumns " << columns
           << "\n\\papersides " << sides
           << "\n\\paperpagestyle " << pagestyle << '\n';
@@ -1411,7 +1463,7 @@ void BufferParams::validate(LaTeXFeatures & features) const
 
        // some languages are only available via polyglossia
        if (features.hasPolyglossiaExclusiveLanguages())
-          features.require("polyglossia");
+               features.require("polyglossia");
 
        if (useNonTeXFonts && fontsMath() != "auto")
                features.require("unicode-math");
@@ -1589,7 +1641,8 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                os << from_ascii(ams);
 
        if (useNonTeXFonts) {
-               os << "\\usepackage{fontspec}\n";
+               if (!features.isProvided("fontspec"))
+                       os << "\\usepackage{fontspec}\n";
                if (features.mustProvide("unicode-math")
                    && features.isAvailable("unicode-math"))
                        os << "\\usepackage{unicode-math}\n";
@@ -1862,20 +1915,20 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        }
 
        // Now insert the LyX specific LaTeX commands...
-       docstring lyxpreamble;
        features.resolveAlternatives();
+       features.expandMultiples();
 
        if (output_sync) {
                if (!output_sync_macro.empty())
-                       lyxpreamble += from_utf8(output_sync_macro) +"\n";
+                       os << from_utf8(output_sync_macro) +"\n";
                else if (features.runparams().flavor == OutputParams::LATEX)
-                       lyxpreamble += "\\usepackage[active]{srcltx}\n";
+                       os << "\\usepackage[active]{srcltx}\n";
                else if (features.runparams().flavor == OutputParams::PDFLATEX)
-                       lyxpreamble += "\\synctex=-1\n";
+                       os << "\\synctex=-1\n";
        }
 
        // The package options (via \PassOptionsToPackage)
-       lyxpreamble += from_ascii(features.getPackageOptions());
+       os << from_ascii(features.getPackageOptions());
 
        // due to interferences with babel and hyperref, the color package has to
        // be loaded (when it is not already loaded) before babel when hyperref
@@ -1883,7 +1936,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        // http://www.lyx.org/trac/ticket/5291
        // we decided therefore to load color always before babel, see
        // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg144349.html
-       lyxpreamble += from_ascii(features.getColorOptions());
+       os << from_ascii(features.getColorOptions());
 
        // If we use hyperref, jurabib, japanese, varioref or vietnamese,
        // we have to call babel before
@@ -1893,15 +1946,15 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                || features.isRequired("varioref")
                || features.isRequired("vietnamese")
                || features.isRequired("japanese"))) {
+                       os << features.getBabelPresettings();
                        // FIXME UNICODE
-                       lyxpreamble += from_utf8(features.getBabelPresettings());
-                       lyxpreamble += from_utf8(babelCall(language_options.str(),
-                                                          features.needBabelLangOptions())) + '\n';
-                       lyxpreamble += from_utf8(features.getBabelPostsettings());
+                       os << from_utf8(babelCall(language_options.str(),
+                                                 features.needBabelLangOptions())) + '\n';
+                       os << features.getBabelPostsettings();
        }
 
        // The optional packages;
-       lyxpreamble += from_ascii(features.getPackages());
+       os << from_ascii(features.getPackages());
 
        // Additional Indices
        if (features.isRequired("splitidx")) {
@@ -1919,16 +1972,16 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                                                  "representable in the current encoding and therefore have been omitted:\n%1$s."),
                                                indexname_latex.second));
                        }
-                       lyxpreamble += "\\newindex[";
-                       lyxpreamble += indexname_latex.first;
-                       lyxpreamble += "]{";
-                       lyxpreamble += escape(iit->shortcut());
-                       lyxpreamble += "}\n";
+                       os << "\\newindex[";
+                       os << indexname_latex.first;
+                       os << "]{";
+                       os << escape(iit->shortcut());
+                       os << "}\n";
                }
        }
 
        // Line spacing
-       lyxpreamble += from_utf8(spacing().writePreamble(features.isProvided("SetSpace")));
+       os << from_utf8(spacing().writePreamble(features.isProvided("SetSpace")));
 
        // PDF support.
        // * Hyperref manual: "Make sure it comes last of your loaded
@@ -1940,66 +1993,62 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        //   avoid errors with algorithm floats.
        // use hyperref explicitly if it is required
        if (features.isRequired("hyperref")) {
-               // pass what we have to stream here, since we need
-               // to access the stream itself in PDFOptions.
-               os << lyxpreamble;
-
                OutputParams tmp_params = features.runparams();
                pdfoptions().writeLaTeX(tmp_params, os,
                                        features.isProvided("hyperref"));
-               // set back for the rest
-               lyxpreamble.clear();
                // correctly break URLs with hyperref and dvi output
                if (features.runparams().flavor == OutputParams::LATEX
                    && features.isAvailable("breakurl"))
-                       lyxpreamble += "\\usepackage{breakurl}\n";
+                       os << "\\usepackage{breakurl}\n";
        } else if (features.isRequired("nameref"))
                // hyperref loads this automatically
-               lyxpreamble += "\\usepackage{nameref}\n";
+               os << "\\usepackage{nameref}\n";
 
        // bibtopic needs to be loaded after hyperref.
        // the dot provides the aux file naming which LyX can detect.
        if (features.mustProvide("bibtopic"))
-               lyxpreamble += "\\usepackage[dot]{bibtopic}\n";
+               os << "\\usepackage[dot]{bibtopic}\n";
 
        // Will be surrounded by \makeatletter and \makeatother when not empty
-       docstring atlyxpreamble;
+       otexstringstream atlyxpreamble;
 
        // Some macros LyX will need
-       docstring tmppreamble(features.getMacros());
-
-       if (!tmppreamble.empty())
-               atlyxpreamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
-                       "LyX specific LaTeX commands.\n"
-                       + tmppreamble + '\n';
-
+       {
+               TexString tmppreamble = features.getMacros();
+               if (!tmppreamble.str.empty())
+                       atlyxpreamble << "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
+                                        "LyX specific LaTeX commands.\n"
+                                     << move(tmppreamble)
+                                     << '\n';
+       }
        // the text class specific preamble
-       tmppreamble = features.getTClassPreamble();
-       if (!tmppreamble.empty())
-               atlyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
-                       "Textclass specific LaTeX commands.\n"
-                       + tmppreamble + '\n';
-
+       {
+               docstring tmppreamble = features.getTClassPreamble();
+               if (!tmppreamble.empty())
+                       atlyxpreamble << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
+                                        "Textclass specific LaTeX commands.\n"
+                                     << tmppreamble
+                                     << '\n';
+       }
        // suppress date if selected
        // use \@ifundefined because we cannot be sure that every document class
        // has a \date command
        if (suppress_date)
-               atlyxpreamble += "\\@ifundefined{date}{}{\\date{}}\n";
+               atlyxpreamble << "\\@ifundefined{date}{}{\\date{}}\n";
 
        /* the user-defined preamble */
        if (!containsOnly(preamble, " \n\t")) {
                // FIXME UNICODE
-               atlyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
-                       "User specified LaTeX commands.\n";
+               atlyxpreamble << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
+                                "User specified LaTeX commands.\n";
 
                // Check if the user preamble contains uncodable glyphs
-               docstring const u_preamble = from_utf8(preamble);
                odocstringstream user_preamble;
                docstring uncodable_glyphs;
                Encoding const * const enc = features.runparams().encoding;
                if (enc) {
-                       for (size_t n = 0; n < u_preamble.size(); ++n) {
-                               char_type c = u_preamble[n];
+                       for (size_t n = 0; n < preamble.size(); ++n) {
+                               char_type c = preamble[n];
                                if (!enc->encodable(c)) {
                                        docstring const glyph(1, c);
                                        LYXERR0("Uncodable character '"
@@ -2016,7 +2065,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                                        user_preamble.put(c);
                        }
                } else
-                       user_preamble << u_preamble;
+                       user_preamble << preamble;
 
                // On BUFFER_VIEW|UPDATE, warn user if we found uncodable glyphs
                if (!features.runparams().dryrun && !uncodable_glyphs.empty()) {
@@ -2034,7 +2083,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                                    "preamble code accordingly."),
                                  uncodable_glyphs));
                }
-               atlyxpreamble += user_preamble.str() + '\n';
+               atlyxpreamble << user_preamble.str() << '\n';
        }
 
        // footmisc must be loaded after setspace
@@ -2042,7 +2091,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        // preamble. For that reason we also pass the options via
        // \PassOptionsToPackage in getPreamble() and not here.
        if (features.mustProvide("footmisc"))
-               atlyxpreamble += "\\usepackage{footmisc}\n";
+               atlyxpreamble << "\\usepackage{footmisc}\n";
 
        // subfig loads internally the LaTeX package "caption". As
        // caption is a very popular package, users will load it in
@@ -2054,11 +2103,10 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        // koma's own caption commands are used instead of caption. We
        // use \PassOptionsToPackage here because the user could have
        // already loaded subfig in the preamble.
-       if (features.isRequired("subfig")) {
-               atlyxpreamble += "\\@ifundefined{showcaptionsetup}{}{%\n"
-                       " \\PassOptionsToPackage{caption=false}{subfig}}\n"
-                       "\\usepackage{subfig}\n";
-       }
+       if (features.mustProvide("subfig"))
+               atlyxpreamble << "\\@ifundefined{showcaptionsetup}{}{%\n"
+                                " \\PassOptionsToPackage{caption=false}{subfig}}\n"
+                                "\\usepackage{subfig}\n";
 
        // Itemize bullet settings need to be last in case the user
        // defines their own bullets that use a package included
@@ -2093,11 +2141,12 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        }
 
        if (!bullets_def.empty())
-               atlyxpreamble += bullets_def + "}\n\n";
+               atlyxpreamble << bullets_def << "}\n\n";
 
        if (!atlyxpreamble.empty())
-               lyxpreamble += "\n\\makeatletter\n"
-                       + atlyxpreamble + "\\makeatother\n\n";
+               os << "\n\\makeatletter\n"
+                  << atlyxpreamble.release()
+                  << "\\makeatother\n\n";
 
        // We try to load babel late, in case it interferes with other packages.
        // Jurabib, hyperref, varioref, bicaption and listings (bug 8995) have to be
@@ -2107,68 +2156,70 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                && !features.isRequired("varioref")
            && !features.isRequired("vietnamese")
            && !features.isRequired("japanese")) {
+               os << features.getBabelPresettings();
                // FIXME UNICODE
-               lyxpreamble += from_utf8(features.getBabelPresettings());
-               lyxpreamble += from_utf8(babelCall(language_options.str(),
-                                                  features.needBabelLangOptions())) + '\n';
-               lyxpreamble += from_utf8(features.getBabelPostsettings());
+               os << from_utf8(babelCall(language_options.str(),
+                                         features.needBabelLangOptions())) + '\n';
+               os << features.getBabelPostsettings();
        }
        if (features.isRequired("bicaption"))
-               lyxpreamble += "\\usepackage{bicaption}\n";
-       if (!listings_params.empty() || features.isRequired("listings"))
-               lyxpreamble += "\\usepackage{listings}\n";
+               os << "\\usepackage{bicaption}\n";
+       if (!listings_params.empty() || features.mustProvide("listings"))
+               os << "\\usepackage{listings}\n";
        if (!listings_params.empty()) {
-               lyxpreamble += "\\lstset{";
+               os << "\\lstset{";
                // do not test validity because listings_params is
                // supposed to be valid
                string par =
                        InsetListingsParams(listings_params).separatedParams(true);
-               lyxpreamble += from_utf8(par);
-               lyxpreamble += "}\n";
+               os << from_utf8(par);
+               os << "}\n";
        }
 
        // xunicode needs to be loaded at least after amsmath, amssymb,
        // esint and the other packages that provide special glyphs
+       // The package only supports XeTeX currently.
        if (features.runparams().flavor == OutputParams::XETEX
            && useNonTeXFonts)
-               lyxpreamble += "\\usepackage{xunicode}\n";
+               os << "\\usepackage{xunicode}\n";
 
        // Polyglossia must be loaded last
        if (use_polyglossia) {
                // call the package
-               lyxpreamble += "\\usepackage{polyglossia}\n";
+               os << "\\usepackage{polyglossia}\n";
                // set the main language
-               lyxpreamble += "\\setdefaultlanguage";
+               os << "\\setdefaultlanguage";
                if (!language->polyglossiaOpts().empty())
-                       lyxpreamble += "[" + from_ascii(language->polyglossiaOpts()) + "]";
-               lyxpreamble += "{" + from_ascii(language->polyglossia()) + "}\n";
+                       os << "[" << from_ascii(language->polyglossiaOpts()) << "]";
+               os << "{" << from_ascii(language->polyglossia()) << "}\n";
                // now setup the other languages
-               std::map<std::string, std::string> const polylangs =
+               set<string> const polylangs =
                        features.getPolyglossiaLanguages();
-               for (std::map<std::string, std::string>::const_iterator mit = polylangs.begin();
+               for (set<string>::const_iterator mit = polylangs.begin();
                     mit != polylangs.end() ; ++mit) {
-                       lyxpreamble += "\\setotherlanguage";
-                       if (!mit->second.empty())
-                               lyxpreamble += "[" + from_ascii(mit->second) + "]";
-                       lyxpreamble += "{" + from_ascii(mit->first) + "}\n";
+                       // We do not output the options here; they are output in
+                       // the language switch commands. This is safer if multiple
+                       // varieties are used.
+                       if (*mit == language->polyglossia())
+                               continue;
+                       os << "\\setotherlanguage";
+                       os << "{" << from_ascii(*mit) << "}\n";
                }
        }
 
        // Load custom language package here
        if (features.langPackage() == LaTeXFeatures::LANG_PACK_CUSTOM) {
                if (lang_package == "default")
-                       lyxpreamble += from_utf8(lyxrc.language_custom_package);
+                       os << from_utf8(lyxrc.language_custom_package);
                else
-                       lyxpreamble += from_utf8(lang_package);
-               lyxpreamble += '\n';
+                       os << from_utf8(lang_package);
+               os << '\n';
        }
 
        docstring const i18npreamble =
                features.getTClassI18nPreamble(use_babel, use_polyglossia);
        if (!i18npreamble.empty())
-               lyxpreamble += i18npreamble + '\n';
-
-       os << lyxpreamble;
+               os << i18npreamble + '\n';
 
        return use_babel;
 }
@@ -2219,6 +2270,7 @@ void BufferParams::setDocumentClass(DocumentClassConstPtr tc)
 {
        // evil, but this function is evil
        doc_class_ = const_pointer_cast<DocumentClass>(tc);
+       invalidateConverterCache();
 }
 
 
@@ -2277,6 +2329,7 @@ void BufferParams::makeDocumentClass(bool const clone)
        if (!baseClass())
                return;
 
+       invalidateConverterCache();
        LayoutModuleList mods;
        LayoutModuleList::iterator it = layout_modules_.begin();
        LayoutModuleList::iterator en = layout_modules_.end();
@@ -2292,10 +2345,11 @@ void BufferParams::makeDocumentClass(bool const clone)
 
        TextClass::ReturnValues success = TextClass::OK;
        if (!forced_local_layout_.empty())
-               success = doc_class_->read(forced_local_layout_, TextClass::MODULE);
+               success = doc_class_->read(to_utf8(forced_local_layout_),
+                                          TextClass::MODULE);
        if (!local_layout_.empty() &&
            (success == TextClass::OK || success == TextClass::OK_OLDFORMAT))
-               success = doc_class_->read(local_layout_, TextClass::MODULE);
+               success = doc_class_->read(to_utf8(local_layout_), TextClass::MODULE);
        if (success != TextClass::OK && success != TextClass::OK_OLDFORMAT) {
                docstring const msg = _("Error reading internal layout information");
                frontend::Alert::warning(_("Read Error"), msg);
@@ -2315,16 +2369,16 @@ bool BufferParams::citationModuleCanBeAdded(string const & modName) const
 }
 
 
-std::string BufferParams::getLocalLayout(bool forced) const
+docstring BufferParams::getLocalLayout(bool forced) const
 {
        if (forced)
-               return doc_class_->forcedLayouts();
+               return from_utf8(doc_class_->forcedLayouts());
        else
                return local_layout_;
 }
 
 
-void BufferParams::setLocalLayout(string const & layout, bool forced)
+void BufferParams::setLocalLayout(docstring const & layout, bool forced)
 {
        if (forced)
                forced_local_layout_ = layout;
@@ -2347,60 +2401,50 @@ bool BufferParams::addLayoutModule(string const & modName)
 
 string BufferParams::bufferFormat() const
 {
-       string format = documentClass().outputFormat();
-       if (format == "latex") {
-               if (useNonTeXFonts)
-                       return "xetex"; // actually "xetex or luatex"
-               if (encoding().package() == Encoding::japanese)
-                       return "platex";
-       }
-       return format;
+       return documentClass().outputFormat();
 }
 
 
-bool BufferParams::isExportable(string const & format) const
+bool BufferParams::isExportable(string const & format, bool need_viewable) const
 {
-       vector<string> backs = backends();
-       for (vector<string>::const_iterator it = backs.begin();
-            it != backs.end(); ++it)
-               if (theConverters().isReachable(*it, format))
+       FormatList const & formats = exportableFormats(need_viewable);
+       FormatList::const_iterator fit = formats.begin();
+       FormatList::const_iterator end = formats.end();
+       for (; fit != end ; ++fit) {
+               if ((*fit)->name() == format)
                        return true;
+       }
        return false;
 }
 
 
-vector<Format const *> BufferParams::exportableFormats(bool only_viewable) const
+FormatList const & BufferParams::exportableFormats(bool only_viewable) const
 {
+       FormatList & cached = only_viewable ?
+                       pimpl_->viewableFormatList : pimpl_->exportableFormatList;
+       bool & valid = only_viewable ? 
+                       pimpl_->isViewCacheValid : pimpl_->isExportCacheValid;
+       if (valid)
+               return cached;
+
        vector<string> const backs = backends();
        set<string> excludes;
        if (useNonTeXFonts) {
                excludes.insert("latex");
                excludes.insert("pdflatex");
        }
-       vector<Format const *> result =
+       FormatList result =
                theConverters().getReachable(backs[0], only_viewable, true, excludes);
        for (vector<string>::const_iterator it = backs.begin() + 1;
             it != backs.end(); ++it) {
-               vector<Format const *>  r =
-                       theConverters().getReachable(*it, only_viewable, false, excludes);
+               FormatList r = theConverters().getReachable(*it, only_viewable, 
+                               false, excludes);
                result.insert(result.end(), r.begin(), r.end());
        }
-       return result;
-}
-
-
-bool BufferParams::isExportableFormat(string const & format) const
-{
-       typedef vector<Format const *> Formats;
-       Formats formats;
-       formats = exportableFormats(true);
-       Formats::const_iterator fit = formats.begin();
-       Formats::const_iterator end = formats.end();
-       for (; fit != end ; ++fit) {
-               if ((*fit)->name() == format)
-                       return true;
-       }
-       return false;
+       sort(result.begin(), result.end(), Format::formatSorter);
+       cached = result;
+       valid = true;
+       return cached;
 }
 
 
@@ -2411,17 +2455,14 @@ vector<string> BufferParams::backends() const
 
        // FIXME: Don't hardcode format names here, but use a flag
        if (buffmt == "latex") {
-               if (!useNonTeXFonts) {
-                       v.push_back("pdflatex");
-                       v.push_back("latex");
-               }
-               v.push_back("luatex");
-               v.push_back("dviluatex");
-               v.push_back("xetex");
-       } else if (buffmt == "xetex") {
-               v.push_back("xetex");
-               // FIXME: need to test all languages (bug 8205)
-               if (!language || !language->isPolyglossiaExclusive()) {
+               if (encoding().package() == Encoding::japanese)
+                       v.push_back("platex");
+               else {
+                       if (!useNonTeXFonts) {
+                               v.push_back("pdflatex");
+                               v.push_back("latex");
+                       }
+                       v.push_back("xetex");
                        v.push_back("luatex");
                        v.push_back("dviluatex");
                }
@@ -2493,7 +2534,7 @@ string BufferParams::getDefaultOutputFormat() const
                return default_output_format;
        if (isDocBook()
            || encoding().package() == Encoding::japanese) {
-               vector<Format const *> const formats = exportableFormats(true);
+               FormatList const & formats = exportableFormats(true);
                if (formats.empty())
                        return string();
                // return the first we find
@@ -2517,9 +2558,9 @@ Font const BufferParams::getFont() const
 }
 
 
-InsetQuotes::QuoteLanguage BufferParams::getQuoteStyle(string const & qs) const
+InsetQuotesParams::QuoteStyle BufferParams::getQuoteStyle(string const & qs) const
 {
-       return quoteslangtranslator().find(qs);
+       return quotesstyletranslator().find(qs);
 }
 
 
@@ -2547,7 +2588,7 @@ void BufferParams::readPreamble(Lexer & lex)
                lyxerr << "Error (BufferParams::readPreamble):"
                        "consistency check failed." << endl;
 
-       preamble = lex.getLongString("\\end_preamble");
+       preamble = lex.getLongString(from_ascii("\\end_preamble"));
 }
 
 
@@ -2561,9 +2602,9 @@ void BufferParams::readLocalLayout(Lexer & lex, bool forced)
 
        if (forced)
                forced_local_layout_ =
-                       lex.getLongString("\\end_forced_local_layout");
+                       lex.getLongString(from_ascii("\\end_forced_local_layout"));
        else
-               local_layout_ = lex.getLongString("\\end_local_layout");
+               local_layout_ = lex.getLongString(from_ascii("\\end_local_layout"));
 }
 
 
@@ -2973,8 +3014,8 @@ void BufferParams::writeEncodingPreamble(otexstream & os,
        // XeTeX/LuaTeX: (see also #9740)
        // With Unicode fonts we use utf8-plain without encoding package.
        // With TeX fonts, we cannot use utf8-plain, but "inputenc" fails.
-       // XeTeX must use ASCII encoding, for LuaTeX, we load
-       // "luainputenc" (see below).
+       // XeTeX must use ASCII encoding (see Buffer.cpp),
+       //  for LuaTeX, we load "luainputenc" (see below).
        if (useNonTeXFonts || features.runparams().flavor == OutputParams::XETEX)
                return;
 
@@ -3261,4 +3302,10 @@ vector<CitationStyle> BufferParams::citeStyles() const
        return styles;
 }
 
+void BufferParams::invalidateConverterCache() const
+{
+       pimpl_->isExportCacheValid = false;
+       pimpl_->isViewCacheValid = false;
+}
+
 } // namespace lyx