]> git.lyx.org Git - lyx.git/blobdiff - src/tex2lyx/text.cpp
update tex2lyx todo list
[lyx.git] / src / tex2lyx / text.cpp
index a34ccc448aa4ca2f063ba6b02c4743b7ac3095e6..50eee93a3d2291596ee63140d7afac665298232f 100644 (file)
 #include "Context.h"
 #include "Encoding.h"
 #include "FloatList.h"
+#include "LaTeXPackages.h"
 #include "Layout.h"
 #include "Length.h"
+#include "Preamble.h"
+
+#include "insets/ExternalTemplate.h"
 
 #include "support/lassert.h"
 #include "support/convert.h"
 #include "support/FileName.h"
 #include "support/filetools.h"
 #include "support/lstrings.h"
+#include "support/lyxtime.h"
 
 #include <algorithm>
 #include <iostream>
@@ -41,10 +46,15 @@ namespace lyx {
 
 
 void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
-               Context const & context)
+               Context const & context, InsetLayout const * layout)
 {
+       bool const forcePlainLayout =
+               layout ? layout->forcePlainLayout() : false;
        Context newcontext(true, context.textclass);
-       newcontext.font = context.font;
+       if (forcePlainLayout)
+               newcontext.layout = &context.textclass.plainLayout();
+       else
+               newcontext.font = context.font;
        parse_text(p, os, flags, outer, newcontext);
        newcontext.check_end_layout(os);
 }
@@ -52,6 +62,17 @@ void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
 
 namespace {
 
+void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
+               Context const & context, string const & name)
+{
+       InsetLayout const * layout = 0;
+       DocumentClass::InsetLayouts::const_iterator it =
+               context.textclass.insetLayouts().find(from_ascii(name));
+       if (it != context.textclass.insetLayouts().end())
+               layout = &(it->second);
+       parse_text_in_inset(p, os, flags, outer, context, layout);
+}
+
 /// parses a paragraph snippet, useful for example for \\emph{...}
 void parse_text_snippet(Parser & p, ostream & os, unsigned flags, bool outer,
                Context & context)
@@ -90,15 +111,72 @@ string parse_text_snippet(Parser & p, unsigned flags, const bool outer,
 }
 
 
