]> git.lyx.org Git - lyx.git/blobdiff - src/tex2lyx/text.cpp
Fix bug #4213: Change tracking support for tex2lyx.
[lyx.git] / src / tex2lyx / text.cpp
index 346922fc2c03703283bc3067e97b2b5440903b67..6d41e1be715b029271a3bd76601ca2205764d85b 100644 (file)
 #include "Context.h"
 #include "Encoding.h"
 #include "FloatList.h"
+#include "LaTeXPackages.h"
 #include "Layout.h"
 #include "Length.h"
+#include "Preamble.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>
@@ -109,6 +112,9 @@ string parse_text_snippet(Parser & p, unsigned flags, const bool outer,
 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 };
+
 /*!
  * natbib commands.
  * The starred forms are also known except for "citefullauthor",
@@ -207,15 +213,18 @@ 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", "hfill", "dotfill", "hrulefill",
-"leftarrowfill", "rightarrowfill", "upbracefill", "downbracefill", 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{}", "hfill{}", "dotfill{}", "hrulefill{}", "leftarrowfill{}",
-"rightarrowfill{}", "upbracefill{}", "downbracefill{}", 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"!
@@ -223,6 +232,9 @@ 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 and an ordered keyword vector
 void split_map(string const & s, map<string, string> & res, vector<string> & keys)
@@ -233,8 +245,8 @@ void split_map(string const & s, map<string, string> & res, vector<string> & key
        keys.resize(v.size());
        for (size_t i = 0; i < v.size(); ++i) {
                size_t const pos   = v[i].find('=');
-               string const index = trim(v[i].substr(0, pos));
-               string const value = trim(v[i].substr(pos + 1, string::npos));
+               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;
        }
@@ -265,15 +277,15 @@ 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;
 }
 
@@ -577,7 +589,7 @@ void output_command_layout(ostream & os, Parser & p, bool outer,
                    p.next_token().character() != '[') 
                        break;
                p.get_token(); // eat '['
-               begin_inset(os, "OptArg\n");
+               begin_inset(os, "Argument\n");
                os << "status collapsed\n\n";
                parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
                end_inset(os);
@@ -585,12 +597,12 @@ void output_command_layout(ostream & os, Parser & p, bool outer,
                ++optargs;
        }
        unsigned int reqargs = 0;
-       while (LYX_FORMAT >= 392 && reqargs < context.layout->reqargs) {
+       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, "OptArg\n");
+               begin_inset(os, "Argument\n");
                os << "status collapsed\n\n";
                parse_text_in_inset(p, os, FLAG_BRACE_LAST, outer, context);
                end_inset(os);
@@ -683,7 +695,7 @@ void parse_arguments(string const & command,
                        break;
                case optional:
                        // true because we must not eat whitespace
-                       // if an optional arg follows me must not strip the
+                       // if an optional arg follows we must not strip the
                        // brackets from this one
                        if (i < no_arguments - 1 &&
                            template_arguments[i+1] == optional)
@@ -775,6 +787,23 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
        } else
                latex_width = p.verbatim_item();
        translate_len(latex_width, width_value, width_unit);
+       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()) {
@@ -787,8 +816,9 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                else
                        p.verbatim_item();
                p.skip_spaces(true);
-               if ((outer_type == "framed" && p.next_token().asInput() != "\\end") ||
-                   (outer_type != "framed" && p.next_token().cat() != catEnd)) {
+               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;
@@ -807,10 +837,12 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                        }
                }
                if (!inner_type.empty()) {
-                       if (inner_flags & FLAG_END)
-                               ss << "\\begin{" << inner_type << '}';
-                       else
-                               ss << '\\' << inner_type;
+                       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())
@@ -821,6 +853,8 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                        if (!(inner_flags & FLAG_END))
                                ss << '{';
                }
+               if (inner_type == "shaded")
+                       ss << "\\begin{shaded}";
                handle_ert(os, ss.str(), parent_context);
                if (!inner_type.empty()) {
                        parse_text(p, os, inner_flags, outer, parent_context);
@@ -831,6 +865,10 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                                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();
                        parse_text(p, os, outer_flags, outer, parent_context);
                        if (outer_flags & FLAG_END)
                                handle_ert(os, "\\end{" + outer_type + '}',
@@ -845,6 +883,8 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                        position = "c";
                if (inner_pos.empty())
                        inner_pos = position;
+               // FIXME: Support makebox
+               bool const use_makebox = false;
                parent_context.check_layout(os);
                begin_inset(os, "Box ");
                if (outer_type == "framed")
@@ -853,9 +893,12 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                        os << "Boxed\n";
                else if (outer_type == "shadowbox")
                        os << "Shadowbox\n";
-               else if (outer_type == "shaded")
+               else if ((outer_type == "shaded" && inner_type.empty()) ||
+                            (outer_type == "minipage" && inner_type == "shaded") ||
+                            (outer_type == "parbox" && inner_type == "shaded")) {
                        os << "Shaded\n";
-               else if (outer_type == "doublebox")
+                       preamble.registerAutomaticallyLoadedPackage("color");
+               } else if (outer_type == "doublebox")
                        os << "Doublebox\n";
                else if (outer_type.empty())
                        os << "Frameless\n";
@@ -865,16 +908,30 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                os << "hor_pos \"" << hor_pos << "\"\n";
                os << "has_inner_box " << !inner_type.empty() << "\n";
                os << "inner_pos \"" << inner_pos << "\"\n";
-               os << "use_parbox " << (inner_type == "parbox") << '\n';
+               os << "use_parbox " << (inner_type == "parbox" || shadedparbox)
+                       << '\n';
+               os << "use_makebox " << use_makebox << '\n';
                os << "width \"" << width_value << width_unit << "\"\n";
                os << "special \"none\"\n";
                os << "height \"" << height_value << height_unit << "\"\n";
                os << "height_special \"" << height_special << "\"\n";
                os << "status open\n\n";
+
+               // 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() || use_makebox) &&
+                       outer_type != "shaded" && outer_type != "framed";
                Context context(true, parent_context.textclass);
-               context.font = parent_context.font;
+               if (forcePlainLayout)
+                       context.layout = &context.textclass.plainLayout();
+               else
+                       context.font = parent_context.font;
 
-               // If we have no inner box the contens will be read with the outer box
+               // 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);
 
@@ -888,6 +945,10 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                // 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);
@@ -935,6 +996,22 @@ void parse_outer_box(Parser & p, ostream & os, unsigned flags, bool outer,
        }
        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();
+                       eat_whitespace(p, os, parent_context, false);
+               }
+       }
        if (outer_type == "shaded") {
                // These boxes never have an inner box
                ;
@@ -942,25 +1019,31 @@ void parse_outer_box(Parser & p, ostream & os, unsigned flags, bool outer,
                inner = p.get_token().cs();
                inner_flags = FLAG_ITEM;
        } else if (p.next_token().asInput() == "\\begin") {
-               // Is this a minipage?
+               // Is this a minipage or shaded box?
                p.pushPosition();
                p.get_token();
                inner = p.getArg('{', '}');
                p.popPosition();
-               if (inner == "minipage") {
-                       p.get_token();
-                       p.getArg('{', '}');
-                       eat_whitespace(p, os, parent_context, false);
+               if (inner == "minipage" || inner == "shaded")
                        inner_flags = FLAG_END;
-               else
+               else
                        inner = "";
        }
+       p.popPosition();
        if (inner_flags == FLAG_END) {
-               active_environments.push_back(inner);
+               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);
-               active_environments.pop_back();
        } 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);
        }
@@ -1022,7 +1105,8 @@ void parse_unknown_environment(Parser & p, string const & name, ostream & os,
 
 
 void parse_environment(Parser & p, ostream & os, bool outer,
-                       string & last_env, Context & parent_context)
+                       string & last_env, bool & title_layout_found,
+                       Context & parent_context)
 {
        Layout const * newlayout;
        InsetLayout const * newinsetlayout = 0;
@@ -1053,6 +1137,14 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                eat_whitespace(p, os, parent_context, false);
                parent_context.check_layout(os);
                begin_inset(os, "Float " + unstarred_name + "\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 (p.hasOpt())
                        os << "placement " << p.getArg('[', ']') << '\n';
                os << "wide " << convert<string>(is_starred)
@@ -1064,11 +1156,86 @@ 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();
+       }
+
+       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();
        }
 
        else if (name == "minipage") {
                eat_whitespace(p, os, parent_context, false);
-               parse_box(p, os, 0, FLAG_END, outer, parent_context, "", "", name);
+               // 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();
        }
 
@@ -1091,6 +1258,8 @@ 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" || name == "shaded") {
@@ -1102,6 +1271,8 @@ void parse_environment(Parser & p, ostream & os, bool outer,
        else if (name == "lstlisting") {
                eat_whitespace(p, os, parent_context, false);
                // FIXME handle listings with parameters
+               //       If this is added, don't forgot to handle the
+               //       automatic color package loading
                if (p.hasOpt())
                        parse_unknown_environment(p, name, os, FLAG_END,
                                                  outer, parent_context);
@@ -1201,6 +1372,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) {
@@ -1211,6 +1425,8 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                context.check_end_deeper(os);
                parent_context.new_paragraph(os);
                p.skip_spaces();
+               if (!title_layout_found)
+                       title_layout_found = newlayout->intitle;
        }
 
        // The single '=' is meant here.
@@ -1448,8 +1664,10 @@ void parse_noweb(Parser & p, ostream & os, Context & context)
                        os << subst(t.asInput(), "\\", "\n\\backslash\n");
                else {
                        ostringstream oss;
-                       begin_inset(oss, "Newline newline");
-                       end_inset(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.
@@ -1595,9 +1813,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
        // (needed for bibtex inset)
        string btprint;
        string bibliographystyle;
-       bool const use_natbib = used_packages.find("natbib") != used_packages.end();
-       bool const use_jurabib = used_packages.find("jurabib") != used_packages.end();
+       bool const use_natbib = preamble.isPackageUsed("natbib");
+       bool const use_jurabib = preamble.isPackageUsed("jurabib");
        string last_env;
+       bool title_layout_found = false;
        while (p.good()) {
                Token const & t = p.get_token();
 
@@ -1904,7 +2123,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                }
 
                else if (t.cs() == "begin")
-                       parse_environment(p, os, outer, last_env, context);
+                       parse_environment(p, os, outer, last_env,
+                                         title_layout_found, context);
 
                else if (t.cs() == "end") {
                        if (flags & FLAG_END) {
@@ -2005,6 +2225,28 @@ 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") {
+                       string const date = p.verbatim_item();
+                       if (date.empty())
+                               preamble.suppressDate(true);
+                       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);
+                                       p.skip_spaces();
+                                       if (!title_layout_found)
+                                               title_layout_found = newlayout->intitle;
+                               } else
+                                       handle_ert(os, "\\date{" + date + '}',
+                                                       context);
+                       }
+               }
+
                // Starred section headings
                // Must attempt to parse "Section*" before "Section".
                else if ((p.next_token().asInput() == "*") &&
@@ -2014,6 +2256,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        p.get_token();
                        output_command_layout(os, p, outer, context, newlayout);
                        p.skip_spaces();
+                       if (!title_layout_found)
+                               title_layout_found = newlayout->intitle;
                }
 
                // Section headings and the like
@@ -2022,20 +2266,22 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        // write the layout
                        output_command_layout(os, p, outer, context, newlayout);
                        p.skip_spaces();
+                       if (!title_layout_found)
+                               title_layout_found = newlayout->intitle;
                }
 
                else if (t.cs() == "caption") {
                        p.skip_spaces();
                        context.check_layout(os);
                        p.skip_spaces();
-                       begin_inset(os, "Caption\n\n");
+                       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, "OptArg\n");
+                               begin_inset(os, "Argument\n");
                                os << "status collapsed\n";
                                parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
                                end_inset(os);
@@ -2051,6 +2297,68 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        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)
@@ -2242,10 +2550,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                }
 
                else if (t.cs() == "makeindex" || t.cs() == "maketitle") {
-                       // FIXME: Somehow prevent title layouts if
-                       // "maketitle" was not found
-                       // swallow this
-                       skip_spaces_braces(p);
+                       if (title_layout_found) {
+                               // swallow this
+                               skip_spaces_braces(p);
+                       } else
+                               handle_ert(os, t.asInput(), context);
                }
 
                else if (t.cs() == "tableofcontents") {
@@ -2355,12 +2664,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" || 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.
@@ -2369,19 +2682,150 @@ 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() == "emph" || t.cs() == "noun") {
+               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() == "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) ||
@@ -2402,7 +2846,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        string const opt = p.getOpt();
                        if (opt.empty()) {
                                context.check_layout(os);
-                               begin_command_inset(os, "ref", t.cs());
+                               char const * const * where = is_known(t.cs(),
+                                       known_ref_commands);
+                               begin_command_inset(os, "ref",
+                                       known_coded_ref_commands[where - known_ref_commands]);
                                os << "reference \""
                                   << convert_command_inset_arg(p.verbatim_item())
                                   << "\"\n";
@@ -2483,7 +2930,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                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';
@@ -2540,7 +2988,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
 
                else if (t.cs() == "index") {
                        context.check_layout(os);
-                       begin_inset(os, "Index\n");
+                       begin_inset(os, "Index idx\n");
                        os << "status collapsed\n";
                        parse_text_in_inset(p, os, FLAG_ITEM, false, context, "Index");
                        end_inset(os);
@@ -2572,24 +3020,45 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                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);
                }
 
                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);
                }
 
-               else if (LYX_FORMAT >= 408 &&
-                        (t.cs() == "textsuperscript" || t.cs() == "textsubscript")) {
+               else if ((t.cs() == "textsuperscript" || t.cs() == "textsubscript")) {
                        context.check_layout(os);
                        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)) {
@@ -2824,7 +3293,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        // try to see whether the string is in unicodesymbols
                        docstring rem;
                        string command = t.asInput() + "{" 
-                               + trim(p.verbatim_item())
+                               + trimSpaceAndEol(p.verbatim_item())
                                + "}";
                        docstring s = encodings.fromLaTeXCommand(from_utf8(command), rem);
                        if (!s.empty()) {
@@ -3015,8 +3484,35 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        end_inset(os);
                }
 
-               else if (t.cs() == "parbox")
-                       parse_box(p, os, 0, FLAG_ITEM, outer, context, "", "", t.cs());
+               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")