-char const * const known_latex_commands[] = { "ref", "cite", "label",
- "index", "printindex", "pageref", "url", "vref", "vpageref", "prettyref",
- "eqref", 0 };
+char const * const known_ref_commands[] = { "ref", "pageref", "vref",
+ "vpageref", "prettyref", "eqref", 0 };
+
+char const * const known_coded_ref_commands[] = { "ref", "pageref", "vref",
+ "vpageref", "formatted", "eqref", 0 };
+
+/**
+ * known polyglossia language names (including variants)
+ */
+const char * const polyglossia_languages[] = {
+"albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
+"nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
+"armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
+"irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
+"bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
+"samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazil",
+"brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton", "french",
+"lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
+"welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
+"marathi", "spanish",
+"american", "ancient", "australian", "british", "monotonic", "newzealand",
+"polytonic", 0};
+
+/**
+ * the same as polyglossia_languages with .lyx names
+ * please keep this in sync with polyglossia_languages line by line!
+ */
+const char * const coded_polyglossia_languages[] = {
+"albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
+"nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
+"armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
+"irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
+"bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
+"samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazilian",
+"brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton", "french",
+"lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
+"welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
+"marathi", "spanish",
+"american", "ancientgreek", "australian", "british", "greek", "newzealand",
+"polutonikogreek", 0};
+
+/**
+ * supported CJK encodings
+ */
+const char * const supported_CJK_encodings[] = {
+"EUC-JP", "KS", "GB", "UTF8", 0};
+
+/**
+ * the same as supported_CJK_encodings with their corresponding LyX language name
+ * please keep this in sync with supported_CJK_encodings line by line!
+ */
+const char * const coded_supported_CJK_encodings[] = {
+"japanese-cjk", "korean", "chinese-simplified", "chinese-traditional", 0};
+
+string CJK2lyx(string const & encoding)
+{
+       char const * const * where = is_known(encoding, supported_CJK_encodings);
+       if (where)
+               return coded_supported_CJK_encodings[where - supported_CJK_encodings];
+       return encoding;
+}
 
 /*!
  * natbib commands.
- * We can't put these into known_latex_commands because the argument order
- * is reversed in lyx if there are 2 arguments.
- * The starred forms are also known.
+ * The starred forms are also known except for "citefullauthor",
+ * "citeyear" and "citeyearpar".
  */
 char const * const known_natbib_commands[] = { "cite", "citet", "citep",
 "citealt", "citealp", "citeauthor", "citeyear", "citeyearpar",
@@ -106,8 +184,6 @@ char const * const known_natbib_commands[] = { "cite", "citet", "citep",
 
 /*!
  * jurabib commands.
- * We can't put these into known_latex_commands because the argument order
- * is reversed in lyx if there are 2 arguments.
  * No starred form other than "cite*" known.
  */
 char const * const known_jurabib_commands[] = { "cite", "citet", "citep",
@@ -116,7 +192,7 @@ char const * const known_jurabib_commands[] = { "cite", "citet", "citep",
 // "fullcite",
 // "footcite", "footcitet", "footcitep", "footcitealt", "footcitealp",
 // "footciteauthor", "footciteyear", "footciteyearpar",
-"citefield", "citetitle", "cite*", 0 };
+"citefield", "citetitle", 0 };
 
 /// LaTeX names for quotes
 char const * const known_quotes[] = { "dq", "guillemotleft", "flqq", "og",
@@ -134,8 +210,8 @@ char const * const known_coded_quotes[] = { "prd", "ard", "ard", "ard",
 char const * const known_sizes[] = { "tiny", "scriptsize", "footnotesize",
 "small", "normalsize", "large", "Large", "LARGE", "huge", "Huge", 0};
 
-/// the same as known_sizes with .lyx names plus a default entry
-char const * const known_coded_sizes[] = { "default", "tiny", "scriptsize", "footnotesize",
+/// the same as known_sizes with .lyx names
+char const * const known_coded_sizes[] = { "tiny", "scriptsize", "footnotesize",
 "small", "normal", "large", "larger", "largest", "huge", "giant", 0};
 
 /// LaTeX 2.09 names for font families
@@ -145,7 +221,12 @@ char const * const known_old_font_families[] = { "rm", "sf", "tt", 0};
 char const * const known_font_families[] = { "rmfamily", "sffamily",
 "ttfamily", 0};
 
-/// the same as known_old_font_families and known_font_families with .lyx names
+/// LaTeX names for font family changing commands
+char const * const known_text_font_families[] = { "textrm", "textsf",
+"texttt", 0};
+
+/// The same as known_old_font_families, known_font_families and
+/// known_text_font_families with .lyx names
 char const * const known_coded_font_families[] = { "roman", "sans",
 "typewriter", 0};
 
@@ -155,7 +236,11 @@ char const * const known_old_font_series[] = { "bf", 0};
 /// LaTeX names for font series
 char const * const known_font_series[] = { "bfseries", "mdseries", 0};
 
-/// the same as known_old_font_series and known_font_series with .lyx names
+/// LaTeX names for font series changing commands
+char const * const known_text_font_series[] = { "textbf", "textmd", 0};
+
+/// The same as known_old_font_series, known_font_series and
+/// known_text_font_series with .lyx names
 char const * const known_coded_font_series[] = { "bold", "medium", 0};
 
 /// LaTeX 2.09 names for font shapes
@@ -165,10 +250,23 @@ char const * const known_old_font_shapes[] = { "it", "sl", "sc", 0};
 char const * const known_font_shapes[] = { "itshape", "slshape", "scshape",
 "upshape", 0};
 
-/// the same as known_old_font_shapes and known_font_shapes with .lyx names
+/// LaTeX names for font shape changing commands
+char const * const known_text_font_shapes[] = { "textit", "textsl", "textsc",
+"textup", 0};
+
+/// The same as known_old_font_shapes, known_font_shapes and
+/// known_text_font_shapes with .lyx names
 char const * const known_coded_font_shapes[] = { "italic", "slanted",
 "smallcaps", "up", 0};
 
+/// Known special characters which need skip_spaces_braces() afterwards
+char const * const known_special_chars[] = {"ldots", "lyxarrow",
+"textcompwordmark", "slash", 0};
+
+/// the same as known_special_chars with .lyx names
+char const * const known_coded_special_chars[] = {"ldots{}", "menuseparator",
+"textcompwordmark{}", "slash{}", 0};
+
 /*!
  * Graphics file extensions known by the dvips driver of the graphics package.
  * These extensions are used to complete the filename of an included
@@ -195,28 +293,43 @@ char const * const known_pdftex_graphics_formats[] = {"png", "pdf", "jpg",
 char const * const known_tex_extensions[] = {"tex", 0};
 
 /// spaces known by InsetSpace
-char const * const known_spaces[] = { " ", "space", ",", "thinspace", "quad",
-"qquad", "enspace", "enskip", "negthinspace", 0};
+char const * const known_spaces[] = { " ", "space", ",",
+"thinspace", "quad", "qquad", "enspace", "enskip",
+"negthinspace", "negmedspace", "negthickspace", "textvisiblespace",
+"hfill", "dotfill", "hrulefill", "leftarrowfill", "rightarrowfill",
+"upbracefill", "downbracefill", 0};
 
 /// the same as known_spaces with .lyx names
 char const * const known_coded_spaces[] = { "space{}", "space{}",
 "thinspace{}", "thinspace{}", "quad{}", "qquad{}", "enspace{}", "enskip{}",
-"negthinspace{}", 0};
+"negthinspace{}", "negmedspace{}", "negthickspace{}", "textvisiblespace{}",
+"hfill{}", "dotfill{}", "hrulefill{}", "leftarrowfill{}", "rightarrowfill{}",
+"upbracefill{}", "downbracefill{}", 0};
+
+/// These are translated by LyX to commands like "\\LyX{}", so we have to put
+/// them in ERT. "LaTeXe" must come before "LaTeX"!
+char const * const known_phrases[] = {"LyX", "TeX", "LaTeXe", "LaTeX", 0};
+char const * const known_coded_phrases[] = {"LyX", "TeX", "LaTeX2e", "LaTeX", 0};
+int const known_phrase_lengths[] = {3, 5, 7, 0};
 
+// string to store the float type to be able to determine the type of subfloats
+string float_type = "";
 
-/// splits "x=z, y=b" into a map
-map<string, string> split_map(string const & s)
+
+/// splits "x=z, y=b" into a map and an ordered keyword vector
+void split_map(string const & s, map<string, string> & res, vector<string> & keys)
 {
-       map<string, string> res;
        vector<string> v;
        split(s, v);
+       res.clear();
+       keys.resize(v.size());
        for (size_t i = 0; i < v.size(); ++i) {
                size_t const pos   = v[i].find('=');
-               string const index = v[i].substr(0, pos);
-               string const value = v[i].substr(pos + 1, string::npos);
-               res[trim(index)] = trim(value);
+               string const index = trimSpaceAndEol(v[i].substr(0, pos));
+               string const value = trimSpaceAndEol(v[i].substr(pos + 1, string::npos));
+               res[index] = value;
+               keys[i] = index;
        }
-       return res;
 }
 
 
@@ -244,20 +357,20 @@ bool splitLatexLength(string const & len, string & value, string & unit)
                        return false;
                }
        } else {
-               value = trim(string(length, 0, i));
+               value = trimSpaceAndEol(string(length, 0, i));
        }
        if (value == "-")
                value = "-1.0";
        // 'cM' is a valid LaTeX length unit. Change it to 'cm'
        if (contains(len, '\\'))
-               unit = trim(string(len, i));
+               unit = trimSpaceAndEol(string(len, i));
        else
-               unit = ascii_lowercase(trim(string(len, i)));
+               unit = ascii_lowercase(trimSpaceAndEol(string(len, i)));
        return true;
 }
 
 
-/// A simple function to translate a latex length to something lyx can
+/// A simple function to translate a latex length to something LyX can
 /// understand. Not perfect, but rather best-effort.
 bool translate_len(string const & length, string & valstring, string & unit)
 {
@@ -345,8 +458,6 @@ void translate_box_len(string const & length, string & value, string & unit, str
 string find_file(string const & name, string const & path,
                 char const * const * extensions)
 {
-       // FIXME UNICODE encoding of name and path may be wrong (makeAbsPath
-       // expects utf8)
        for (char const * const * what = extensions; *what; ++what) {
                string const trial = addExtension(name, *what);
                if (makeAbsPath(trial, path).exists())
@@ -362,22 +473,84 @@ void begin_inset(ostream & os, string const & name)
 }
 
 
+void begin_command_inset(ostream & os, string const & name,
+                         string const & latexname)
+{
+       begin_inset(os, "CommandInset ");
+       os << name << "\nLatexCommand " << latexname << '\n';
+}
+
+
 void end_inset(ostream & os)
 {
        os << "\n\\end_inset\n\n";
 }
 
 
-void skip_braces(Parser & p)
+bool skip_braces(Parser & p)
 {
        if (p.next_token().cat() != catBegin)
-               return;
+               return false;
        p.get_token();
        if (p.next_token().cat() == catEnd) {
                p.get_token();
-               return;
+               return true;
        }
        p.putback();
+       return false;
+}
+
+
+/// replace LaTeX commands in \p s from the unicodesymbols file with their
+/// unicode points
+docstring convert_unicodesymbols(docstring s)
+{
+       odocstringstream os;
+       for (size_t i = 0; i < s.size();) {
+               if (s[i] != '\\') {
+                       os.put(s[i++]);
+                       continue;
+               }
+               s = s.substr(i);
+               bool termination;
+               docstring rem;
+               set<string> req;
+               docstring parsed = encodings.fromLaTeXCommand(s,
+                               Encodings::TEXT_CMD, termination, rem, &req);
+               set<string>::const_iterator it = req.begin();
+               set<string>::const_iterator en = req.end();
+               for (; it != en; ++it)
+                       preamble.registerAutomaticallyLoadedPackage(*it);
+               os << parsed;
+               s = rem;
+               if (s.empty() || s[0] != '\\')
+                       i = 0;
+               else
+                       i = 1;
+       }
+       return os.str();
+}
+
+
+/// try to convert \p s to a valid InsetCommand argument
+string convert_command_inset_arg(string s)
+{
+       if (isAscii(s))
+               // since we don't know the input encoding we can't use from_utf8
+               s = to_utf8(convert_unicodesymbols(from_ascii(s)));
+       // LyX cannot handle newlines in a latex command
+       return subst(s, "\n", " ");
+}
+
+
+void handle_backslash(ostream & os, string const & s)
+{
+       for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
+               if (*it == '\\')
+                       os << "\n\\backslash\n";
+               else
+                       os << *it;
+       }
 }
 
 
@@ -410,12 +583,7 @@ void handle_comment(ostream & os, string const & s, Context & context)
        begin_inset(os, "ERT");
        os << "\nstatus collapsed\n";
        newcontext.check_layout(os);
-       for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
-               if (*it == '\\')
-                       os << "\n\\backslash\n";
-               else
-                       os << *it;
-       }
+       handle_backslash(os, s);
        // make sure that our comment is the last thing on the line
        newcontext.new_paragraph(os);
        newcontext.check_layout(os);
@@ -424,31 +592,55 @@ void handle_comment(ostream & os, string const & s, Context & context)
 }
 
 
-Layout const * findLayout(TextClass const & textclass, string const & name)
+Layout const * findLayout(TextClass const & textclass, string const & name, bool command)
+{
+       Layout const * layout = findLayoutWithoutModule(textclass, name, command);
+       if (layout)
+               return layout;
+       if (checkModule(name, command))
+               return findLayoutWithoutModule(textclass, name, command);
+       return layout;
+}
+
+
+InsetLayout const * findInsetLayout(TextClass const & textclass, string const & name, bool command)
 {
-       DocumentClass::const_iterator lit = textclass.begin();
-       DocumentClass::const_iterator len = textclass.end();
-       for (; lit != len; ++lit)
-               if (lit->latexname() == name)
-                       return &*lit;
-       return 0;
+       InsetLayout const * insetlayout = findInsetLayoutWithoutModule(textclass, name, command);
+       if (insetlayout)
+               return insetlayout;
+       if (checkModule(name, command))
+               return findInsetLayoutWithoutModule(textclass, name, command);
+       return insetlayout;
 }
 
 
 void eat_whitespace(Parser &, ostream &, Context &, bool);
 
 
-Layout * captionlayout()
+/*!
+ * Skips whitespace and braces.
+ * This should be called after a command has been parsed that is not put into
+ * ERT, and where LyX adds "{}" if needed.
+ */
+void skip_spaces_braces(Parser & p, bool keepws = false)
 {
-       static Layout * lay = 0;
-       if (!lay) {
-               lay = new Layout;
-               lay->name_ = from_ascii("Caption");
-               lay->latexname_ = "caption";
-               lay->latextype = LATEX_COMMAND;
-               lay->optionalargs = 1;
-       }
-       return lay;
+       /* The following four examples produce the same typeset output and
+          should be handled by this function:
+          - abc \j{} xyz
+          - abc \j {} xyz
+          - abc \j
+            {} xyz
+          - abc \j %comment
+            {} xyz
+        */
+       // Unfortunately we need to skip comments, too.
+       // We can't use eat_whitespace since writing them after the {}
+       // results in different output in some cases.
+       bool const skipped_spaces = p.skip_spaces(true);
+       bool const skipped_braces = skip_braces(p);
+       if (keepws && skipped_spaces && !skipped_braces)
+               // put back the space (it is better handled by check_space)
+               p.unskip_spaces(true);
 }
 
 
@@ -456,6 +648,15 @@ void output_command_layout(ostream & os, Parser & p, bool outer,
                           Context & parent_context,
                           Layout const * newlayout)
 {
+       TeXFont const oldFont = parent_context.font;
+       // save the current font size
+       string const size = oldFont.size;
+       // reset the font size to default, because the font size switches
+       // don't affect section headings and the like
+       parent_context.font.size = Context::normalfont.size;
+       // we only need to write the font change if we have an open layout
+       if (!parent_context.atParagraphStart())
+               output_font_change(os, oldFont, parent_context.font);
        parent_context.check_end_layout(os);
        Context context(true, parent_context.textclass, newlayout,
                        parent_context.layout, parent_context.font);
@@ -467,16 +668,32 @@ void output_command_layout(ostream & os, Parser & p, bool outer,
        }
        context.check_deeper(os);
        context.check_layout(os);
-       if (context.layout->optionalargs > 0) {
+       unsigned int optargs = 0;
+       while (optargs < context.layout->optargs) {
                eat_whitespace(p, os, context, false);
-               if (p.next_token().character() == '[') {
-                       p.get_token(); // eat '['
-                       begin_inset(os, "OptArg\n");
-                       os << "status collapsed\n\n";
-                       parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
-                       end_inset(os);
-                       eat_whitespace(p, os, context, false);
-               }
+               if (p.next_token().cat() == catEscape ||
+                   p.next_token().character() != '[')
+                       break;
+               p.get_token(); // eat '['
+               begin_inset(os, "Argument\n");
+               os << "status collapsed\n\n";
+               parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
+               end_inset(os);
+               eat_whitespace(p, os, context, false);
+               ++optargs;
+       }
+       unsigned int reqargs = 0;
+       while (reqargs < context.layout->reqargs) {
+               eat_whitespace(p, os, context, false);
+               if (p.next_token().cat() != catBegin)
+                       break;
+               p.get_token(); // eat '{'
+               begin_inset(os, "Argument\n");
+               os << "status collapsed\n\n";
+               parse_text_in_inset(p, os, FLAG_BRACE_LAST, outer, context);
+               end_inset(os);
+               eat_whitespace(p, os, context, false);
+               ++reqargs;
        }
        parse_text(p, os, FLAG_ITEM, outer, context);
        context.check_end_layout(os);
@@ -489,6 +706,9 @@ void output_command_layout(ostream & os, Parser & p, bool outer,
        // We don't need really a new paragraph, but
        // we must make sure that the next item gets a \begin_layout.
        parent_context.new_paragraph(os);
+       // Set the font size to the original value. No need to output it here
+       // (Context::begin_layout() will do that if needed)
+       parent_context.font.size = size;
 }
 
 
@@ -540,18 +760,40 @@ void parse_arguments(string const & command,
        for (size_t i = 0; i < no_arguments; ++i) {
                switch (template_arguments[i]) {
                case required:
+               case req_group:
                        // This argument contains regular LaTeX
                        handle_ert(os, ert + '{', context);
                        eat_whitespace(p, os, context, false);
-                       parse_text(p, os, FLAG_ITEM, outer, context);
+                       if (template_arguments[i] == required)
+                               parse_text(p, os, FLAG_ITEM, outer, context);
+                       else
+                               parse_text_snippet(p, os, FLAG_ITEM, outer, context);
                        ert = "}";
                        break;
+               case item:
+                       // This argument consists only of a single item.
+                       // The presence of '{' or not must be preserved.
+                       p.skip_spaces();
+                       if (p.next_token().cat() == catBegin)
+                               ert += '{' + p.verbatim_item() + '}';
+                       else
+                               ert += p.verbatim_item();
+                       break;
+               case displaymath:
                case verbatim:
                        // This argument may contain special characters
                        ert += '{' + p.verbatim_item() + '}';
                        break;
                case optional:
-                       ert += p.getOpt();
+               case opt_group:
+                       // true because we must not eat whitespace
+                       // if an optional arg follows we must not strip the
+                       // brackets from this one
+                       if (i < no_arguments - 1 &&
+                           template_arguments[i+1] == optional)
+                               ert += p.getFullOpt(true);
+                       else
+                               ert += p.getOpt(true);
                        break;
                }
        }
@@ -577,65 +819,193 @@ bool parse_command(string const & command, Parser & p, ostream & os,
 
 
 /// Parses a minipage or parbox
-void parse_box(Parser & p, ostream & os, unsigned flags, bool outer,
-              Context & parent_context, bool use_parbox)
+void parse_box(Parser & p, ostream & os, unsigned outer_flags,
+               unsigned inner_flags, bool outer, Context & parent_context,
+               string const & outer_type, string const & special,
+               string const & inner_type)
 {
        string position;
        string inner_pos;
+       string hor_pos = "c";
        // We need to set the height to the LaTeX default of 1\\totalheight
        // for the case when no height argument is given
        string height_value = "1";
        string height_unit = "in";
        string height_special = "totalheight";
        string latex_height;
-       if (p.next_token().asInput() == "[") {
-               position = p.getArg('[', ']');
+       string width_value;
+       string width_unit;
+       string latex_width;
+       string width_special = "none";
+       if (!inner_type.empty() && p.hasOpt()) {
+               if (inner_type != "makebox")
+                       position = p.getArg('[', ']');
+               else {
+                       latex_width = p.getArg('[', ']');
+                       translate_box_len(latex_width, width_value, width_unit, width_special);
+                       position = "t";
+               }
                if (position != "t" && position != "c" && position != "b") {
+                       cerr << "invalid position " << position << " for "
+                            << inner_type << endl;
                        position = "c";
-                       cerr << "invalid position for minipage/parbox" << endl;
                }
-               if (p.next_token().asInput() == "[") {
-                       latex_height = p.getArg('[', ']');
-                       translate_box_len(latex_height, height_value, height_unit, height_special);
+               if (p.hasOpt()) {
+                       if (inner_type != "makebox") {
+                               latex_height = p.getArg('[', ']');
+                               translate_box_len(latex_height, height_value, height_unit, height_special);
+                       } else
+                               hor_pos = p.getArg('[', ']');
 
-                       if (p.next_token().asInput() == "[") {
+                       if (p.hasOpt()) {
                                inner_pos = p.getArg('[', ']');
                                if (inner_pos != "c" && inner_pos != "t" &&
                                    inner_pos != "b" && inner_pos != "s") {
+                                       cerr << "invalid inner_pos "
+                                            << inner_pos << " for "
+                                            << inner_type << endl;
                                        inner_pos = position;
-                                       cerr << "invalid inner_pos for minipage/parbox"
-                                            << endl;
                                }
                        }
                }
        }
-       string width_value;
-       string width_unit;
-       string const latex_width = p.verbatim_item();
+       if (inner_type.empty()) {
+               if (special.empty() && outer_type != "framebox")
+                       latex_width = "1\\columnwidth";
+               else {
+                       Parser p2(special);
+                       latex_width = p2.getArg('[', ']');
+                       string const opt = p2.getArg('[', ']');
+                       if (!opt.empty()) {
+                               hor_pos = opt;
+                               if (hor_pos != "l" && hor_pos != "c" &&
+                                   hor_pos != "r") {
+                                       cerr << "invalid hor_pos " << hor_pos
+                                            << " for " << outer_type << endl;
+                                       hor_pos = "c";
+                               }
+                       }
+               }
+       } else if (inner_type != "makebox")
+               latex_width = p.verbatim_item();
+       // if e.g. only \ovalbox{content} was used, set the width to 1\columnwidth
+       // as this is LyX's standard for such cases (except for makebox)
+       // \framebox is more special and handled below
+       if (latex_width.empty() && inner_type != "makebox"
+               && outer_type != "framebox")
+               latex_width = "1\\columnwidth";
+
        translate_len(latex_width, width_value, width_unit);
-       if (contains(width_unit, '\\') || contains(height_unit, '\\')) {
-               // LyX can't handle length variables
-               ostringstream ss;
-               if (use_parbox)
-                       ss << "\\parbox";
+
+       bool shadedparbox = false;
+       if (inner_type == "shaded") {
+               eat_whitespace(p, os, parent_context, false);
+               if (outer_type == "parbox") {
+                       // Eat '{'
+                       if (p.next_token().cat() == catBegin)
+                               p.get_token();
+                       eat_whitespace(p, os, parent_context, false);
+                       shadedparbox = true;
+               }
+               p.get_token();
+               p.getArg('{', '}');
+       }
+       // If we already read the inner box we have to push the inner env
+       if (!outer_type.empty() && !inner_type.empty() &&
+           (inner_flags & FLAG_END))
+               active_environments.push_back(inner_type);
+       // LyX can't handle length variables
+       bool use_ert = contains(width_unit, '\\') || contains(height_unit, '\\');
+       if (!use_ert && !outer_type.empty() && !inner_type.empty()) {
+               // Look whether there is some content after the end of the
+               // inner box, but before the end of the outer box.
+               // If yes, we need to output ERT.
+               p.pushPosition();
+               if (inner_flags & FLAG_END)
+                       p.verbatimEnvironment(inner_type);
                else
-                       ss << "\\begin{minipage}";
-               if (!position.empty())
-                       ss << '[' << position << ']';
-               if (!latex_height.empty())
-                       ss << '[' << latex_height << ']';
-               if (!inner_pos.empty())
-                       ss << '[' << inner_pos << ']';
-               ss << "{" << latex_width << "}";
-               if (use_parbox)
-                       ss << '{';
+                       p.verbatim_item();
+               p.skip_spaces(true);
+               bool const outer_env(outer_type == "framed" || outer_type == "minipage");
+               if ((outer_env && p.next_token().asInput() != "\\end") ||
+                   (!outer_env && p.next_token().cat() != catEnd)) {
+                       // something is between the end of the inner box and
+                       // the end of the outer box, so we need to use ERT.
+                       use_ert = true;
+               }
+               p.popPosition();
+       }
+       // if only \makebox{content} was used we can set its width to 1\width
+       // because this identic and also identic to \mbox
+       // this doesn't work for \framebox{content}, thus we have to use ERT for this
+       if (latex_width.empty() && inner_type == "makebox") {
+               width_value = "1";
+               width_unit = "in";
+               width_special = "width";
+       } else if (latex_width.empty() && outer_type == "framebox") {
+               use_ert = true;
+       }
+       if (use_ert) {
+               ostringstream ss;
+               if (!outer_type.empty()) {
+                       if (outer_flags & FLAG_END)
+                               ss << "\\begin{" << outer_type << '}';
+                       else {
+                               ss << '\\' << outer_type << '{';
+                               if (!special.empty())
+                                       ss << special;
+                       }
+               }
+               if (!inner_type.empty()) {
+                       if (inner_type != "shaded") {
+                               if (inner_flags & FLAG_END)
+                                       ss << "\\begin{" << inner_type << '}';
+                               else
+                                       ss << '\\' << inner_type;
+                       }
+                       if (!position.empty())
+                               ss << '[' << position << ']';
+                       if (!latex_height.empty())
+                               ss << '[' << latex_height << ']';
+                       if (!inner_pos.empty())
+                               ss << '[' << inner_pos << ']';
+                       ss << '{' << latex_width << '}';
+                       if (!(inner_flags & FLAG_END))
+                               ss << '{';
+               }
+               if (inner_type == "shaded")
+                       ss << "\\begin{shaded}";
                handle_ert(os, ss.str(), parent_context);
-               parent_context.new_paragraph(os);
-               parse_text_in_inset(p, os, flags, outer, parent_context);
-               if (use_parbox)
-                       handle_ert(os, "}", parent_context);
-               else
-                       handle_ert(os, "\\end{minipage}", parent_context);
+               if (!inner_type.empty()) {
+                       parse_text(p, os, inner_flags, outer, parent_context);
+                       if (inner_flags & FLAG_END)
+                               handle_ert(os, "\\end{" + inner_type + '}',
+                                          parent_context);
+                       else
+                               handle_ert(os, "}", parent_context);
+               }
+               if (!outer_type.empty()) {
+                       // If we already read the inner box we have to pop
+                       // the inner env
+                       if (!inner_type.empty() && (inner_flags & FLAG_END))
+                               active_environments.pop_back();
+
+                       // Ensure that the end of the outer box is parsed correctly:
+                       // The opening brace has been eaten by parse_outer_box()
+                       if (!outer_type.empty() && (outer_flags & FLAG_ITEM)) {
+                               outer_flags &= ~FLAG_ITEM;
+                               outer_flags |= FLAG_BRACE_LAST;
+                       }
+                       parse_text(p, os, outer_flags, outer, parent_context);
+                       if (outer_flags & FLAG_END)
+                               handle_ert(os, "\\end{" + outer_type + '}',
+                                          parent_context);
+                       else if (inner_type.empty() && outer_type == "framebox")
+                               // in this case it is already closed later
+                               ;
+                       else
+                               handle_ert(os, "}", parent_context);
+               }
        } else {
                // LyX does not like empty positions, so we have
                // to set them to the LaTeX default values here.
@@ -644,21 +1014,77 @@ void parse_box(Parser & p, ostream & os, unsigned flags, bool outer,
                if (inner_pos.empty())
                        inner_pos = position;
                parent_context.check_layout(os);
-               begin_inset(os, "Box Frameless\n");
+               begin_inset(os, "Box ");
+               if (outer_type == "framed")
+                       os << "Framed\n";
+               else if (outer_type == "framebox")
+                       os << "Boxed\n";
+               else if (outer_type == "shadowbox")
+                       os << "Shadowbox\n";
+               else if ((outer_type == "shaded" && inner_type.empty()) ||
+                            (outer_type == "minipage" && inner_type == "shaded") ||
+                            (outer_type == "parbox" && inner_type == "shaded")) {
+                       os << "Shaded\n";
+                       preamble.registerAutomaticallyLoadedPackage("color");
+               } else if (outer_type == "doublebox")
+                       os << "Doublebox\n";
+               else if (outer_type.empty())
+                       os << "Frameless\n";
+               else
+                       os << outer_type << '\n';
                os << "position \"" << position << "\"\n";
-               os << "hor_pos \"c\"\n";
-               os << "has_inner_box 1\n";
+               os << "hor_pos \"" << hor_pos << "\"\n";
+               os << "has_inner_box " << !inner_type.empty() << "\n";
                os << "inner_pos \"" << inner_pos << "\"\n";
-               os << "use_parbox " << use_parbox << "\n";
+               os << "use_parbox " << (inner_type == "parbox" || shadedparbox)
+                  << '\n';
+               os << "use_makebox " << (inner_type == "makebox") << '\n';
                os << "width \"" << width_value << width_unit << "\"\n";
-               os << "special \"none\"\n";
+               os << "special \"" << width_special << "\"\n";
                os << "height \"" << height_value << height_unit << "\"\n";
                os << "height_special \"" << height_special << "\"\n";
                os << "status open\n\n";
-               parse_text_in_inset(p, os, flags, outer, parent_context);
+
+               // Unfortunately we can't use parse_text_in_inset:
+               // InsetBox::forcePlainLayout() is hard coded and does not
+               // use the inset layout. Apart from that do we call parse_text
+               // up to two times, but need only one check_end_layout.
+               bool const forcePlainLayout =
+                       (!inner_type.empty() || inner_type == "makebox") &&
+                       outer_type != "shaded" && outer_type != "framed";
+               Context context(true, parent_context.textclass);
+               if (forcePlainLayout)
+                       context.layout = &context.textclass.plainLayout();
+               else
+                       context.font = parent_context.font;
+
+               // If we have no inner box the contents will be read with the outer box
+               if (!inner_type.empty())
+                       parse_text(p, os, inner_flags, outer, context);
+
+               // Ensure that the end of the outer box is parsed correctly:
+               // The opening brace has been eaten by parse_outer_box()
+               if (!outer_type.empty() && (outer_flags & FLAG_ITEM)) {
+                       outer_flags &= ~FLAG_ITEM;
+                       outer_flags |= FLAG_BRACE_LAST;
+               }
+
+               // Find end of outer box, output contents if inner_type is
+               // empty and output possible comments
+               if (!outer_type.empty()) {
+                       // If we already read the inner box we have to pop
+                       // the inner env
+                       if (!inner_type.empty() && (inner_flags & FLAG_END))
+                               active_environments.pop_back();
+                       // This does not output anything but comments if
+                       // inner_type is not empty (see use_ert)
+                       parse_text(p, os, outer_flags, outer, context);
+               }
+
+               context.check_end_layout(os);
                end_inset(os);
 #ifdef PRESERVE_LAYOUT
-               // lyx puts a % after the end of the minipage
+               // LyX puts a % after the end of the minipage
                if (p.next_token().cat() == catNewline && p.next_token().cs().size() > 1) {
                        // new paragraph
                        //handle_comment(os, "%dummy", parent_context);
@@ -672,7 +1098,8 @@ void parse_box(Parser & p, ostream & os, unsigned flags, bool outer,
                        p.skip_spaces();
                        // We add a protected space if something real follows
                        if (p.good() && p.next_token().cat() != catComment) {
-                               os << "\\InsetSpace ~\n";
+                               begin_inset(os, "space ~\n");
+                               end_inset(os);
                        }
                }
 #endif
@@ -680,6 +1107,115 @@ void parse_box(Parser & p, ostream & os, unsigned flags, bool outer,
 }
 
 
+void parse_outer_box(Parser & p, ostream & os, unsigned flags, bool outer,
+                     Context & parent_context, string const & outer_type,
+                     string const & special)
+{
+       eat_whitespace(p, os, parent_context, false);
+       if (flags & FLAG_ITEM) {
+               // Eat '{'
+               if (p.next_token().cat() == catBegin)
+                       p.get_token();
+               else
+                       cerr << "Warning: Ignoring missing '{' after \\"
+                            << outer_type << '.' << endl;
+               eat_whitespace(p, os, parent_context, false);
+       }
+       string inner;
+       unsigned int inner_flags = 0;
+       p.pushPosition();
+       if (outer_type == "minipage" || outer_type == "parbox") {
+               p.skip_spaces(true);
+               while (p.hasOpt()) {
+                       p.getArg('[', ']');
+                       p.skip_spaces(true);
+               }
+               p.getArg('{', '}');
+               p.skip_spaces(true);
+               if (outer_type == "parbox") {
+                       // Eat '{'
+                       if (p.next_token().cat() == catBegin)
+                               p.get_token();
+                       p.skip_spaces(true);
+               }
+       }
+       if (outer_type == "shaded") {
+               // These boxes never have an inner box
+               ;
+       } else if (p.next_token().asInput() == "\\parbox") {
+               inner = p.get_token().cs();
+               inner_flags = FLAG_ITEM;
+       } else if (p.next_token().asInput() == "\\begin") {
+               // Is this a minipage or shaded box?
+               p.pushPosition();
+               p.get_token();
+               inner = p.getArg('{', '}');
+               p.popPosition();
+               if (inner == "minipage" || inner == "shaded")
+                       inner_flags = FLAG_END;
+               else
+                       inner = "";
+       }
+       p.popPosition();
+       if (inner_flags == FLAG_END) {
+               if (inner != "shaded")
+               {
+                       p.get_token();
+                       p.getArg('{', '}');
+                       eat_whitespace(p, os, parent_context, false);
+               }
+               parse_box(p, os, flags, FLAG_END, outer, parent_context,
+                         outer_type, special, inner);
+       } else {
+               if (inner_flags == FLAG_ITEM) {
+                       p.get_token();
+                       eat_whitespace(p, os, parent_context, false);
+               }
+               parse_box(p, os, flags, inner_flags, outer, parent_context,
+                         outer_type, special, inner);
+       }
+}
+
+
+void parse_listings(Parser & p, ostream & os, Context & parent_context, bool in_line)
+{
+       parent_context.check_layout(os);
+       begin_inset(os, "listings\n");
+       if (p.hasOpt()) {
+               string arg = p.verbatimOption();
+               os << "lstparams " << '"' << arg << '"' << '\n';
+       }
+       if (in_line)
+               os << "inline true\n";
+       else
+               os << "inline false\n";
+       os << "status collapsed\n";
+       Context context(true, parent_context.textclass);
+       context.layout = &parent_context.textclass.plainLayout();
+       string s;
+       if (in_line) {
+               s = p.plainCommand('!', '!', "lstinline");
+               context.new_paragraph(os);
+               context.check_layout(os);
+       } else
+               s = p.plainEnvironment("lstlisting");
+       for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
+               if (*it == '\\')
+                       os << "\n\\backslash\n";
+               else if (*it == '\n') {
+                       // avoid adding an empty paragraph at the end
+                       if (it + 1 != et) {
+                               context.new_paragraph(os);
+                               context.check_layout(os);
+                       }
+               } else
+                       os << *it;
+       }
+       context.check_end_layout(os);
+       end_inset(os);
+}
+
+
 /// parse an unknown environment
 void parse_unknown_environment(Parser & p, string const & name, ostream & os,
                               unsigned flags, bool outer,
@@ -708,9 +1244,10 @@ void parse_unknown_environment(Parser & p, string const & name, ostream & os,
 
 
 void parse_environment(Parser & p, ostream & os, bool outer,
-                      Context & parent_context)
+                       string & last_env, Context & parent_context)
 {
        Layout const * newlayout;
+       InsetLayout const * newinsetlayout = 0;
        string const name = p.getArg('{', '}');
        const bool is_starred = suffixIs(name, '*');
        string const unstarred_name = rtrim(name, "*");
@@ -723,24 +1260,70 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                parse_math(p, os, FLAG_END, MATH_MODE);
                os << "\\end{" << name << "}";
                end_inset(os);
+               if (is_display_math_env(name)) {
+                       // Prevent the conversion of a line break to a space
+                       // (bug 7668). This does not change the output, but
+                       // looks ugly in LyX.
+                       eat_whitespace(p, os, parent_context, false);
+               }
+       }
+
+       else if (is_known(name, polyglossia_languages)) {
+               // We must begin a new paragraph if not already done
+               if (! parent_context.atParagraphStart()) {
+                       parent_context.check_end_layout(os);
+                       parent_context.new_paragraph(os);
+               }
+               // save the language in the context so that it is
+               // handled by parse_text
+               parent_context.font.language = polyglossia2lyx(name);
+               parse_text(p, os, FLAG_END, outer, parent_context);
+               // Just in case the environment is empty
+               parent_context.extra_stuff.erase();
+               // We must begin a new paragraph to reset the language
+               parent_context.new_paragraph(os);
+               p.skip_spaces();
        }
 
-       else if (name == "tabular" || name == "longtable") {
+       else if (unstarred_name == "tabular" || name == "longtable") {
                eat_whitespace(p, os, parent_context, false);
+               string width = "0pt";
+               if (name == "tabular*") {
+                       width = lyx::translate_len(p.getArg('{', '}'));
+                       eat_whitespace(p, os, parent_context, false);
+               }
                parent_context.check_layout(os);
                begin_inset(os, "Tabular ");
-               handle_tabular(p, os, name == "longtable", parent_context);
+               handle_tabular(p, os, name, width, parent_context);
                end_inset(os);
                p.skip_spaces();
        }
 
        else if (parent_context.textclass.floats().typeExist(unstarred_name)) {
+               eat_whitespace(p, os, parent_context, false);
+               string const opt = p.hasOpt() ? p.getArg('[', ']') : string();
                eat_whitespace(p, os, parent_context, false);
                parent_context.check_layout(os);
                begin_inset(os, "Float " + unstarred_name + "\n");
-               if (p.next_token().asInput() == "[") {
-                       os << "placement " << p.getArg('[', ']') << '\n';
+               // store the float type for subfloats
+               // subfloats only work with figures and tables
+               if (unstarred_name == "figure")
+                       float_type = unstarred_name;
+               else if (unstarred_name == "table")
+                       float_type = unstarred_name;
+               else
+                       float_type = "";
+               if (!opt.empty())
+                       os << "placement " << opt << '\n';
+               if (contains(opt, "H"))
+                       preamble.registerAutomaticallyLoadedPackage("float");
+               else {
+                       Floating const & fl = parent_context.textclass.floats()
+                               .getType(unstarred_name);
+                       if (!fl.floattype().empty() && fl.usesFloatPkg())
+                               preamble.registerAutomaticallyLoadedPackage("float");
                }
+
                os << "wide " << convert<string>(is_starred)
                   << "\nsideways false"
                   << "\nstatus open\n\n";
@@ -750,11 +1333,88 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                // we must make sure that the next item gets a \begin_layout.
                parent_context.new_paragraph(os);
                p.skip_spaces();
+               // the float is parsed thus delete the type
+               float_type = "";
+       }
+
+       else if (unstarred_name == "sidewaysfigure"
+               || unstarred_name == "sidewaystable") {
+               eat_whitespace(p, os, parent_context, false);
+               parent_context.check_layout(os);
+               if (unstarred_name == "sidewaysfigure")
+                       begin_inset(os, "Float figure\n");
+               else
+                       begin_inset(os, "Float table\n");
+               os << "wide " << convert<string>(is_starred)
+                  << "\nsideways true"
+                  << "\nstatus open\n\n";
+               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+               end_inset(os);
+               // We don't need really a new paragraph, but
+               // we must make sure that the next item gets a \begin_layout.
+               parent_context.new_paragraph(os);
+               p.skip_spaces();
+               preamble.registerAutomaticallyLoadedPackage("rotfloat");
+       }
+
+       else if (name == "wrapfigure" || name == "wraptable") {
+               // syntax is \begin{wrapfigure}[lines]{placement}[overhang]{width}
+               eat_whitespace(p, os, parent_context, false);
+               parent_context.check_layout(os);
+               // default values
+               string lines = "0";
+               string overhang = "0col%";
+               // parse
+               if (p.hasOpt())
+                       lines = p.getArg('[', ']');
+               string const placement = p.getArg('{', '}');
+               if (p.hasOpt())
+                       overhang = p.getArg('[', ']');
+               string const width = p.getArg('{', '}');
+               // write
+               if (name == "wrapfigure")
+                       begin_inset(os, "Wrap figure\n");
+               else
+                       begin_inset(os, "Wrap table\n");
+               os << "lines " << lines
+                  << "\nplacement " << placement
+                  << "\noverhang " << lyx::translate_len(overhang)
+                  << "\nwidth " << lyx::translate_len(width)
+                  << "\nstatus open\n\n";
+               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+               end_inset(os);
+               // We don't need really a new paragraph, but
+               // we must make sure that the next item gets a \begin_layout.
+               parent_context.new_paragraph(os);
+               p.skip_spaces();
+               preamble.registerAutomaticallyLoadedPackage("wrapfig");
        }
 
        else if (name == "minipage") {
                eat_whitespace(p, os, parent_context, false);
-               parse_box(p, os, FLAG_END, outer, parent_context, false);
+               // Test whether this is an outer box of a shaded box
+               p.pushPosition();
+               // swallow arguments
+               while (p.hasOpt()) {
+                       p.getArg('[', ']');
+                       p.skip_spaces(true);
+               }
+               p.getArg('{', '}');
+               p.skip_spaces(true);
+               Token t = p.get_token();
+               bool shaded = false;
+               if (t.asInput() == "\\begin") {
+                       p.skip_spaces(true);
+                       if (p.getArg('{', '}') == "shaded")
+                               shaded = true;
+               }
+               p.popPosition();
+               if (shaded)
+                       parse_outer_box(p, os, FLAG_END, outer,
+                                       parent_context, name, "shaded");
+               else
+                       parse_box(p, os, 0, FLAG_END, outer, parent_context,
+                                 "", "", name);
                p.skip_spaces();
        }
 
@@ -767,6 +1427,82 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                end_inset(os);
                p.skip_spaces();
                skip_braces(p); // eat {} that might by set by LyX behind comments
+               preamble.registerAutomaticallyLoadedPackage("verbatim");
+       }
+
+       else if (name == "verbatim") {
+               os << "\n\\end_layout\n\n\\begin_layout Verbatim\n";
+               string const s = p.plainEnvironment("verbatim");
+               string::const_iterator it2 = s.begin();
+               for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
+                       if (*it == '\\')
+                               os << "\\backslash ";
+                       else if (*it == '\n') {
+                               it2 = it + 1;
+                               // avoid adding an empty paragraph at the end
+                               // FIXME: if there are 2 consecutive spaces at the end ignore it
+                               // because LyX will re-add a \n
+                               // This hack must be removed once bug 8049 is fixed!
+                               if ((it + 1 != et) && (it + 2 != et || *it2 != '\n'))
+                                       os << "\n\\end_layout\n\\begin_layout Verbatim\n";
+                       } else 
+                               os << *it;
+               }
+               os << "\n\\end_layout\n\n";
+               p.skip_spaces();
+               // reset to Standard layout
+               os << "\n\\begin_layout Standard\n";
+       }
+
+       else if (name == "CJK") {
+               // the scheme is \begin{CJK}{encoding}{mapping}{text}
+               // It is impossible to decide if a CJK environment was in its own paragraph or within
+               // a line. We therefore always assume a paragraph since the latter is a rare case.
+               eat_whitespace(p, os, parent_context, false);
+               parent_context.check_end_layout(os);
+               // store the encoding to be able to reset it
+               string const encoding_old = p.encoding_latex_;
+               string const encoding = p.getArg('{', '}');
+               // SJIS and BIG5 don't work with LaTeX according to the comment in unicode.cpp
+               // JIS does not work with LyX's encoding conversion
+               if (encoding != "SJIS" && encoding != "BIG5" && encoding != "JIS")
+                       p.setEncoding(encoding);
+               else
+                       p.setEncoding("utf8");
+               // LyX doesn't support the second argument so if
+               // this is used we need to output everything as ERT
+               string const mapping = p.getArg('{', '}');
+               if ( (!mapping.empty() && mapping != " ")
+                       || (!is_known(encoding, supported_CJK_encodings))) {
+                       parent_context.check_layout(os);
+                       handle_ert(os, "\\begin{" + name + "}{" + encoding + "}{" + mapping + "}",
+                                      parent_context);
+                       // we must parse the content as verbatim because e.g. SJIS can contain
+                       // normally invalid characters
+                       string const s = p.plainEnvironment("CJK");
+                       string::const_iterator it2 = s.begin();
+                       for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
+                               if (*it == '\\')
+                                       handle_ert(os, "\\", parent_context);
+                               else if (*it == '$')
+                                       handle_ert(os, "$", parent_context);
+                               else 
+                                       os << *it;
+                       }
+                       p.skip_spaces();
+                       handle_ert(os, "\\end{" + name + "}",
+                                      parent_context);
+               } else {
+                       string const lang = CJK2lyx(encoding);
+                       // store the language because we must reset it at the end
+                       string const lang_old = parent_context.font.language;
+                       parent_context.font.language = lang;
+                       parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+                       parent_context.font.language = lang_old;
+                       parent_context.new_paragraph(os);
+               }
+               p.encoding_latex_ = encoding_old;
+               p.skip_spaces();
        }
 
        else if (name == "lyxgreyedout") {
@@ -777,25 +1513,21 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
                end_inset(os);
                p.skip_spaces();
+               if (!preamble.notefontcolor().empty())
+                       preamble.registerAutomaticallyLoadedPackage("color");
        }
 
-       else if (name == "framed") {
+       else if (name == "framed" || name == "shaded") {
                eat_whitespace(p, os, parent_context, false);
-               parent_context.check_layout(os);
-               begin_inset(os, "Note Framed\n");
-               os << "status open\n";
-               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
-               end_inset(os);
+               parse_outer_box(p, os, FLAG_END, outer, parent_context, name, "");
                p.skip_spaces();
        }
 
-       else if (name == "shaded") {
+       else if (name == "lstlisting") {
                eat_whitespace(p, os, parent_context, false);
-               parent_context.check_layout(os);
-               begin_inset(os, "Note Shaded\n");
-               os << "status open\n";
-               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
-               end_inset(os);
+               // FIXME handle the automatic color package loading
+               // uwestoehr asks: In what case color is loaded?
+               parse_listings(p, os, parent_context, false);
                p.skip_spaces();
        }
 
@@ -828,12 +1560,16 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                        parent_context.add_extra_stuff("\\align center\n");
                else if (name == "singlespace")
                        parent_context.add_extra_stuff("\\paragraph_spacing single\n");
-               else if (name == "onehalfspace")
+               else if (name == "onehalfspace") {
                        parent_context.add_extra_stuff("\\paragraph_spacing onehalf\n");
-               else if (name == "doublespace")
+                       preamble.registerAutomaticallyLoadedPackage("setspace");
+               } else if (name == "doublespace") {
                        parent_context.add_extra_stuff("\\paragraph_spacing double\n");
-               else if (name == "spacing")
+                       preamble.registerAutomaticallyLoadedPackage("setspace");
+               } else if (name == "spacing") {
                        parent_context.add_extra_stuff("\\paragraph_spacing other " + p.verbatim_item() + "\n");
+                       preamble.registerAutomaticallyLoadedPackage("setspace");
+               }
                parse_text(p, os, FLAG_END, outer, parent_context);
                // Just in case the environment is empty
                parent_context.extra_stuff.erase();
@@ -843,8 +1579,7 @@ void parse_environment(Parser & p, ostream & os, bool outer,
        }
 
        // The single '=' is meant here.
-       else if ((newlayout = findLayout(parent_context.textclass, name)) &&
-                 newlayout->isEnvironment()) {
+       else if ((newlayout = findLayout(parent_context.textclass, name, false))) {
                eat_whitespace(p, os, parent_context, false);
                Context context(true, parent_context.textclass, newlayout,
                                parent_context.layout, parent_context.font);
@@ -855,9 +1590,31 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                        context.need_end_deeper = true;
                }
                parent_context.check_end_layout(os);
-               switch (context.layout->latextype) {
-               case  LATEX_LIST_ENVIRONMENT:
-                       context.add_par_extra_stuff("\\labelwidthstring "
+               if (last_env == name) {
+                       // we need to output a separator since LyX would export
+                       // the two environments as one otherwise (bug 5716)
+                       docstring const sep = from_ascii("--Separator--");
+                       TeX2LyXDocClass const & textclass(parent_context.textclass);
+                       if (textclass.hasLayout(sep)) {
+                               Context newcontext(parent_context);
+                               newcontext.layout = &(textclass[sep]);
+                               newcontext.check_layout(os);
+                               newcontext.check_end_layout(os);
+                       } else {
+                               parent_context.check_layout(os);
+                               begin_inset(os, "Note Note\n");
+                               os << "status closed\n";
+                               Context newcontext(true, textclass,
+                                               &(textclass.defaultLayout()));
+                               newcontext.check_layout(os);
+                               newcontext.check_end_layout(os);
+                               end_inset(os);
+                               parent_context.check_end_layout(os);
+                       }
+               }
+               switch (context.layout->latextype) {
+               case  LATEX_LIST_ENVIRONMENT:
+                       context.add_par_extra_stuff("\\labelwidthstring "
                                                    + p.verbatim_item() + '\n');
                        p.skip_spaces();
                        break;
@@ -869,6 +1626,49 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                        break;
                }
                context.check_deeper(os);
+               // handle known optional and required arguments
+               // layouts require all optional arguments before the required ones
+               // Unfortunately LyX can't handle arguments of list arguments (bug 7468):
+               // It is impossible to place anything after the environment name,
+               // but before the first \\item.
+               if (context.layout->latextype == LATEX_ENVIRONMENT) {
+                       bool need_layout = true;
+                       unsigned int optargs = 0;
+                       while (optargs < context.layout->optargs) {
+                               eat_whitespace(p, os, context, false);
+                               if (p.next_token().cat() == catEscape ||
+                                   p.next_token().character() != '[')
+                                       break;
+                               p.get_token(); // eat '['
+                               if (need_layout) {
+                                       context.check_layout(os);
+                                       need_layout = false;
+                               }
+                               begin_inset(os, "Argument\n");
+                               os << "status collapsed\n\n";
+                               parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
+                               end_inset(os);
+                               eat_whitespace(p, os, context, false);
+                               ++optargs;
+                       }
+                       unsigned int reqargs = 0;
+                       while (reqargs < context.layout->reqargs) {
+                               eat_whitespace(p, os, context, false);
+                               if (p.next_token().cat() != catBegin)
+                                       break;
+                               p.get_token(); // eat '{'
+                               if (need_layout) {
+                                       context.check_layout(os);
+                                       need_layout = false;
+                               }
+                               begin_inset(os, "Argument\n");
+                               os << "status collapsed\n\n";
+                               parse_text_in_inset(p, os, FLAG_BRACE_LAST, outer, context);
+                               end_inset(os);
+                               eat_whitespace(p, os, context, false);
+                               ++reqargs;
+                       }
+               }
                parse_text(p, os, FLAG_END, outer, context);
                context.check_end_layout(os);
                if (parent_context.deeper_paragraph) {
@@ -879,6 +1679,24 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                context.check_end_deeper(os);
                parent_context.new_paragraph(os);
                p.skip_spaces();
+               if (!preamble.titleLayoutFound())
+                       preamble.titleLayoutFound(newlayout->intitle);
+               set<string> const & req = newlayout->requires();
+               set<string>::const_iterator it = req.begin();
+               set<string>::const_iterator en = req.end();
+               for (; it != en; ++it)
+                       preamble.registerAutomaticallyLoadedPackage(*it);
+       }
+
+       // The single '=' is meant here.
+       else if ((newinsetlayout = findInsetLayout(parent_context.textclass, name, false))) {
+               eat_whitespace(p, os, parent_context, false);
+               parent_context.check_layout(os);
+               begin_inset(os, "Flex ");
+               os << to_utf8(newinsetlayout->name()) << '\n'
+                  << "status collapsed\n";
+               parse_text_in_inset(p, os, FLAG_END, false, parent_context, newinsetlayout);
+               end_inset(os);
        }
 
        else if (name == "appendix") {
@@ -929,6 +1747,7 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                parse_unknown_environment(p, name, os, FLAG_END, outer,
                                          parent_context);
 
+       last_env = name;
        active_environments.pop_back();
 }
 
@@ -1051,13 +1870,10 @@ string const normalize_filename(string const & name)
 /// convention (relative to .lyx file) if it is relative
 void fix_relative_filename(string & name)
 {
-       FileName fname(name);
-       if (fname.isAbsolute())
+       if (FileName::isAbsolute(name))
                return;
 
-       // FIXME UNICODE encoding of name may be wrong (makeAbsPath expects
-       // utf8)
-       name = to_utf8(makeRelPath(from_utf8(makeAbsPath(name, getMasterFilePath()).absFilename()),
+       name = to_utf8(makeRelPath(from_utf8(makeAbsPath(name, getMasterFilePath()).absFileName()),
                                   from_utf8(getParentFilePath())));
 }
 
@@ -1105,8 +1921,14 @@ void parse_noweb(Parser & p, ostream & os, Context & context)
                // at all.
                if (t.cat() == catEscape)
                        os << subst(t.asInput(), "\\", "\n\\backslash\n");
-               else
-                       os << subst(t.asInput(), "\n", "\n\\newline\n");
+               else {
+                       ostringstream oss;
+                       Context tmp(false, context.textclass,
+                                   &context.textclass[from_ascii("Scrap")]);
+                       tmp.need_end_layout = true;
+                       tmp.check_layout(oss);
+                       os << subst(t.asInput(), "\n", oss.str());
+               }
                // The scrap chunk is ended by an @ at the beginning of a line.
                // After the @ the line may contain a comment and/or
                // whitespace, but nothing else.
@@ -1130,6 +1952,136 @@ void parse_noweb(Parser & p, ostream & os, Context & context)
        newcontext.check_end_layout(os);
 }
 
+
+/// detects \\def, \\long\\def and \\global\\long\\def with ws and comments
+bool is_macro(Parser & p)
+{
+       Token first = p.curr_token();
+       if (first.cat() != catEscape || !p.good())
+               return false;
+       if (first.cs() == "def")
+               return true;
+       if (first.cs() != "global" && first.cs() != "long")
+               return false;
+       Token second = p.get_token();
+       int pos = 1;
+       while (p.good() && !p.isParagraph() && (second.cat() == catSpace ||
+              second.cat() == catNewline || second.cat() == catComment)) {
+               second = p.get_token();
+               pos++;
+       }
+       bool secondvalid = second.cat() == catEscape;
+       Token third;
+       bool thirdvalid = false;
+       if (p.good() && first.cs() == "global" && secondvalid &&
+           second.cs() == "long") {
+               third = p.get_token();
+               pos++;
+               while (p.good() && !p.isParagraph() &&
+                      (third.cat() == catSpace ||
+                       third.cat() == catNewline ||
+                       third.cat() == catComment)) {
+                       third = p.get_token();
+                       pos++;
+               }
+               thirdvalid = third.cat() == catEscape;
+       }
+       for (int i = 0; i < pos; ++i)
+               p.putback();
+       if (!secondvalid)
+               return false;
+       if (!thirdvalid)
+               return (first.cs() == "global" || first.cs() == "long") &&
+                      second.cs() == "def";
+       return first.cs() == "global" && second.cs() == "long" &&
+              third.cs() == "def";
+}
+
+
+/// Parse a macro definition (assumes that is_macro() returned true)
+void parse_macro(Parser & p, ostream & os, Context & context)
+{
+       context.check_layout(os);
+       Token first = p.curr_token();
+       Token second;
+       Token third;
+       string command = first.asInput();
+       if (first.cs() != "def") {
+               p.get_token();
+               eat_whitespace(p, os, context, false);
+               second = p.curr_token();
+               command += second.asInput();
+               if (second.cs() != "def") {
+                       p.get_token();
+                       eat_whitespace(p, os, context, false);
+                       third = p.curr_token();
+                       command += third.asInput();
+               }
+       }
+       eat_whitespace(p, os, context, false);
+       string const name = p.get_token().cs();
+       eat_whitespace(p, os, context, false);
+
+       // parameter text
+       bool simple = true;
+       string paramtext;
+       int arity = 0;
+       while (p.next_token().cat() != catBegin) {
+               if (p.next_token().cat() == catParameter) {
+                       // # found
+                       p.get_token();
+                       paramtext += "#";
+
+                       // followed by number?
+                       if (p.next_token().cat() == catOther) {
+                               char c = p.getChar();
+                               paramtext += c;
+                               // number = current arity + 1?
+                               if (c == arity + '0' + 1)
+                                       ++arity;
+                               else
+                                       simple = false;
+                       } else
+                               paramtext += p.get_token().cs();
+               } else {
+                       paramtext += p.get_token().cs();
+                       simple = false;
+               }
+       }
+
+       // only output simple (i.e. compatible) macro as FormulaMacros
+       string ert = '\\' + name + ' ' + paramtext + '{' + p.verbatim_item() + '}';
+       if (simple) {
+               context.check_layout(os);
+               begin_inset(os, "FormulaMacro");
+               os << "\n\\def" << ert;
+               end_inset(os);
+       } else
+               handle_ert(os, command + ert, context);
+}
+
+
+void registerExternalTemplatePackages(string const & name)
+{
+       external::TemplateManager const & etm = external::TemplateManager::get();
+       external::Template const * const et = etm.getTemplateByName(name);
+       if (!et)
+               return;
+       external::Template::Formats::const_iterator cit = et->formats.end();
+       if (pdflatex)
+               cit = et->formats.find("PDFLaTeX");
+       if (cit == et->formats.end())
+               // If the template has not specified a PDFLaTeX output,
+               // we try the LaTeX format.
+               cit = et->formats.find("LaTeX");
+       if (cit == et->formats.end())
+               return;
+       vector<string>::const_iterator qit = cit->second.requirements.begin();
+       vector<string>::const_iterator qend = cit->second.requirements.end();
+       for (; qit != qend; ++qit)
+               preamble.registerAutomaticallyLoadedPackage(*qit);
+}
+
 } // anonymous namespace
 
 
@@ -1137,17 +2089,57 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                Context & context)
 {
        Layout const * newlayout = 0;
-       // store the current selectlanguage to be used after \foreignlanguage
-       string selectlang;
-       // Store the latest bibliographystyle (needed for bibtex inset)
-       string bibliographystyle;
-       bool const use_natbib = used_packages.find("natbib") != used_packages.end();
-       bool const use_jurabib = used_packages.find("jurabib") != used_packages.end();
+       InsetLayout const * newinsetlayout = 0;
+       char const * const * where = 0;
+       // Store the latest bibliographystyle and nocite{*} option
+       // (needed for bibtex inset)
+       string btprint;
+       string bibliographystyle = "default";
+       bool const use_natbib = preamble.isPackageUsed("natbib");
+       bool const use_jurabib = preamble.isPackageUsed("jurabib");
+       string last_env;
        while (p.good()) {
                Token const & t = p.get_token();
 
+       // it is impossible to determine the correct document language if CJK is used.
+       // Therefore write a note at the beginning of the document
+       if (have_CJK) {
+               context.check_layout(os);
+               begin_inset(os, "Note Note\n");
+               os << "status open\n\\begin_layout Plain Layout\n"
+                  << "\\series bold\n"
+                  << "Important information:\n"
+                  << "\\end_layout\n\n"
+                  << "\\begin_layout Plain Layout\n"
+                  << "This document contains text in Chinese, Japanese or Korean.\n"
+                  << " It was therefore impossible for tex2lyx to set the correct document langue for your document."
+                  << " Please set in the document settings by yourself!\n"
+                  << "\\end_layout\n";
+               end_inset(os);
+               have_CJK = false;
+       }
+
+       // it is impossible to determine the correct encoding for non-CJK Japanese.
+       // Therefore write a note at the beginning of the document
+       if (is_nonCJKJapanese) {
+               context.check_layout(os);
+               begin_inset(os, "Note Note\n");
+               os << "status open\n\\begin_layout Plain Layout\n"
+                  << "\\series bold\n"
+                  << "Important information:\n"
+                  << "\\end_layout\n\n"
+                  << "\\begin_layout Plain Layout\n"
+                  << "This document is in Japanese (non-CJK).\n"
+                  << " It was therefore impossible for tex2lyx to determine the correct encoding."
+                  << " The encoding EUC-JP was assumed. If this is incorrect, please set the correct"
+                  << " encoding in the document settings.\n"
+                  << "\\end_layout\n";
+               end_inset(os);
+               is_nonCJKJapanese = false;
+       }
+
 #ifdef FILEDEBUG
-               cerr << "t: " << t << " flags: " << flags << "\n";
+               debugToken(cerr, t, flags);
 #endif
 
                if (flags & FLAG_ITEM) {
@@ -1166,9 +2158,18 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        flags |= FLAG_LEAVE;
                }
 
-               if (t.character() == ']' && (flags & FLAG_BRACK_LAST))
+               if (t.cat() != catEscape && t.character() == ']' &&
+                   (flags & FLAG_BRACK_LAST))
+                       return;
+               if (t.cat() == catEnd && (flags & FLAG_BRACE_LAST))
                        return;
 
+               // If there is anything between \end{env} and \begin{env} we
+               // don't need to output a separator.
+               if (t.cat() != catSpace && t.cat() != catNewline &&
+                   t.asInput() != "\\begin")
+                       last_env = "";
+
                //
                // cat codes
                //
@@ -1177,7 +2178,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        context.check_layout(os);
                        begin_inset(os, "Formula ");
                        Token const & n = p.get_token();
-                       if (n.cat() == catMath && outer) {
+                       bool const display(n.cat() == catMath && outer);
+                       if (display) {
                                // TeX's $$...$$ syntax for displayed math
                                os << "\\[";
                                parse_math(p, os, FLAG_SIMPLE, MATH_MODE);
@@ -1191,6 +2193,12 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                os << '$';
                        }
                        end_inset(os);
+                       if (display) {
+                               // Prevent the conversion of a line break to a
+                               // space (bug 7668). This does not change the
+                               // output, but looks ugly in LyX.
+                               eat_whitespace(p, os, context, false);
+                       }
                }
 
                else if (t.cat() == catSuper || t.cat() == catSub)
@@ -1257,8 +2265,33 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        handle_ert(os, s, context);
                }
 
-               else if (t.cat() == catLetter ||
-                              t.cat() == catOther ||
+               else if (t.cat() == catLetter) {
+                       context.check_layout(os);
+                       // Workaround for bug 4752.
+                       // FIXME: This whole code block needs to be removed
+                       //        when the bug is fixed and tex2lyx produces
+                       //        the updated file format.
+                       // The replacement algorithm in LyX is so stupid that
+                       // it even translates a phrase if it is part of a word.
+                       bool handled = false;
+                       for (int const * l = known_phrase_lengths; *l; ++l) {
+                               string phrase = t.cs();
+                               for (int i = 1; i < *l && p.next_token().isAlnumASCII(); ++i)
+                                       phrase += p.get_token().cs();
+                               if (is_known(phrase, known_coded_phrases)) {
+                                       handle_ert(os, phrase, context);
+                                       handled = true;
+                                       break;
+                               } else {
+                                       for (size_t i = 1; i < phrase.length(); ++i)
+                                               p.putback();
+                               }
+                       }
+                       if (!handled)
+                               os << t.cs();
+               }
+
+               else if (t.cat() == catOther ||
                               t.cat() == catAlign ||
                               t.cat() == catParameter) {
                        // This translates "&" to "\\&" which may be wrong...
@@ -1279,14 +2312,18 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        if (t.character() == '~') {
                                if (context.layout->free_spacing)
                                        os << ' ';
-                               else
-                                       os << "\\InsetSpace ~\n";
+                               else {
+                                       begin_inset(os, "space ~\n");
+                                       end_inset(os);
+                               }
                        } else
                                os << t.cs();
                }
 
-               else if (t.cat() == catBegin &&
-                        p.next_token().cat() == catEnd) {
+               else if (t.cat() == catBegin) {
+                       Token const next = p.next_token();
+                       Token const end = p.next_next_token();
+                       if (next.cat() == catEnd) {
                        // {}
                        Token const prev = p.prev_token();
                        p.get_token();
@@ -1296,14 +2333,19 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                ; // ignore it in {}`` or -{}-
                        else
                                handle_ert(os, "{}", context);
-
-               }
-
-               else if (t.cat() == catBegin) {
+                       } else if (next.cat() == catEscape &&
+                                  is_known(next.cs(), known_quotes) &&
+                                  end.cat() == catEnd) {
+                               // Something like {\textquoteright} (e.g.
+                               // from writer2latex). LyX writes
+                               // \textquoteright{}, so we may skip the
+                               // braces here for better readability.
+                               parse_text_snippet(p, os, FLAG_BRACE_LAST,
+                                                  outer, context);
+                       } else {
                        context.check_layout(os);
                        // special handling of font attribute changes
                        Token const prev = p.prev_token();
-                       Token const next = p.next_token();
                        TeXFont const oldFont = context.font;
                        if (next.character() == '[' ||
                            next.character() == ']' ||
@@ -1376,6 +2418,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                parse_text_snippet(p, os, FLAG_BRACE_LAST,
                                                   outer, context);
                                handle_ert(os, "}", context);
+                               }
                        }
                }
 
@@ -1410,10 +2453,15 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        parse_math(p, os, FLAG_EQUATION, MATH_MODE);
                        os << "\\]";
                        end_inset(os);
+                       // Prevent the conversion of a line break to a space
+                       // (bug 7668). This does not change the output, but
+                       // looks ugly in LyX.
+                       eat_whitespace(p, os, context, false);
                }
 
                else if (t.cs() == "begin")
-                       parse_environment(p, os, outer, context);
+                       parse_environment(p, os, outer, last_env,
+                                         context);
 
                else if (t.cs() == "end") {
                        if (flags & FLAG_END) {
@@ -1428,15 +2476,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                }
 
                else if (t.cs() == "item") {
-                       p.skip_spaces();
                        string s;
-                       bool optarg = false;
-                       if (p.next_token().character() == '[') {
-                               p.get_token(); // eat '['
-                               s = parse_text_snippet(p, FLAG_BRACK_LAST,
-                                                      outer, context);
-                               optarg = true;
-                       }
+                       bool const optarg = p.hasOpt();
+                       if (optarg) {
+                               // FIXME: This swallows comments, but we cannot use
+                               //        eat_whitespace() since we must not output
+                               //        anything before the item.
+                               p.skip_spaces(true);
+                               s = p.verbatimOption();
+                       } else
+                               p.skip_spaces(false);
                        context.set_item();
                        context.check_layout(os);
                        if (context.has_item) {
@@ -1450,15 +2499,32 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        }
                        if (optarg) {
                                if (context.layout->labeltype != LABEL_MANUAL) {
-                                       // lyx does not support \item[\mybullet]
+                                       // LyX does not support \item[\mybullet]
                                        // in itemize environments
-                                       handle_ert(os, "[", context);
-                                       os << s;
-                                       handle_ert(os, "]", context);
+                                       Parser p2(s + ']');
+                                       os << parse_text_snippet(p2,
+                                               FLAG_BRACK_LAST, outer, context);
                                } else if (!s.empty()) {
+                                       // LyX adds braces around the argument,
+                                       // so we need to remove them here.
+                                       if (s.size() > 2 && s[0] == '{' &&
+                                           s[s.size()-1] == '}')
+                                               s = s.substr(1, s.size()-2);
+                                       // If the argument contains a space we
+                                       // must put it into ERT: Otherwise LyX
+                                       // would misinterpret the space as
+                                       // item delimiter (bug 7663)
+                                       if (contains(s, ' ')) {
+                                               handle_ert(os, s, context);
+                                       } else {
+                                               Parser p2(s + ']');
+                                               os << parse_text_snippet(p2,
+                                                       FLAG_BRACK_LAST,
+                                                       outer, context);
+                                       }
                                        // The space is needed to separate the
                                        // item from the rest of the sentence.
-                                       os << s << ' ';
+                                       os << ' ';
                                        eat_whitespace(p, os, context, false);
                                }
                        }
@@ -1467,53 +2533,73 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                else if (t.cs() == "bibitem") {
                        context.set_item();
                        context.check_layout(os);
-                       os << "\\bibitem ";
-                       os << p.getOpt();
-                       os << '{' << p.verbatim_item() << '}' << "\n";
-               }
-
-               else if (t.cs() == "def") {
-                       context.check_layout(os);
-                       eat_whitespace(p, os, context, false);
-                       string name = p.get_token().cs();
                        eat_whitespace(p, os, context, false);
+                       string label = convert_command_inset_arg(p.verbatimOption());
+                       string key = convert_command_inset_arg(p.verbatim_item());
+                       if (contains(label, '\\') || contains(key, '\\')) {
+                               // LyX can't handle LaTeX commands in labels or keys
+                               handle_ert(os, t.asInput() + '[' + label +
+                                              "]{" + p.verbatim_item() + '}',
+                                          context);
+                       } else {
+                               begin_command_inset(os, "bibitem", "bibitem");
+                               os << "label \"" << label << "\"\n"
+                                     "key \"" << key << "\"\n";
+                               end_inset(os);
+                       }
+               }
 
-                       // parameter text
-                       bool simple = true;
-                       string paramtext;
-                       int arity = 0;
-                       while (p.next_token().cat() != catBegin) {
-                               if (p.next_token().cat() == catParameter) {
-                                       // # found
+               else if (is_macro(p)) {
+                       // catch the case of \def\inputGnumericTable
+                       bool macro = true;
+                       if (t.cs() == "def") {
+                               Token second = p.next_token();
+                               if (second.cs() == "inputGnumericTable") {
+                                       p.pushPosition();
                                        p.get_token();
-                                       paramtext += "#";
-
-                                       // followed by number?
-                                       if (p.next_token().cat() == catOther) {
-                                               char c = p.getChar();
-                                               paramtext += c;
-                                               // number = current arity + 1?
-                                               if (c == arity + '0' + 1)
-                                                       ++arity;
+                                       skip_braces(p);
+                                       Token third = p.get_token();
+                                       p.popPosition();
+                                       if (third.cs() == "input") {
+                                               p.get_token();
+                                               skip_braces(p);
+                                               p.get_token();
+                                               string name = normalize_filename(p.verbatim_item());
+                                               string const path = getMasterFilePath();
+                                               // We want to preserve relative / absolute filenames,
+                                               // therefore path is only used for testing
+                                               // The file extension is in every case ".tex".
+                                               // So we need to remove this extension and check for
+                                               // the original one.
+                                               name = removeExtension(name);
+                                               if (!makeAbsPath(name, path).exists()) {
+                                                       char const * const Gnumeric_formats[] = {"gnumeric",
+                                                               "ods", "xls", 0};
+                                                       string const Gnumeric_name =
+                                                               find_file(name, path, Gnumeric_formats);
+                                                       if (!Gnumeric_name.empty())
+                                                               name = Gnumeric_name;
+                                               }
+                                               if (makeAbsPath(name, path).exists())
+                                                       fix_relative_filename(name);
                                                else
-                                                       simple = false;
-                                       } else
-                                               paramtext += p.get_token().asString();
-                               } else {
-                                       paramtext += p.get_token().asString();
-                                       simple = false;
+                                                       cerr << "Warning: Could not find file '"
+                                                            << name << "'." << endl;
+                                               context.check_layout(os);
+                                               begin_inset(os, "External\n\ttemplate ");
+                                               os << "GnumericSpreadsheet\n\tfilename "
+                                                  << name << "\n";
+                                               end_inset(os);
+                                               context.check_layout(os);
+                                               macro = false;
+                                               // register the packages that are automatically reloaded
+                                               // by the Gnumeric template
+                                               registerExternalTemplatePackages("GnumericSpreadsheet");
+                                       }
                                }
                        }
-
-                       // only output simple (i.e. compatible) macro as FormulaMacros
-                       string ert = "\\def\\" + name + ' ' + paramtext + '{' + p.verbatim_item() + '}';
-                       if (simple) {
-                               context.check_layout(os);
-                               begin_inset(os, "FormulaMacro");
-                               os << "\n" << ert;
-                               end_inset(os);
-                       } else
-                               handle_ert(os, ert, context);
+                       if (macro)
+                               parse_macro(p, os, context);
                }
 
                else if (t.cs() == "noindent") {
@@ -1544,61 +2630,164 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        eat_whitespace(p, os, context, true);
                }
 
+               // Must catch empty dates before findLayout is called below
+               else if (t.cs() == "date") {
+                       eat_whitespace(p, os, context, false);
+                       p.pushPosition();
+                       string const date = p.verbatim_item();
+                       p.popPosition();
+                       if (date.empty()) {
+                               preamble.suppressDate(true);
+                               p.verbatim_item();
+                       } else {
+                               preamble.suppressDate(false);
+                               if (context.new_layout_allowed &&
+                                   (newlayout = findLayout(context.textclass,
+                                                           t.cs(), true))) {
+                                       // write the layout
+                                       output_command_layout(os, p, outer,
+                                                       context, newlayout);
+                                       parse_text_snippet(p, os, FLAG_ITEM, outer, context);
+                                       if (!preamble.titleLayoutFound())
+                                               preamble.titleLayoutFound(newlayout->intitle);
+                                       set<string> const & req = newlayout->requires();
+                                       set<string>::const_iterator it = req.begin();
+                                       set<string>::const_iterator en = req.end();
+                                       for (; it != en; ++it)
+                                               preamble.registerAutomaticallyLoadedPackage(*it);
+                               } else
+                                       handle_ert(os,
+                                               "\\date{" + p.verbatim_item() + '}',
+                                               context);
+                       }
+               }
+
                // Starred section headings
                // Must attempt to parse "Section*" before "Section".
                else if ((p.next_token().asInput() == "*") &&
                         context.new_layout_allowed &&
-                        (newlayout = findLayout(context.textclass, t.cs() + '*')) &&
-                        newlayout->isCommand()) {
-                       TeXFont const oldFont = context.font;
-                       // save the current font size
-                       string const size = oldFont.size;
-                       // reset the font size to default, because the font size switches don't
-                       // affect section headings and the like
-                       context.font.size = known_coded_sizes[0];
-                       output_font_change(os, oldFont, context.font);
+                        (newlayout = findLayout(context.textclass, t.cs() + '*', true))) {
                        // write the layout
                        p.get_token();
                        output_command_layout(os, p, outer, context, newlayout);
-                       // set the font size to the original value
-                       context.font.size = size;
-                       output_font_change(os, oldFont, context.font);
                        p.skip_spaces();
+                       if (!preamble.titleLayoutFound())
+                               preamble.titleLayoutFound(newlayout->intitle);
+                       set<string> const & req = newlayout->requires();
+                       for (set<string>::const_iterator it = req.begin(); it != req.end(); ++it)
+                               preamble.registerAutomaticallyLoadedPackage(*it);
                }
 
                // Section headings and the like
                else if (context.new_layout_allowed &&
-                        (newlayout = findLayout(context.textclass, t.cs())) &&
-                        newlayout->isCommand()) {
-                       TeXFont const oldFont = context.font;
-                       // save the current font size
-                       string const size = oldFont.size;
-                       // reset the font size to default, because the font size switches don't
-                       // affect section headings and the like
-                       context.font.size = known_coded_sizes[0];
-                       output_font_change(os, oldFont, context.font);
+                        (newlayout = findLayout(context.textclass, t.cs(), true))) {
                        // write the layout
                        output_command_layout(os, p, outer, context, newlayout);
-                       // set the font size to the original value
-                       context.font.size = size;
-                       output_font_change(os, oldFont, context.font);
                        p.skip_spaces();
+                       if (!preamble.titleLayoutFound())
+                               preamble.titleLayoutFound(newlayout->intitle);
+                       set<string> const & req = newlayout->requires();
+                       for (set<string>::const_iterator it = req.begin(); it != req.end(); ++it)
+                               preamble.registerAutomaticallyLoadedPackage(*it);
                }
 
-               // Special handling for \caption
-               // FIXME: remove this when InsetCaption is supported.
-               else if (context.new_layout_allowed &&
-                        t.cs() == captionlayout()->latexname()) {
-                       output_command_layout(os, p, outer, context, 
-                                             captionlayout());
+               else if (t.cs() == "caption") {
+                       p.skip_spaces();
+                       context.check_layout(os);
+                       p.skip_spaces();
+                       begin_inset(os, "Caption\n");
+                       Context newcontext(true, context.textclass);
+                       newcontext.font = context.font;
+                       newcontext.check_layout(os);
+                       if (p.next_token().cat() != catEscape &&
+                           p.next_token().character() == '[') {
+                               p.get_token(); // eat '['
+                               begin_inset(os, "Argument\n");
+                               os << "status collapsed\n";
+                               parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
+                               end_inset(os);
+                               eat_whitespace(p, os, context, false);
+                       }
+                       parse_text(p, os, FLAG_ITEM, outer, context);
+                       context.check_end_layout(os);
+                       // We don't need really a new paragraph, but
+                       // we must make sure that the next item gets a \begin_layout.
+                       context.new_paragraph(os);
+                       end_inset(os);
+                       p.skip_spaces();
+                       newcontext.check_end_layout(os);
+               }
+
+               else if (t.cs() == "subfloat") {
+                       // the syntax is \subfloat[caption]{content}
+                       // if it is a table of figure depends on the surrounding float
+                       bool has_caption = false;
                        p.skip_spaces();
+                       // do nothing if there is no outer float
+                       if (!float_type.empty()) {
+                               context.check_layout(os);
+                               p.skip_spaces();
+                               begin_inset(os, "Float " + float_type + "\n");
+                               os << "wide false"
+                                  << "\nsideways false"
+                                  << "\nstatus collapsed\n\n";
+                               // test for caption
+                               string caption;
+                               if (p.next_token().cat() != catEscape &&
+                                               p.next_token().character() == '[') {
+                                                       p.get_token(); // eat '['
+                                                       caption = parse_text_snippet(p, FLAG_BRACK_LAST, outer, context);
+                                                       has_caption = true;
+                               }
+                               // the content
+                               parse_text_in_inset(p, os, FLAG_ITEM, outer, context);
+                               // the caption comes always as the last
+                               if (has_caption) {
+                                       // we must make sure that the caption gets a \begin_layout
+                                       os << "\n\\begin_layout Plain Layout";
+                                       p.skip_spaces();
+                                       begin_inset(os, "Caption\n");
+                                       Context newcontext(true, context.textclass);
+                                       newcontext.font = context.font;
+                                       newcontext.check_layout(os);
+                                       os << caption << "\n";
+                                       newcontext.check_end_layout(os);
+                                       // We don't need really a new paragraph, but
+                                       // we must make sure that the next item gets a \begin_layout.
+                                       //newcontext.new_paragraph(os);
+                                       end_inset(os);
+                                       p.skip_spaces();
+                               }
+                               // We don't need really a new paragraph, but
+                               // we must make sure that the next item gets a \begin_layout.
+                               if (has_caption)
+                                       context.new_paragraph(os);
+                               end_inset(os);
+                               p.skip_spaces();
+                               context.check_end_layout(os);
+                               // close the layout we opened
+                               if (has_caption)
+                                       os << "\n\\end_layout\n";
+                       } else {
+                               // if the float type is not supported or there is no surrounding float
+                               // output it as ERT
+                               if (p.hasOpt()) {
+                                       string opt_arg = convert_command_inset_arg(p.getArg('[', ']'));
+                                       handle_ert(os, t.asInput() + '[' + opt_arg +
+                                              "]{" + p.verbatim_item() + '}', context);
+                               } else
+                                       handle_ert(os, t.asInput() + "{" + p.verbatim_item() + '}', context);
+                       }
                }
 
                else if (t.cs() == "includegraphics") {
                        bool const clip = p.next_token().asInput() == "*";
                        if (clip)
                                p.get_token();
-                       map<string, string> opts = split_map(p.getArg('[', ']'));
+                       string const arg = p.getArg('[', ']');
+                       map<string, string> opts;
+                       vector<string> keys;
+                       split_map(arg, opts, keys);
                        if (clip)
                                opts["clip"] = string();
                        string name = normalize_filename(p.verbatim_item());
@@ -1606,8 +2795,6 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        string const path = getMasterFilePath();
                        // We want to preserve relative / absolute filenames,
                        // therefore path is only used for testing
-                       // FIXME UNICODE encoding of name and path may be
-                       // wrong (makeAbsPath expects utf8)
                        if (!makeAbsPath(name, path).exists()) {
                                // The file extension is probably missing.
                                // Now try to find it out.
@@ -1634,12 +2821,12 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                                     << endl;
                                        }
                                        name = dvips_name;
-                               } else if (!pdftex_name.empty())
+                               } else if (!pdftex_name.empty()) {
                                        name = pdftex_name;
+                                       pdflatex = true;
+                               }
                        }
 
-                       // FIXME UNICODE encoding of name and path may be
-                       // wrong (makeAbsPath expects utf8)
                        if (makeAbsPath(name, path).exists())
                                fix_relative_filename(name);
                        else
@@ -1662,9 +2849,20 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                val = val*100;
                                os << "\tscale " << val << '\n';
                        }
-                       if (opts.find("angle") != opts.end())
+                       if (opts.find("angle") != opts.end()) {
                                os << "\trotateAngle "
                                   << opts["angle"] << '\n';
+                               vector<string>::const_iterator a =
+                                       find(keys.begin(), keys.end(), "angle");
+                               vector<string>::const_iterator s =
+                                       find(keys.begin(), keys.end(), "width");
+                               if (s == keys.end())
+                                       s = find(keys.begin(), keys.end(), "height");
+                               if (s == keys.end())
+                                       s = find(keys.begin(), keys.end(), "scale");
+                               if (s != keys.end() && distance(s, a) > 0)
+                                       os << "\tscaleBeforeRotation\n";
+                       }
                        if (opts.find("origin") != opts.end()) {
                                ostringstream ss;
                                string const opt = opts["origin"];
@@ -1739,6 +2937,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        // Warn about invalid options.
                        // Check whether some option was given twice.
                        end_inset(os);
+                       preamble.registerAutomaticallyLoadedPackage("graphicx");
                }
 
                else if (t.cs() == "footnote" ||
@@ -1760,6 +2959,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        end_inset(os);
                }
 
+               else if (t.cs() == "lstinline") {
+                       p.skip_spaces();
+                       parse_listings(p, os, context, true);
+               }
+
                else if (t.cs() == "ensuremath") {
                        p.skip_spaces();
                        context.check_layout(os);
@@ -1772,47 +2976,40 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                           context);
                }
 
-               else if (t.cs() == "hfill") {
-                       context.check_layout(os);
-                       os << "\n\\hfill\n";
-                       skip_braces(p);
-                       p.skip_spaces();
-               }
-
                else if (t.cs() == "makeindex" || t.cs() == "maketitle") {
-                       // FIXME: Somehow prevent title layouts if
-                       // "maketitle" was not found
-                       p.skip_spaces();
-                       skip_braces(p); // swallow this
+                       if (preamble.titleLayoutFound()) {
+                               // swallow this
+                               skip_spaces_braces(p);
+                       } else
+                               handle_ert(os, t.asInput(), context);
                }
 
-               else if (t.cs() == "tableofcontents") {
-                       p.skip_spaces();
+               else if (t.cs() == "tableofcontents" || t.cs() == "lstlistoflistings") {
                        context.check_layout(os);
-                       begin_inset(os, "LatexCommand \\tableofcontents\n");
+                       begin_command_inset(os, "toc", t.cs());
                        end_inset(os);
-                       skip_braces(p); // swallow this
+                       skip_spaces_braces(p);
+                       if (t.cs() == "lstlistoflistings")
+                               preamble.registerAutomaticallyLoadedPackage("listings");
                }
 
                else if (t.cs() == "listoffigures") {
-                       p.skip_spaces();
                        context.check_layout(os);
                        begin_inset(os, "FloatList figure\n");
                        end_inset(os);
-                       skip_braces(p); // swallow this
+                       skip_spaces_braces(p);
                }
 
                else if (t.cs() == "listoftables") {
-                       p.skip_spaces();
                        context.check_layout(os);
                        begin_inset(os, "FloatList table\n");
                        end_inset(os);
-                       skip_braces(p); // swallow this
+                       skip_spaces_braces(p);
                }
 
                else if (t.cs() == "listof") {
                        p.skip_spaces(true);
-                       string const name = p.get_token().asString();
+                       string const name = p.get_token().cs();
                        if (context.textclass.floats().typeExist(name)) {
                                context.check_layout(os);
                                begin_inset(os, "FloatList ");
@@ -1823,50 +3020,20 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                handle_ert(os, "\\listof{" + name + "}", context);
                }
 
-               else if (t.cs() == "textrm")
-                       parse_text_attributes(p, os, FLAG_ITEM, outer,
-                                             context, "\\family",
-                                             context.font.family, "roman");
-
-               else if (t.cs() == "textsf")
+               else if ((where = is_known(t.cs(), known_text_font_families)))
                        parse_text_attributes(p, os, FLAG_ITEM, outer,
-                                             context, "\\family",
-                                             context.font.family, "sans");
+                               context, "\\family", context.font.family,
+                               known_coded_font_families[where - known_text_font_families]);
 
-               else if (t.cs() == "texttt")
+               else if ((where = is_known(t.cs(), known_text_font_series)))
                        parse_text_attributes(p, os, FLAG_ITEM, outer,
-                                             context, "\\family",
-                                             context.font.family, "typewriter");
+                               context, "\\series", context.font.series,
+                               known_coded_font_series[where - known_text_font_series]);
 
-               else if (t.cs() == "textmd")
+               else if ((where = is_known(t.cs(), known_text_font_shapes)))
                        parse_text_attributes(p, os, FLAG_ITEM, outer,
-                                             context, "\\series",
-                                             context.font.series, "medium");
-
-               else if (t.cs() == "textbf")
-                       parse_text_attributes(p, os, FLAG_ITEM, outer,
-                                             context, "\\series",
-                                             context.font.series, "bold");
-
-               else if (t.cs() == "textup")
-                       parse_text_attributes(p, os, FLAG_ITEM, outer,
-                                             context, "\\shape",
-                                             context.font.shape, "up");
-
-               else if (t.cs() == "textit")
-                       parse_text_attributes(p, os, FLAG_ITEM, outer,
-                                             context, "\\shape",
-                                             context.font.shape, "italic");
-
-               else if (t.cs() == "textsl")
-                       parse_text_attributes(p, os, FLAG_ITEM, outer,
-                                             context, "\\shape",
-                                             context.font.shape, "slanted");
-
-               else if (t.cs() == "textsc")
-                       parse_text_attributes(p, os, FLAG_ITEM, outer,
-                                             context, "\\shape",
-                                             context.font.shape, "smallcaps");
+                               context, "\\shape", context.font.shape,
+                               known_coded_font_shapes[where - known_text_font_shapes]);
 
                else if (t.cs() == "textnormal" || t.cs() == "normalfont") {
                        context.check_layout(os);
@@ -1896,12 +3063,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                        parse_text_snippet(p, os, FLAG_ITEM, outer, context);
                                        context.check_layout(os);
                                        os << "\n\\color inherit\n";
+                                       preamble.registerAutomaticallyLoadedPackage("color");
                        } else
                                // for custom defined colors
                                handle_ert(os, t.asInput() + "{" + color + "}", context);
                }
 
-               else if (t.cs() == "underbar") {
+               else if (t.cs() == "underbar" || t.cs() == "uline") {
+                       // \underbar is not 100% correct (LyX outputs \uline
+                       // of ulem.sty). The difference is that \ulem allows
+                       // line breaks, and \underbar does not.
                        // Do NOT handle \underline.
                        // \underbar cuts through y, g, q, p etc.,
                        // \underline does not.
@@ -1910,19 +3081,184 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        parse_text_snippet(p, os, FLAG_ITEM, outer, context);
                        context.check_layout(os);
                        os << "\n\\bar default\n";
+                       preamble.registerAutomaticallyLoadedPackage("ulem");
+               }
+
+               else if (t.cs() == "sout") {
+                       context.check_layout(os);
+                       os << "\n\\strikeout on\n";
+                       parse_text_snippet(p, os, FLAG_ITEM, outer, context);
+                       context.check_layout(os);
+                       os << "\n\\strikeout default\n";
+                       preamble.registerAutomaticallyLoadedPackage("ulem");
                }
 
-               else if (t.cs() == "emph" || t.cs() == "noun") {
+               else if (t.cs() == "uuline" || t.cs() == "uwave" ||
+                        t.cs() == "emph" || t.cs() == "noun") {
                        context.check_layout(os);
                        os << "\n\\" << t.cs() << " on\n";
                        parse_text_snippet(p, os, FLAG_ITEM, outer, context);
                        context.check_layout(os);
                        os << "\n\\" << t.cs() << " default\n";
+                       if (t.cs() == "uuline" || t.cs() == "uwave")
+                               preamble.registerAutomaticallyLoadedPackage("ulem");
+               }
+
+               else if (t.cs() == "lyxadded" || t.cs() == "lyxdeleted") {
+                       context.check_layout(os);
+                       string name = p.getArg('{', '}');
+                       string localtime = p.getArg('{', '}');
+                       preamble.registerAuthor(name);
+                       Author const & author = preamble.getAuthor(name);
+                       // from_ctime() will fail if LyX decides to output the
+                       // time in the text language. It might also use a wrong
+                       // time zone (if the original LyX document was exported
+                       // with a different time zone).
+                       time_t ptime = from_ctime(localtime);
+                       if (ptime == static_cast<time_t>(-1)) {
+                               cerr << "Warning: Could not parse time `" << localtime
+                                    << "´ for change tracking, using current time instead.\n";
+                               ptime = current_time();
+                       }
+                       if (t.cs() == "lyxadded")
+                               os << "\n\\change_inserted ";
+                       else
+                               os << "\n\\change_deleted ";
+                       os << author.bufferId() << ' ' << ptime << '\n';
+                       parse_text_snippet(p, os, FLAG_ITEM, outer, context);
+                       bool dvipost    = LaTeXPackages::isAvailable("dvipost");
+                       bool xcolorulem = LaTeXPackages::isAvailable("ulem") &&
+                                         LaTeXPackages::isAvailable("xcolor");
+                       // No need to test for luatex, since luatex comes in
+                       // two flavours (dvi and pdf), like latex, and those
+                       // are detected by pdflatex.
+                       if (pdflatex || xetex) {
+                               if (xcolorulem) {
+                                       preamble.registerAutomaticallyLoadedPackage("ulem");
+                                       preamble.registerAutomaticallyLoadedPackage("xcolor");
+                                       preamble.registerAutomaticallyLoadedPackage("pdfcolmk");
+                               }
+                       } else {
+                               if (dvipost) {
+                                       preamble.registerAutomaticallyLoadedPackage("dvipost");
+                               } else if (xcolorulem) {
+                                       preamble.registerAutomaticallyLoadedPackage("ulem");
+                                       preamble.registerAutomaticallyLoadedPackage("xcolor");
+                               }
+                       }
+               }
+
+               else if (t.cs() == "phantom" || t.cs() == "hphantom" ||
+                            t.cs() == "vphantom") {
+                       context.check_layout(os);
+                       if (t.cs() == "phantom")
+                               begin_inset(os, "Phantom Phantom\n");
+                       if (t.cs() == "hphantom")
+                               begin_inset(os, "Phantom HPhantom\n");
+                       if (t.cs() == "vphantom")
+                               begin_inset(os, "Phantom VPhantom\n");
+                       os << "status open\n";
+                       parse_text_in_inset(p, os, FLAG_ITEM, outer, context,
+                                           "Phantom");
+                       end_inset(os);
+               }
+
+               else if (t.cs() == "href") {
+                       context.check_layout(os);
+                       string target = p.getArg('{', '}');
+                       string name = p.getArg('{', '}');
+                       string type;
+                       size_t i = target.find(':');
+                       if (i != string::npos) {
+                               type = target.substr(0, i + 1);
+                               if (type == "mailto:" || type == "file:")
+                                       target = target.substr(i + 1);
+                               // handle the case that name is equal to target, except of "http://"
+                               else if (target.substr(i + 3) == name && type == "http:")
+                                       target = name;
+                       }
+                       begin_command_inset(os, "href", "href");
+                       if (name != target)
+                               os << "name \"" << name << "\"\n";
+                       os << "target \"" << target << "\"\n";
+                       if (type == "mailto:" || type == "file:")
+                               os << "type \"" << type << "\"\n";
+                       end_inset(os);
+                       skip_spaces_braces(p);
                }
 
                else if (t.cs() == "lyxline") {
+                       // swallow size argument (it is not used anyway)
+                       p.getArg('{', '}');
+                       if (!context.atParagraphStart()) {
+                               // so our line is in the middle of a paragraph
+                               // we need to add a new line, lest this line
+                               // follow the other content on that line and
+                               // run off the side of the page
+                               // FIXME: This may create an empty paragraph,
+                               //        but without that it would not be
+                               //        possible to set noindent below.
+                               //        Fortunately LaTeX does not care
+                               //        about the empty paragraph.
+                               context.new_paragraph(os);
+                       }
+                       if (preamble.indentParagraphs()) {
+                               // we need to unindent, lest the line be too long
+                               context.add_par_extra_stuff("\\noindent\n");
+                       }
+                       context.check_layout(os);
+                       begin_command_inset(os, "line", "rule");
+                       os << "offset \"0.5ex\"\n"
+                             "width \"100line%\"\n"
+                             "height \"1pt\"\n";
+                       end_inset(os);
+               }
+
+               else if (t.cs() == "rule") {
+                       string const offset = (p.hasOpt() ? p.getArg('[', ']') : string());
+                       string const width = p.getArg('{', '}');
+                       string const thickness = p.getArg('{', '}');
                        context.check_layout(os);
-                       os << "\\lyxline";
+                       begin_command_inset(os, "line", "rule");
+                       if (!offset.empty())
+                               os << "offset \"" << translate_len(offset) << "\"\n";
+                       os << "width \"" << translate_len(width) << "\"\n"
+                                 "height \"" << translate_len(thickness) << "\"\n";
+                       end_inset(os);
+               }
+
+               else if (is_known(t.cs(), known_phrases) ||
+                        (t.cs() == "protect" &&
+                         p.next_token().cat() == catEscape &&
+                         is_known(p.next_token().cs(), known_phrases))) {
+                       // LyX sometimes puts a \protect in front, so we have to ignore it
+                       // FIXME: This needs to be changed when bug 4752 is fixed.
+                       where = is_known(
+                               t.cs() == "protect" ? p.get_token().cs() : t.cs(),
+                               known_phrases);
+                       context.check_layout(os);
+                       os << known_coded_phrases[where - known_phrases];
+                       skip_spaces_braces(p);
+               }
+
+               else if ((where = is_known(t.cs(), known_ref_commands))) {
+                       string const opt = p.getOpt();
+                       if (opt.empty()) {
+                               context.check_layout(os);
+                               begin_command_inset(os, "ref",
+                                       known_coded_ref_commands[where - known_ref_commands]);
+                               os << "reference \""
+                                  << convert_command_inset_arg(p.verbatim_item())
+                                  << "\"\n";
+                               end_inset(os);
+                               if (t.cs() == "vref" || t.cs() == "vpageref")
+                                       preamble.registerAutomaticallyLoadedPackage("varioref");
+
+                       } else {
+                               // LyX does not support optional arguments of ref commands
+                               handle_ert(os, t.asInput() + '[' + opt + "]{" +
+                                              p.verbatim_item() + "}", context);
+                       }
                }
 
                else if (use_natbib &&
@@ -1932,19 +3268,14 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                           t.cs() != "citeyearpar") ||
                          p.next_token().asInput() != "*")) {
                        context.check_layout(os);
-                       // tex                       lyx
-                       // \citet[before][after]{a}  \citet[after][before]{a}
-                       // \citet[before][]{a}       \citet[][before]{a}
-                       // \citet[after]{a}          \citet[after]{a}
-                       // \citet{a}                 \citet{a}
-                       string command = '\\' + t.cs();
+                       string command = t.cs();
                        if (p.next_token().asInput() == "*") {
                                command += '*';
                                p.get_token();
                        }
-                       if (command == "\\citefullauthor")
+                       if (command == "citefullauthor")
                                // alternative name for "\\citeauthor*"
-                               command = "\\citeauthor*";
+                               command = "citeauthor*";
 
                        // text before the citation
                        string before;
@@ -1952,14 +3283,14 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        string after;
                        get_cite_arguments(p, true, before, after);
 
-                       if (command == "\\cite") {
+                       if (command == "cite") {
                                // \cite without optional argument means
                                // \citet, \cite with at least one optional
                                // argument means \citep.
                                if (before.empty() && after.empty())
-                                       command = "\\citet";
+                                       command = "citet";
                                else
-                                       command = "\\citep";
+                                       command = "citep";
                        }
                        if (before.empty() && after == "[]")
                                // avoid \citet[]{a}
@@ -1969,18 +3300,38 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                before.erase();
                                after.erase();
                        }
-                       begin_inset(os, "LatexCommand ");
-                       os << command << after << before
-                          << '{' << p.verbatim_item() << "}\n";
+                       // remove the brackets around after and before
+                       if (!after.empty()) {
+                               after.erase(0, 1);
+                               after.erase(after.length() - 1, 1);
+                               after = convert_command_inset_arg(after);
+                       }
+                       if (!before.empty()) {
+                               before.erase(0, 1);
+                               before.erase(before.length() - 1, 1);
+                               before = convert_command_inset_arg(before);
+                       }
+                       begin_command_inset(os, "citation", command);
+                       os << "after " << '"' << after << '"' << "\n";
+                       os << "before " << '"' << before << '"' << "\n";
+                       os << "key \""
+                          << convert_command_inset_arg(p.verbatim_item())
+                          << "\"\n";
                        end_inset(os);
                }
 
                else if (use_jurabib &&
-                        is_known(t.cs(), known_jurabib_commands)) {
+                        is_known(t.cs(), known_jurabib_commands) &&
+                        (t.cs() == "cite" || p.next_token().asInput() != "*")) {
                        context.check_layout(os);
-                       string const command = '\\' + t.cs();
+                       string command = t.cs();
+                       if (p.next_token().asInput() == "*") {
+                               command += '*';
+                               p.get_token();
+                       }
                        char argumentOrder = '\0';
-                       vector<string> const & options = used_packages["jurabib"];
+                       vector<string> const options =
+                               preamble.getPackageOptions("jurabib");
                        if (find(options.begin(), options.end(),
                                      "natbiborder") != options.end())
                                argumentOrder = 'n';
@@ -2004,29 +3355,124 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                        "package options if you used an\n"
                                        "earlier jurabib version." << endl;
                        }
-                       begin_inset(os, "LatexCommand ");
-                       os << command << after << before
-                          << '{' << citation << "}\n";
+                       if (!after.empty()) {
+                               after.erase(0, 1);
+                               after.erase(after.length() - 1, 1);
+                       }
+                       if (!before.empty()) {
+                               before.erase(0, 1);
+                               before.erase(before.length() - 1, 1);
+                       }
+                       begin_command_inset(os, "citation", command);
+                       os << "after " << '"' << after << '"' << "\n";
+                       os << "before " << '"' << before << '"' << "\n";
+                       os << "key " << '"' << citation << '"' << "\n";
+                       end_inset(os);
+               }
+
+               else if (t.cs() == "cite"
+                       || t.cs() == "nocite") {
+                       context.check_layout(os);
+                       string after = convert_command_inset_arg(p.getArg('[', ']'));
+                       string key = convert_command_inset_arg(p.verbatim_item());
+                       // store the case that it is "\nocite{*}" to use it later for
+                       // the BibTeX inset
+                       if (key != "*") {
+                               begin_command_inset(os, "citation", t.cs());
+                               os << "after " << '"' << after << '"' << "\n";
+                               os << "key " << '"' << key << '"' << "\n";
+                               end_inset(os);
+                       } else if (t.cs() == "nocite")
+                               btprint = key;
+               }
+
+               else if (t.cs() == "index" ||
+                        (t.cs() == "sindex" && preamble.use_indices() == "true")) {
+                       context.check_layout(os);
+                       string const arg = (t.cs() == "sindex" && p.hasOpt()) ?
+                               p.getArg('[', ']') : "";
+                       string const kind = arg.empty() ? "idx" : arg;
+                       begin_inset(os, "Index ");
+                       os << kind << "\nstatus collapsed\n";
+                       parse_text_in_inset(p, os, FLAG_ITEM, false, context, "Index");
+                       end_inset(os);
+                       if (kind != "idx")
+                               preamble.registerAutomaticallyLoadedPackage("splitidx");
+               }
+
+               else if (t.cs() == "nomenclature") {
+                       context.check_layout(os);
+                       begin_command_inset(os, "nomenclature", "nomenclature");
+                       string prefix = convert_command_inset_arg(p.getArg('[', ']'));
+                       if (!prefix.empty())
+                               os << "prefix " << '"' << prefix << '"' << "\n";
+                       os << "symbol " << '"'
+                          << convert_command_inset_arg(p.verbatim_item());
+                       os << "\"\ndescription \""
+                          << convert_command_inset_arg(p.verbatim_item())
+                          << "\"\n";
+                       end_inset(os);
+                       preamble.registerAutomaticallyLoadedPackage("nomencl");
+               }
+
+               else if (t.cs() == "label") {
+                       context.check_layout(os);
+                       begin_command_inset(os, "label", "label");
+                       os << "name \""
+                          << convert_command_inset_arg(p.verbatim_item())
+                          << "\"\n";
+                       end_inset(os);
+               }
+
+               else if (t.cs() == "printindex") {
+                       context.check_layout(os);
+                       begin_command_inset(os, "index_print", "printindex");
+                       os << "type \"idx\"\n";
+                       end_inset(os);
+                       skip_spaces_braces(p);
+                       preamble.registerAutomaticallyLoadedPackage("makeidx");
+                       if (preamble.use_indices() == "true")
+                               preamble.registerAutomaticallyLoadedPackage("splitidx");
+               }
+
+               else if (t.cs() == "printnomenclature") {
+                       string width = "";
+                       string width_type = "";
+                       context.check_layout(os);
+                       begin_command_inset(os, "nomencl_print", "printnomenclature");
+                       // case of a custom width
+                       if (p.hasOpt()) {
+                               width = p.getArg('[', ']');
+                               width = translate_len(width);
+                               width_type = "custom";
+                       }
+                       // case of no custom width
+                       // the case of no custom width but the width set
+                       // via \settowidth{\nomlabelwidth}{***} cannot be supported
+                       // because the user could have set anything, not only the width
+                       // of the longest label (which would be width_type = "auto")
+                       string label = convert_command_inset_arg(p.getArg('{', '}'));
+                       if (label.empty() && width_type.empty())
+                               width_type = "none";
+                       os << "set_width \"" << width_type << "\"\n";
+                       if (width_type == "custom")
+                               os << "width \"" << width << '\"';
                        end_inset(os);
+                       skip_spaces_braces(p);
+                       preamble.registerAutomaticallyLoadedPackage("nomencl");
                }
 
-               else if (is_known(t.cs(), known_latex_commands)) {
-                       // This needs to be after the check for natbib and
-                       // jurabib commands, because "cite" has different
-                       // arguments with natbib and jurabib.
+               else if ((t.cs() == "textsuperscript" || t.cs() == "textsubscript")) {
                        context.check_layout(os);
-                       begin_inset(os, "LatexCommand ");
-                       os << '\\' << t.cs();
-                       // lyx cannot handle newlines in a latex command
-                       // FIXME: Move the substitution into parser::getOpt()?
-                       os << subst(p.getOpt(), "\n", " ");
-                       os << subst(p.getOpt(), "\n", " ");
-                       os << '{' << subst(p.verbatim_item(), "\n", " ") << "}\n";
+                       begin_inset(os, "script ");
+                       os << t.cs().substr(4) << '\n';
+                       parse_text_in_inset(p, os, FLAG_ITEM, false, context);
                        end_inset(os);
+                       if (t.cs() == "textsubscript")
+                               preamble.registerAutomaticallyLoadedPackage("subscript");
                }
 
-               else if (is_known(t.cs(), known_quotes)) {
-                       char const * const * where = is_known(t.cs(), known_quotes);
+               else if ((where = is_known(t.cs(), known_quotes))) {
                        context.check_layout(os);
                        begin_inset(os, "Quotes ");
                        os << known_coded_quotes[where - known_quotes];
@@ -2038,22 +3484,17 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        skip_braces(p);
                }
 
-               else if (is_known(t.cs(), known_sizes) &&
+               else if ((where = is_known(t.cs(), known_sizes)) &&
                         context.new_layout_allowed) {
-                       char const * const * where = is_known(t.cs(), known_sizes);
                        context.check_layout(os);
                        TeXFont const oldFont = context.font;
-                       // the font size index differs by 1, because the known_coded_sizes
-                       // has additionally a "default" entry
-                       context.font.size = known_coded_sizes[where - known_sizes + 1];
+                       context.font.size = known_coded_sizes[where - known_sizes];
                        output_font_change(os, oldFont, context.font);
                        eat_whitespace(p, os, context, false);
                }
 
-               else if (is_known(t.cs(), known_font_families) &&
+               else if ((where = is_known(t.cs(), known_font_families)) &&
                         context.new_layout_allowed) {
-                       char const * const * where =
-                               is_known(t.cs(), known_font_families);
                        context.check_layout(os);
                        TeXFont const oldFont = context.font;
                        context.font.family =
@@ -2062,10 +3503,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        eat_whitespace(p, os, context, false);
                }
 
-               else if (is_known(t.cs(), known_font_series) &&
+               else if ((where = is_known(t.cs(), known_font_series)) &&
                         context.new_layout_allowed) {
-                       char const * const * where =
-                               is_known(t.cs(), known_font_series);
                        context.check_layout(os);
                        TeXFont const oldFont = context.font;
                        context.font.series =
@@ -2074,10 +3513,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        eat_whitespace(p, os, context, false);
                }
 
-               else if (is_known(t.cs(), known_font_shapes) &&
+               else if ((where = is_known(t.cs(), known_font_shapes)) &&
                         context.new_layout_allowed) {
-                       char const * const * where =
-                               is_known(t.cs(), known_font_shapes);
                        context.check_layout(os);
                        TeXFont const oldFont = context.font;
                        context.font.shape =
@@ -2085,10 +3522,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        output_font_change(os, oldFont, context.font);
                        eat_whitespace(p, os, context, false);
                }
-               else if (is_known(t.cs(), known_old_font_families) &&
+               else if ((where = is_known(t.cs(), known_old_font_families)) &&
                         context.new_layout_allowed) {
-                       char const * const * where =
-                               is_known(t.cs(), known_old_font_families);
                        context.check_layout(os);
                        TeXFont const oldFont = context.font;
                        context.font.init();
@@ -2099,10 +3534,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        eat_whitespace(p, os, context, false);
                }
 
-               else if (is_known(t.cs(), known_old_font_series) &&
+               else if ((where = is_known(t.cs(), known_old_font_series)) &&
                         context.new_layout_allowed) {
-                       char const * const * where =
-                               is_known(t.cs(), known_old_font_series);
                        context.check_layout(os);
                        TeXFont const oldFont = context.font;
                        context.font.init();
@@ -2113,10 +3546,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        eat_whitespace(p, os, context, false);
                }
 
-               else if (is_known(t.cs(), known_old_font_shapes) &&
+               else if ((where = is_known(t.cs(), known_old_font_shapes)) &&
                         context.new_layout_allowed) {
-                       char const * const * where =
-                               is_known(t.cs(), known_old_font_shapes);
                        context.check_layout(os);
                        TeXFont const oldFont = context.font;
                        context.font.init();
@@ -2129,53 +3560,74 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
 
                else if (t.cs() == "selectlanguage") {
                        context.check_layout(os);
-                       // save the language for the case that a \foreignlanguage is used 
-                       selectlang = subst(p.verbatim_item(), "\n", " ");
-                       os << "\\lang " << selectlang << "\n";
-                       
+                       // save the language for the case that a
+                       // \foreignlanguage is used
+                       context.font.language = babel2lyx(p.verbatim_item());
+                       os << "\n\\lang " << context.font.language << "\n";
                }
 
                else if (t.cs() == "foreignlanguage") {
-                       context.check_layout(os);
-                       os << "\n\\lang " << subst(p.verbatim_item(), "\n", " ") << "\n";
-                       os << subst(p.verbatim_item(), "\n", " ");
-                       // set back to last selectlanguage
-                       os << "\n\\lang " << selectlang << "\n";
+                       string const lang = babel2lyx(p.verbatim_item());
+                       parse_text_attributes(p, os, FLAG_ITEM, outer,
+                                             context, "\\lang",
+                                             context.font.language, lang);
                }
-
-               else if (t.cs() == "inputencoding")
-                       // write nothing because this is done by LyX using the "\lang"
-                       // information given by selectlanguage and foreignlanguage
-                       subst(p.verbatim_item(), "\n", " ");
                
-               else if (t.cs() == "LyX" || t.cs() == "TeX"
-                        || t.cs() == "LaTeX") {
-                       context.check_layout(os);
-                       os << t.cs();
-                       skip_braces(p); // eat {}
+               else if (is_known(t.cs().substr(4, string::npos), polyglossia_languages)) {
+                       // scheme is \textLANGUAGE{text} where LANGUAGE is in polyglossia_languages[]
+                       string lang;
+                       // We have to output the whole command if it has an option
+                       // because LyX doesn't support this yet, see bug #8214,
+                       // only if there is a single option specifying a variant, we can handle it.
+                       if (p.hasOpt()) {
+                               string langopts = p.getOpt();
+                               // check if the option contains a variant, if yes, extract it
+                               string::size_type pos_var = langopts.find("variant");
+                               string::size_type i = langopts.find(',');
+                               if (pos_var != string::npos){
+                                       string variant;
+                                       if (i == string::npos) {
+                                               variant = langopts.substr(pos_var + 8, langopts.length() - pos_var - 9);
+                                               lang = polyglossia2lyx(variant);
+                                               parse_text_attributes(p, os, FLAG_ITEM, outer,
+                                                                         context, "\\lang",
+                                                                         context.font.language, lang);
+                                       }
+                                       else
+                                               handle_ert(os, t.asInput() + langopts, context);
+                               } else
+                                       handle_ert(os, t.asInput() + langopts, context);
+                       } else {
+                               lang = polyglossia2lyx(t.cs().substr(4, string::npos));
+                               parse_text_attributes(p, os, FLAG_ITEM, outer,
+                                                         context, "\\lang",
+                                                         context.font.language, lang);
+                       }
                }
 
-               else if (t.cs() == "LaTeXe") {
-                       context.check_layout(os);
-                       os << "LaTeX2e";
-                       skip_braces(p); // eat {}
+               else if (t.cs() == "inputencoding") {
+                       // nothing to write here
+                       string const enc = subst(p.verbatim_item(), "\n", " ");
+                       p.setEncoding(enc);
                }
 
-               else if (t.cs() == "ldots") {
+               else if ((where = is_known(t.cs(), known_special_chars))) {
                        context.check_layout(os);
-                       skip_braces(p);
-                       os << "\\SpecialChar \\ldots{}\n";
+                       os << "\\SpecialChar \\"
+                          << known_coded_special_chars[where - known_special_chars]
+                          << '\n';
+                       skip_spaces_braces(p);
                }
 
-               else if (t.cs() == "lyxarrow") {
+               else if (t.cs() == "nobreakdash" && p.next_token().asInput() == "-") {
                        context.check_layout(os);
-                       os << "\\SpecialChar \\menuseparator\n";
-                       skip_braces(p);
+                       os << "\\SpecialChar \\nobreakdash-\n";
+                       p.get_token();
                }
 
-               else if (t.cs() == "textcompwordmark") {
+               else if (t.cs() == "textquotedbl") {
                        context.check_layout(os);
-                       os << "\\SpecialChar \\textcompwordmark{}\n";
+                       os << "\"";
                        skip_braces(p);
                }
 
@@ -2193,19 +3645,19 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                else if (t.cs() == "textasciitilde") {
                        context.check_layout(os);
                        os << '~';
-                       skip_braces(p);
+                       skip_spaces_braces(p);
                }
 
                else if (t.cs() == "textasciicircum") {
                        context.check_layout(os);
                        os << '^';
-                       skip_braces(p);
+                       skip_spaces_braces(p);
                }
 
                else if (t.cs() == "textbackslash") {
                        context.check_layout(os);
                        os << "\n\\backslash\n";
-                       skip_braces(p);
+                       skip_spaces_braces(p);
                }
 
                else if (t.cs() == "_" || t.cs() == "&" || t.cs() == "#"
@@ -2246,23 +3698,28 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                else if (t.cs() == "=" && (flags & FLAG_TABBING))
                        handle_ert(os, t.asInput(), context);
 
-               else if (t.cs() == "H" || t.cs() == "c" || t.cs() == "^"
-                        || t.cs() == "'" || t.cs() == "`"
-                        || t.cs() == "~" || t.cs() == "." || t.cs() == "=") {
-                       // we need the trim as the LyX parser chokes on such spaces
+               // accents (see Table 6 in Comprehensive LaTeX Symbol List)
+               else if (t.cs().size() == 1
+                        && contains("\"'.=^`bcdHkrtuv~", t.cs())) {
                        context.check_layout(os);
                        // try to see whether the string is in unicodesymbols
+                       bool termination;
                        docstring rem;
-                       string command = t.asInput() + "{" 
-                               + trim(p.verbatim_item())
+                       string command = t.asInput() + "{"
+                               + trimSpaceAndEol(p.verbatim_item())
                                + "}";
-                       docstring s = encodings.fromLaTeXCommand(from_utf8(command), rem);
+                       set<string> req;
+                       docstring s = encodings.fromLaTeXCommand(from_utf8(command),
+                               Encodings::TEXT_CMD | Encodings::MATH_CMD,
+                               termination, rem, &req);
                        if (!s.empty()) {
                                if (!rem.empty())
-                                       cerr << "When parsing " << command 
+                                       cerr << "When parsing " << command
                                             << ", result is " << to_utf8(s)
                                             << "+" << to_utf8(rem) << endl;
                                os << to_utf8(s);
+                               for (set<string>::const_iterator it = req.begin(); it != req.end(); ++it)
+                                       preamble.registerAutomaticallyLoadedPackage(*it);
                        } else
                                // we did not find a non-ert version
                                handle_ert(os, command, context);
@@ -2270,39 +3727,41 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
 
                else if (t.cs() == "\\") {
                        context.check_layout(os);
-                       string const next = p.next_token().asInput();
-                       if (next == "[")
+                       if (p.hasOpt())
                                handle_ert(os, "\\\\" + p.getOpt(), context);
-                       else if (next == "*") {
+                       else if (p.next_token().asInput() == "*") {
                                p.get_token();
+                               // getOpt() eats the following space if there
+                               // is no optional argument, but that is OK
+                               // here since it has no effect in the output.
                                handle_ert(os, "\\\\*" + p.getOpt(), context);
                        }
                        else {
-                               os << "\n\\newline\n";
+                               begin_inset(os, "Newline newline");
+                               end_inset(os);
                        }
                }
 
                else if (t.cs() == "newline" ||
-                       t.cs() == "linebreak") {
+                        (t.cs() == "linebreak" && !p.hasOpt())) {
                        context.check_layout(os);
-                       os << "\n\\" << t.cs() << "\n";
-                       skip_braces(p); // eat {}
+                       begin_inset(os, "Newline ");
+                       os << t.cs();
+                       end_inset(os);
+                       skip_spaces_braces(p);
                }
 
                else if (t.cs() == "input" || t.cs() == "include"
                         || t.cs() == "verbatiminput") {
-                       string name = '\\' + t.cs();
+                       string name = t.cs();
                        if (t.cs() == "verbatiminput"
                            && p.next_token().asInput() == "*")
                                name += p.get_token().asInput();
                        context.check_layout(os);
-                       begin_inset(os, "Include ");
                        string filename(normalize_filename(p.getArg('{', '}')));
                        string const path = getMasterFilePath();
                        // We want to preserve relative / absolute filenames,
                        // therefore path is only used for testing
-                       // FIXME UNICODE encoding of filename and path may be
-                       // wrong (makeAbsPath expects utf8)
                        if ((t.cs() == "include" || t.cs() == "input") &&
                            !makeAbsPath(filename, path).exists()) {
                                // The file extension is probably missing.
@@ -2313,65 +3772,209 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                if (!tex_name.empty())
                                        filename = tex_name;
                        }
-                       // FIXME UNICODE encoding of filename and path may be
-                       // wrong (makeAbsPath expects utf8)
+                       bool external = false;
+                       string outname;
                        if (makeAbsPath(filename, path).exists()) {
                                string const abstexname =
-                                       makeAbsPath(filename, path).absFilename();
+                                       makeAbsPath(filename, path).absFileName();
                                string const abslyxname =
                                        changeExtension(abstexname, ".lyx");
+                               string const absfigname =
+                                       changeExtension(abstexname, ".fig");
                                fix_relative_filename(filename);
                                string const lyxname =
                                        changeExtension(filename, ".lyx");
-                               if (t.cs() != "verbatiminput" &&
-                                   tex2lyx(abstexname, FileName(abslyxname))) {
-                                       os << name << '{' << lyxname << "}\n";
+                               bool xfig = false;
+                               external = FileName(absfigname).exists();
+                               if (t.cs() == "input") {
+                                       string const ext = getExtension(abstexname);
+
+                                       // Combined PS/LaTeX:
+                                       // x.eps, x.pstex_t (old xfig)
+                                       // x.pstex, x.pstex_t (new xfig, e.g. 3.2.5)
+                                       FileName const absepsname(
+                                               changeExtension(abstexname, ".eps"));
+                                       FileName const abspstexname(
+                                               changeExtension(abstexname, ".pstex"));
+                                       bool const xfigeps =
+                                               (absepsname.exists() ||
+                                                abspstexname.exists()) &&
+                                               ext == "pstex_t";
+
+                                       // Combined PDF/LaTeX:
+                                       // x.pdf, x.pdftex_t (old xfig)
+                                       // x.pdf, x.pdf_t (new xfig, e.g. 3.2.5)
+                                       FileName const abspdfname(
+                                               changeExtension(abstexname, ".pdf"));
+                                       bool const xfigpdf =
+                                               abspdfname.exists() &&
+                                               (ext == "pdftex_t" || ext == "pdf_t");
+                                       if (xfigpdf)
+                                               pdflatex = true;
+
+                                       // Combined PS/PDF/LaTeX:
+                                       // x_pspdftex.eps, x_pspdftex.pdf, x.pspdftex
+                                       string const absbase2(
+                                               removeExtension(abstexname) + "_pspdftex");
+                                       FileName const abseps2name(
+                                               addExtension(absbase2, ".eps"));
+                                       FileName const abspdf2name(
+                                               addExtension(absbase2, ".pdf"));
+                                       bool const xfigboth =
+                                               abspdf2name.exists() &&
+                                               abseps2name.exists() && ext == "pspdftex";
+
+                                       xfig = xfigpdf || xfigeps || xfigboth;
+                                       external = external && xfig;
+                               }
+                               if (external) {
+                                       outname = changeExtension(filename, ".fig");
+                               } else if (xfig) {
+                                       // Don't try to convert, the result
+                                       // would be full of ERT.
+                                       outname = filename;
+                               } else if (t.cs() != "verbatiminput" &&
+                                   tex2lyx(abstexname, FileName(abslyxname),
+                                           p.getEncoding())) {
+                                       outname = lyxname;
                                } else {
-                                       os << name << '{' << filename << "}\n";
+                                       outname = filename;
                                }
                        } else {
                                cerr << "Warning: Could not find included file '"
                                     << filename << "'." << endl;
-                               os << name << '{' << filename << "}\n";
+                               outname = filename;
+                       }
+                       if (external) {
+                               begin_inset(os, "External\n");
+                               os << "\ttemplate XFig\n"
+                                  << "\tfilename " << outname << '\n';
+                               registerExternalTemplatePackages("XFig");
+                       } else {
+                               begin_command_inset(os, "include", name);
+                               os << "preview false\n"
+                                     "filename \"" << outname << "\"\n";
+                               if (t.cs() == "verbatiminput")
+                                       preamble.registerAutomaticallyLoadedPackage("verbatim");
                        }
-                       os << "preview false\n";
                        end_inset(os);
                }
 
                else if (t.cs() == "bibliographystyle") {
                        // store new bibliographystyle
                        bibliographystyle = p.verbatim_item();
-                       // output new bibliographystyle.
-                       // This is only necessary if used in some other macro than \bibliography.
-                       handle_ert(os, "\\bibliographystyle{" + bibliographystyle + "}", context);
+                       // If any other command than \bibliography and
+                       // \nocite{*} follows, we need to output the style
+                       // (because it might be used by that command).
+                       // Otherwise, it will automatically be output by LyX.
+                       p.pushPosition();
+                       bool output = true;
+                       for (Token t2 = p.get_token(); p.good(); t2 = p.get_token()) {
+                               if (t2.cat() == catBegin)
+                                       break;
+                               if (t2.cat() != catEscape)
+                                       continue;
+                               if (t2.cs() == "nocite") {
+                                       if (p.getArg('{', '}') == "*")
+                                               continue;
+                               } else if (t2.cs() == "bibliography")
+                                       output = false;
+                               break;
+                       }
+                       p.popPosition();
+                       if (output) {
+                               handle_ert(os,
+                                       "\\bibliographystyle{" + bibliographystyle + '}',
+                                       context);
+                       }
                }
 
                else if (t.cs() == "bibliography") {
                        context.check_layout(os);
-                       begin_inset(os, "LatexCommand ");
-                       os << "\\bibtex";
-                       // Do we have a bibliographystyle set?
-                       if (!bibliographystyle.empty()) {
-                               os << '[' << bibliographystyle << ']';
+                       begin_command_inset(os, "bibtex", "bibtex");
+                       if (!btprint.empty()) {
+                               os << "btprint " << '"' << "btPrintAll" << '"' << "\n";
+                               // clear the string because the next BibTeX inset can be without the
+                               // \nocite{*} option
+                               btprint.clear();
                        }
-                       os << '{' << p.verbatim_item() << "}\n";
+                       os << "bibfiles " << '"' << p.verbatim_item() << '"' << "\n";
+                       // Do we have a bibliographystyle set?
+                       if (!bibliographystyle.empty())
+                               os << "options " << '"' << bibliographystyle << '"' << "\n";
                        end_inset(os);
                }
 
-               else if (t.cs() == "parbox")
-                       parse_box(p, os, FLAG_ITEM, outer, context, true);
-               
+               else if (t.cs() == "parbox") {
+                       // Test whether this is an outer box of a shaded box
+                       p.pushPosition();
+                       // swallow arguments
+                       while (p.hasOpt()) {
+                               p.getArg('[', ']');
+                               p.skip_spaces(true);
+                       }
+                       p.getArg('{', '}');
+                       p.skip_spaces(true);
+                       // eat the '{'
+                       if (p.next_token().cat() == catBegin)
+                               p.get_token();
+                       p.skip_spaces(true);
+                       Token to = p.get_token();
+                       bool shaded = false;
+                       if (to.asInput() == "\\begin") {
+                               p.skip_spaces(true);
+                               if (p.getArg('{', '}') == "shaded")
+                                       shaded = true;
+                       }
+                       p.popPosition();
+                       if (shaded) {
+                               parse_outer_box(p, os, FLAG_ITEM, outer,
+                                               context, "parbox", "shaded");
+                       } else
+                               parse_box(p, os, 0, FLAG_ITEM, outer, context,
+                                         "", "", t.cs());
+               }
+
+               else if (t.cs() == "ovalbox" || t.cs() == "Ovalbox" ||
+                        t.cs() == "shadowbox" || t.cs() == "doublebox")
+                       parse_outer_box(p, os, FLAG_ITEM, outer, context, t.cs(), "");
+
+               else if (t.cs() == "framebox") {
+                       if (p.next_token().character() == '(') {
+                               //the syntax is: \framebox(x,y)[position]{content}
+                               string arg = t.asInput();
+                               arg += p.getFullParentheseArg();
+                               arg += p.getFullOpt();
+                               eat_whitespace(p, os, context, false);
+                               handle_ert(os, arg + '{', context);
+                               eat_whitespace(p, os, context, false);
+                               parse_text(p, os, FLAG_ITEM, outer, context);
+                               handle_ert(os, "}", context);
+                       } else {
+                               string special = p.getFullOpt();
+                               special += p.getOpt();
+                               parse_outer_box(p, os, FLAG_ITEM, outer,
+                                               context, t.cs(), special);
+                       }
+               }
+
                //\makebox() is part of the picture environment and different from \makebox{}
-               //\makebox{} will be parsed by parse_box when bug 2956 is fixed
+               //\makebox{} will be parsed by parse_box
                else if (t.cs() == "makebox") {
-                       string arg = t.asInput();
-                       if (p.next_token().character() == '(')
+                       if (p.next_token().character() == '(') {
                                //the syntax is: \makebox(x,y)[position]{content}
+                               string arg = t.asInput();
                                arg += p.getFullParentheseArg();
-                       else
-                               //the syntax is: \makebox[width][position]{content}
                                arg += p.getFullOpt();
-                       handle_ert(os, arg + p.getFullOpt(), context);
+                               eat_whitespace(p, os, context, false);
+                               handle_ert(os, arg + '{', context);
+                               eat_whitespace(p, os, context, false);
+                               parse_text(p, os, FLAG_ITEM, outer, context);
+                               handle_ert(os, "}", context);
+                       } else
+                               //the syntax is: \makebox[width][position]{content}
+                               parse_box(p, os, 0, FLAG_ITEM, outer, context,
+                                         "", "", t.cs());
                }
 
                else if (t.cs() == "smallskip" ||
@@ -2382,15 +3985,15 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        begin_inset(os, "VSpace ");
                        os << t.cs();
                        end_inset(os);
-                       skip_braces(p);
+                       skip_spaces_braces(p);
                }
 
-               else if (is_known(t.cs(), known_spaces)) {
-                       char const * const * where = is_known(t.cs(), known_spaces);
+               else if ((where = is_known(t.cs(), known_spaces))) {
                        context.check_layout(os);
-                       os << "\\InsetSpace ";
+                       begin_inset(os, "space ");
                        os << '\\' << known_coded_spaces[where - known_spaces]
                           << '\n';
+                       end_inset(os);
                        // LaTeX swallows whitespace after all spaces except
                        // "\\,". We have to do that here, too, because LyX
                        // adds "{}" which would make the spaces significant.
@@ -2405,19 +4008,28 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                }
 
                else if (t.cs() == "newpage" ||
-                       t.cs() == "pagebreak" ||
-                       t.cs() == "clearpage" ||
-                       t.cs() == "cleardoublepage") {
+                        (t.cs() == "pagebreak" && !p.hasOpt()) ||
+                        t.cs() == "clearpage" ||
+                        t.cs() == "cleardoublepage") {
                        context.check_layout(os);
-                       os << "\n\\" << t.cs() << "\n";
-                       skip_braces(p); // eat {}
+                       begin_inset(os, "Newpage ");
+                       os << t.cs();
+                       end_inset(os);
+                       skip_spaces_braces(p);
                }
 
-               else if (t.cs() == "newcommand" ||
-                        t.cs() == "providecommand" ||
-                        t.cs() == "renewcommand") {
-                       // these could be handled by parse_command(), but
-                       // we need to call add_known_command() here.
+               else if (t.cs() == "DeclareRobustCommand" ||
+                        t.cs() == "DeclareRobustCommandx" ||
+                        t.cs() == "newcommand" ||
+                        t.cs() == "newcommandx" ||
+                        t.cs() == "providecommand" ||
+                        t.cs() == "providecommandx" ||
+                        t.cs() == "renewcommand" ||
+                        t.cs() == "renewcommandx") {
+                       // DeclareRobustCommand, DeclareRobustCommandx,
+                       // providecommand and providecommandx could be handled
+                       // by parse_command(), but we need to call
+                       // add_known_command() here.
                        string name = t.asInput();
                        if (p.next_token().asInput() == "*") {
                                // Starred form. Eat '*'
@@ -2425,29 +4037,70 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                name += '*';
                        }
                        string const command = p.verbatim_item();
-                       string const opt1 = p.getOpt();
+                       string const opt1 = p.getFullOpt();
                        string const opt2 = p.getFullOpt();
                        add_known_command(command, opt1, !opt2.empty());
                        string const ert = name + '{' + command + '}' +
                                           opt1 + opt2 +
                                           '{' + p.verbatim_item() + '}';
 
-                       context.check_layout(os);
-                       begin_inset(os, "FormulaMacro");
-                       os << "\n" << ert;
-                       end_inset(os);
+                       if (t.cs() == "DeclareRobustCommand" ||
+                           t.cs() == "DeclareRobustCommandx" ||
+                           t.cs() == "providecommand" ||
+                           t.cs() == "providecommandx" ||
+                           name[name.length()-1] == '*')
+                               handle_ert(os, ert, context);
+                       else {
+                               context.check_layout(os);
+                               begin_inset(os, "FormulaMacro");
+                               os << "\n" << ert;
+                               end_inset(os);
+                       }
                }
-               
-               else if (t.cs() == "vspace") {
+
+               else if (t.cs() == "let" && p.next_token().asInput() != "*") {
+                       // let could be handled by parse_command(),
+                       // but we need to call add_known_command() here.
+                       string ert = t.asInput();
+                       string name;
+                       p.skip_spaces();
+                       if (p.next_token().cat() == catBegin) {
+                               name = p.verbatim_item();
+                               ert += '{' + name + '}';
+                       } else {
+                               name = p.verbatim_item();
+                               ert += name;
+                       }
+                       string command;
+                       p.skip_spaces();
+                       if (p.next_token().cat() == catBegin) {
+                               command = p.verbatim_item();
+                               ert += '{' + command + '}';
+                       } else {
+                               command = p.verbatim_item();
+                               ert += command;
+                       }
+                       // If command is known, make name known too, to parse
+                       // its arguments correctly. For this reason we also
+                       // have commands in syntax.default that are hardcoded.
+                       CommandMap::iterator it = known_commands.find(command);
+                       if (it != known_commands.end())
+                               known_commands[t.asInput()] = it->second;
+                       handle_ert(os, ert, context);
+               }
+
+               else if (t.cs() == "hspace" || t.cs() == "vspace") {
                        bool starred = false;
                        if (p.next_token().asInput() == "*") {
                                p.get_token();
                                starred = true;
                        }
+                       string name = t.asInput();
                        string const length = p.verbatim_item();
                        string unit;
                        string valstring;
                        bool valid = splitLatexLength(length, valstring, unit);
+                       bool known_hspace = false;
                        bool known_vspace = false;
                        bool known_unit = false;
                        double value;
@@ -2455,21 +4108,31 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                istringstream iss(valstring);
                                iss >> value;
                                if (value == 1.0) {
-                                       if (unit == "\\smallskipamount") {
-                                               unit = "smallskip";
-                                               known_vspace = true;
-                                       } else if (unit == "\\medskipamount") {
-                                               unit = "medskip";
-                                               known_vspace = true;
-                                       } else if (unit == "\\bigskipamount") {
-                                               unit = "bigskip";
-                                               known_vspace = true;
-                                       } else if (unit == "\\fill") {
-                                               unit = "vfill";
-                                               known_vspace = true;
+                                       if (t.cs()[0] == 'h') {
+                                               if (unit == "\\fill") {
+                                                       if (!starred) {
+                                                               unit = "";
+                                                               name = "\\hfill";
+                                                       }
+                                                       known_hspace = true;
+                                               }
+                                       } else {
+                                               if (unit == "\\smallskipamount") {
+                                                       unit = "smallskip";
+                                                       known_vspace = true;
+                                               } else if (unit == "\\medskipamount") {
+                                                       unit = "medskip";
+                                                       known_vspace = true;
+                                               } else if (unit == "\\bigskipamount") {
+                                                       unit = "bigskip";
+                                                       known_vspace = true;
+                                               } else if (unit == "\\fill") {
+                                                       unit = "vfill";
+                                                       known_vspace = true;
+                                               }
                                        }
                                }
-                               if (!known_vspace) {
+                               if (!known_hspace && !known_vspace) {
                                        switch (unitFromString(unit)) {
                                        case Length::SP:
                                        case Length::PT:
@@ -2491,8 +4154,23 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                }
                        }
 
-                       if (known_unit || known_vspace) {
-                               // Literal length or known variable
+                       if (t.cs()[0] == 'h' && (known_unit || known_hspace)) {
+                               // Literal horizontal length or known variable
+                               context.check_layout(os);
+                               begin_inset(os, "space ");
+                               os << name;
+                               if (starred)
+                                       os << '*';
+                               os << '{';
+                               if (known_hspace)
+                                       os << unit;
+                               os << "}";
+                               if (known_unit && !known_hspace)
+                                       os << "\n\\length "
+                                          << translate_len(length);
+                               end_inset(os);
+                       } else if (known_unit || known_vspace) {
+                               // Literal vertical length or known variable
                                context.check_layout(os);
                                begin_inset(os, "VSpace ");
                                if (known_unit)
@@ -2502,8 +4180,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                        os << '*';
                                end_inset(os);
                        } else {
-                               // LyX can't handle other length variables in Inset VSpace
-                               string name = t.asInput();
+                               // LyX can't handle other length variables in Inset VSpace/space
                                if (starred)
                                        name += '*';
                                if (valid) {
@@ -2518,18 +4195,138 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        }
                }
 
+               // The single '=' is meant here.
+               else if ((newinsetlayout = findInsetLayout(context.textclass, t.cs(), true))) {
+                       p.skip_spaces();
+                       context.check_layout(os);
+                       begin_inset(os, "Flex ");
+                       os << to_utf8(newinsetlayout->name()) << '\n'
+                          << "status collapsed\n";
+                       parse_text_in_inset(p, os, FLAG_ITEM, false, context, newinsetlayout);
+                       end_inset(os);
+               }
+
+               else if (t.cs() == "includepdf") {
+                       p.skip_spaces();
+                       string const arg = p.getArg('[', ']');
+                       map<string, string> opts;
+                       vector<string> keys;
+                       split_map(arg, opts, keys);
+                       string name = normalize_filename(p.verbatim_item());
+                       string const path = getMasterFilePath();
+                       // We want to preserve relative / absolute filenames,
+                       // therefore path is only used for testing
+                       if (!makeAbsPath(name, path).exists()) {
+                               // The file extension is probably missing.
+                               // Now try to find it out.
+                               char const * const pdfpages_format[] = {"pdf", 0};
+                               string const pdftex_name =
+                                       find_file(name, path, pdfpages_format);
+                               if (!pdftex_name.empty()) {
+                                       name = pdftex_name;
+                                       pdflatex = true;
+                               }
+                       }
+                       if (makeAbsPath(name, path).exists())
+                               fix_relative_filename(name);
+                       else
+                               cerr << "Warning: Could not find file '"
+                                    << name << "'." << endl;
+                       // write output
+                       context.check_layout(os);
+                       begin_inset(os, "External\n\ttemplate ");
+                       os << "PDFPages\n\tfilename "
+                          << name << "\n";
+                       // parse the options
+                       if (opts.find("pages") != opts.end())
+                               os << "\textra LaTeX \"pages="
+                                  << opts["pages"] << "\"\n";
+                       if (opts.find("angle") != opts.end())
+                               os << "\trotateAngle "
+                                  << opts["angle"] << '\n';
+                       if (opts.find("origin") != opts.end()) {
+                               ostringstream ss;
+                               string const opt = opts["origin"];
+                               if (opt == "tl") ss << "topleft";
+                               if (opt == "bl") ss << "bottomleft";
+                               if (opt == "Bl") ss << "baselineleft";
+                               if (opt == "c") ss << "center";
+                               if (opt == "tc") ss << "topcenter";
+                               if (opt == "bc") ss << "bottomcenter";
+                               if (opt == "Bc") ss << "baselinecenter";
+                               if (opt == "tr") ss << "topright";
+                               if (opt == "br") ss << "bottomright";
+                               if (opt == "Br") ss << "baselineright";
+                               if (!ss.str().empty())
+                                       os << "\trotateOrigin " << ss.str() << '\n';
+                               else
+                                       cerr << "Warning: Ignoring unknown includegraphics origin argument '" << opt << "'\n";
+                       }
+                       if (opts.find("width") != opts.end())
+                               os << "\twidth "
+                                  << translate_len(opts["width"]) << '\n';
+                       if (opts.find("height") != opts.end())
+                               os << "\theight "
+                                  << translate_len(opts["height"]) << '\n';
+                       if (opts.find("keepaspectratio") != opts.end())
+                               os << "\tkeepAspectRatio\n";
+                       end_inset(os);
+                       context.check_layout(os);
+                       registerExternalTemplatePackages("PDFPages");
+               }
+
+               else if (t.cs() == "loadgame") {
+                       p.skip_spaces();
+                       string name = normalize_filename(p.verbatim_item());
+                       string const path = getMasterFilePath();
+                       // We want to preserve relative / absolute filenames,
+                       // therefore path is only used for testing
+                       if (!makeAbsPath(name, path).exists()) {
+                               // The file extension is probably missing.
+                               // Now try to find it out.
+                               char const * const lyxskak_format[] = {"fen", 0};
+                               string const lyxskak_name =
+                                       find_file(name, path, lyxskak_format);
+                               if (!lyxskak_name.empty())
+                                       name = lyxskak_name;
+                       }
+                       if (makeAbsPath(name, path).exists())
+                               fix_relative_filename(name);
+                       else
+                               cerr << "Warning: Could not find file '"
+                                    << name << "'." << endl;
+                       context.check_layout(os);
+                       begin_inset(os, "External\n\ttemplate ");
+                       os << "ChessDiagram\n\tfilename "
+                          << name << "\n";
+                       end_inset(os);
+                       context.check_layout(os);
+                       // after a \loadgame follows a \showboard
+                       if (p.get_token().asInput() == "showboard")
+                               p.get_token();
+                       registerExternalTemplatePackages("ChessDiagram");
+               }
+
                else {
                        // try to see whether the string is in unicodesymbols
+                       // Only use text mode commands, since we are in text mode here,
+                       // and math commands may be invalid (bug 6797)
+                       bool termination;
                        docstring rem;
-                       docstring s = encodings.fromLaTeXCommand(from_utf8(t.asInput()), rem);
+                       set<string> req;
+                       docstring s = encodings.fromLaTeXCommand(from_utf8(t.asInput()),
+                                       Encodings::TEXT_CMD, termination, rem, &req);
                        if (!s.empty()) {
                                if (!rem.empty())
-                                       cerr << "When parsing " << t.cs() 
+                                       cerr << "When parsing " << t.cs()
                                             << ", result is " << to_utf8(s)
                                             << "+" << to_utf8(rem) << endl;
                                context.check_layout(os);
                                os << to_utf8(s);
-                               skip_braces(p); // eat {}
+                               if (termination)
+                                       skip_spaces_braces(p);
+                               for (set<string>::const_iterator it = req.begin(); it != req.end(); ++it)
+                                       preamble.registerAutomaticallyLoadedPackage(*it);
                        }
                        //cerr << "#: " << t << " mode: " << mode << endl;
                        // heuristic: read up to next non-nested space