+ // This is only attempted at landscape environments that consist only
+ // of a longtable (this is how longtables in LyX are rotated by 90 degs).
+ // Other landscape environment is handled via the landscape module, thus
+ // we will fall through in that case.
+ if (name == "landscape") {
+ // We check if the next thing is a longtable
+ p.pushPosition();
+ bool found_end = false;
+ bool only_longtable = false;
+ bool end_longtable = false;
+ p.get_token();
+ p.get_token();
+ string envname = p.getArg('{', '}');
+ if (envname == "longtable" || envname == "xltabular") {
+ // Now we check if the longtable is the only content
+ // of the landscape environment
+ string const ltenv = envname;
+ while (!found_end && !end_longtable && p.good()) {
+ envname = p.next_token().cat() == catBegin
+ ? p.getArg('{', '}') : string();
+ Token const & t = p.get_token();
+ p.skip_spaces();
+ end_longtable = t.asInput() != "\\end"
+ && envname == ltenv;
+ found_end = t.asInput() == "\\end"
+ && envname == "landscape";
+ }
+ if (end_longtable) {
+ p.get_token();
+ envname = p.getArg('{', '}');
+ only_longtable = p.next_next_token().asInput() == "\\end"
+ && envname == "landscape";
+ }
+ if (only_longtable) {
+ p.popPosition();
+ p.skip_spaces();
+ int const save_tablerotation = parent_context.tablerotation;
+ parent_context.tablerotation = 90;
+ parse_text(p, os, FLAG_END, outer, parent_context);
+ parent_context.tablerotation = save_tablerotation;
+ p.skip_spaces();
+ break;
+ }
+ // fall through
+ }
+ // fall through
+ p.popPosition();
+ }
+
+ if (name == "framed" || name == "shaded") {
+ eat_whitespace(p, os, parent_context, false);
+ parse_outer_box(p, os, FLAG_END, outer, parent_context, name, "");
+ p.skip_spaces();
+ preamble.registerAutomaticallyLoadedPackage("framed");
+ break;
+ }
+
+ if (name == "listing") {
+ minted_float = "float";
+ eat_whitespace(p, os, parent_context, false);
+ string const opt = p.hasOpt() ? p.getArg('[', ']') : string();
+ if (!opt.empty())
+ minted_float += "=" + opt;
+ // If something precedes \begin{minted}, we output it at the end
+ // as a caption, in order to keep it inside the listings inset.
+ eat_whitespace(p, os, parent_context, true);
+ p.pushPosition();
+ Token const & t = p.get_token();
+ p.skip_spaces(true);
+ string const envname = p.next_token().cat() == catBegin
+ ? p.getArg('{', '}') : string();
+ bool prologue = t.asInput() != "\\begin" || envname != "minted";
+ p.popPosition();
+ minted_float_has_caption = false;
+ string content = parse_text_snippet(p, FLAG_END, outer,
+ parent_context);
+ size_t i = content.find("\\begin_inset listings");
+ bool minted_env = i != string::npos;
+ string caption;
+ if (prologue) {
+ caption = content.substr(0, i);
+ content.erase(0, i);
+ }
+ parent_context.check_layout(os);
+ if (minted_env && minted_float_has_caption) {
+ eat_whitespace(p, os, parent_context, true);
+ os << content << "\n";
+ if (!caption.empty())
+ os << caption << "\n";
+ os << "\n\\end_layout\n"; // close inner layout
+ end_inset(os); // close caption inset
+ os << "\n\\end_layout\n"; // close outer layout
+ } else if (!caption.empty()) {
+ if (!minted_env) {
+ begin_inset(os, "listings\n");
+ os << "lstparams " << '"' << minted_float << '"' << '\n';
+ os << "inline false\n";
+ os << "status collapsed\n";
+ }
+ os << "\n\\begin_layout Plain Layout\n";
+ begin_inset(os, "Caption Standard\n");
+ Context newcontext(true, parent_context.textclass,
+ 0, 0, parent_context.font);
+ newcontext.check_layout(os);
+ os << caption << "\n";
+ newcontext.check_end_layout(os);
+ end_inset(os);
+ os << "\n\\end_layout\n";
+ } else if (content.empty()) {
+ begin_inset(os, "listings\n");
+ os << "lstparams " << '"' << minted_float << '"' << '\n';
+ os << "inline false\n";
+ os << "status collapsed\n";
+ } else {
+ os << content << "\n";
+ }
+ end_inset(os); // close listings inset
+ parent_context.check_end_layout(os);
+ parent_context.new_paragraph(os);
+ p.skip_spaces();
+ minted_float.clear();
+ minted_float_has_caption = false;
+ break;
+ }
+
+ if (name == "lstlisting" || name == "minted") {
+ bool use_minted = name == "minted";
+ // with listings, we do not eat newlines here since
+ // \begin{lstlistings}
+ // [foo]
+ // and
+ // // \begin{lstlistings}%
+ //
+ // [foo]
+ // reads [foo] as content, whereas
+ // // \begin{lstlistings}%
+ // [foo]
+ // or
+ // \begin{lstlistings}[foo,
+ // bar]
+ // reads [foo...] as argument.
+ eat_whitespace(p, os, parent_context, false, use_minted);
+ if (use_minted && minted_float.empty()) {
+ // look ahead for a bottom caption
+ p.pushPosition();
+ bool found_end_minted = false;
+ while (!found_end_minted && p.good()) {
+ Token const & t = p.get_token();
+ p.skip_spaces();
+ string const envname =
+ p.next_token().cat() == catBegin
+ ? p.getArg('{', '}') : string();
+ found_end_minted = t.asInput() == "\\end"
+ && envname == "minted";
+ }
+ eat_whitespace(p, os, parent_context, true);
+ Token const & t = p.get_token();
+ p.skip_spaces(true);
+ if (t.asInput() == "\\lyxmintcaption") {
+ string const pos = p.getArg('[', ']');
+ if (pos == "b") {
+ string const caption =
+ parse_text_snippet(p, FLAG_ITEM,
+ false, parent_context);
+ minted_nonfloat_caption = "[b]" + caption;
+ eat_whitespace(p, os, parent_context, true);
+ }
+ }
+ p.popPosition();
+ }
+ parse_listings(p, os, parent_context, false, use_minted);
+ p.skip_spaces();
+ break;
+ }
+
+ if (!parent_context.new_layout_allowed) {
+ parse_unknown_environment(p, name, os, FLAG_END, outer,
+ parent_context);
+ break;
+ }
+
+ // Alignment and spacing settings
+ // FIXME (bug xxxx): These settings can span multiple paragraphs and
+ // therefore are totally broken!
+ // Note that \centering, \raggedright, and \raggedleft cannot be handled, as
+ // they are commands not environments. They are furthermore switches that
+ // can be ended by another switches, but also by commands like \footnote or
+ // \parbox. So the only safe way is to leave them untouched.
+ // However, we support the pseudo-environments
+ // \begin{centering} ... \end{centering}
+ // \begin{raggedright} ... \end{raggedright}
+ // \begin{raggedleft} ... \end{raggedleft}
+ // since they are used by LyX in floats (for spacing reasons)
+ if (name == "center" || name == "centering"
+ || name == "flushleft" || name == "raggedright"
+ || name == "flushright" || name == "raggedleft"
+ || name == "singlespace" || name == "onehalfspace"
+ || name == "doublespace" || name == "spacing") {
+ eat_whitespace(p, os, parent_context, false);
+ // 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);
+ }
+ if (name == "flushleft" || name == "raggedright")
+ parent_context.add_extra_stuff("\\align left\n");
+ else if (name == "flushright" || name == "raggedleft")
+ parent_context.add_extra_stuff("\\align right\n");
+ else if (name == "center" || name == "centering")
+ 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") {
+ parent_context.add_extra_stuff("\\paragraph_spacing onehalf\n");
+ preamble.registerAutomaticallyLoadedPackage("setspace");
+ } else if (name == "doublespace") {
+ parent_context.add_extra_stuff("\\paragraph_spacing double\n");
+ 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();
+ // We must begin a new paragraph to reset the alignment
+ parent_context.new_paragraph(os);
+ p.skip_spaces();
+ break;
+ }
+
+ // The single '=' is meant here.
+ 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);
+ if (parent_context.deeper_paragraph) {
+ // We are beginning a nested environment after a
+ // deeper paragraph inside the outer list environment.
+ // Therefore we don't need to output a "begin deeper".
+ context.need_end_deeper = true;
+ }
+ parent_context.check_end_layout(os);
+ if (last_env == name) {
+ // we need to output a separator since LyX would export
+ // the two environments as one otherwise (bug 5716)
+ TeX2LyXDocClass const & textclass(parent_context.textclass);
+ Context newcontext(true, textclass,
+ &(textclass.defaultLayout()));
+ newcontext.check_layout(os);
+ begin_inset(os, "Separator plain\n");
+ end_inset(os);
+ newcontext.check_end_layout(os);
+ }
+ switch (context.layout->latextype) {
+ case LATEX_LIST_ENVIRONMENT:
+ context.in_list_preamble =
+ !context.layout->listpreamble().empty()
+ && p.hasListPreamble(context.layout->itemcommand());
+ context.add_par_extra_stuff("\\labelwidthstring "
+ + p.verbatim_item() + '\n');
+ p.skip_spaces();
+ break;
+ case LATEX_BIB_ENVIRONMENT:
+ p.verbatim_item(); // swallow next arg
+ p.skip_spaces();
+ break;
+ default:
+ break;
+ }
+ context.check_deeper(os);
+ if (newlayout->keepempty) {
+ // We need to start a new paragraph
+ // even if it is empty.
+ context.new_paragraph(os);
+ context.check_layout(os);
+ }
+ // handle known optional and required arguments
+ if (context.layout->latextype == LATEX_ENVIRONMENT)
+ output_arguments(os, p, outer, false, string(), context,
+ context.layout->latexargs());
+ else if (context.layout->latextype == LATEX_ITEM_ENVIRONMENT) {
+ context.in_list_preamble =
+ !context.layout->listpreamble().empty()
+ && p.hasListPreamble(context.layout->itemcommand());
+ ostringstream oss;
+ output_arguments(oss, p, outer, false, string(), context,
+ context.layout->latexargs());
+ context.list_extra_stuff = oss.str();
+ }
+ if (context.in_list_preamble) {
+ // Collect the stuff between \begin and first \item
+ context.list_preamble =
+ parse_text_snippet(p, FLAG_END, outer, context);
+ context.in_list_preamble = false;
+ }
+ parse_text(p, os, FLAG_END, outer, context);
+ if (context.layout->latextype == LATEX_ENVIRONMENT)
+ output_arguments(os, p, outer, false, "post", context,
+ context.layout->postcommandargs());
+ context.check_end_layout(os);
+ if (parent_context.deeper_paragraph) {
+ // We must suppress the "end deeper" because we
+ // suppressed the "begin deeper" above.
+ context.need_end_deeper = false;
+ }
+ 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->required();
+ set<string>::const_iterator it = req.begin();
+ set<string>::const_iterator en = req.end();
+ for (; it != en; ++it)
+ preamble.registerAutomaticallyLoadedPackage(*it);
+ break;
+ }
+
+ // The single '=' is meant here.
+ if ((newinsetlayout = findInsetLayout(parent_context.textclass, name, false))) {
+ eat_whitespace(p, os, parent_context, false);
+ parent_context.check_layout(os);
+ begin_inset(os, "Flex ");
+ docstring flex_name = newinsetlayout->name();
+ // FIXME: what do we do if the prefix is not Flex: ?
+ if (prefixIs(flex_name, from_ascii("Flex:")))
+ flex_name.erase(0, 5);
+ os << to_utf8(flex_name) << '\n'
+ << "status collapsed\n";
+ if (newinsetlayout->isPassThru()) {
+ string const arg = p.verbatimEnvironment(name);
+ Context context(true, parent_context.textclass,
+ &parent_context.textclass.plainLayout(),
+ parent_context.layout);
+ output_ert(os, arg, parent_context);
+ } else
+ parse_text_in_inset(p, os, FLAG_END, false, parent_context, newinsetlayout);
+ end_inset(os);
+ break;
+ }
+
+ if (name == "appendix") {
+ // This is no good latex style, but it works and is used in some documents...
+ eat_whitespace(p, os, parent_context, false);
+ parent_context.check_end_layout(os);
+ Context context(true, parent_context.textclass, parent_context.layout,
+ parent_context.layout, parent_context.font);
+ context.check_layout(os);
+ os << "\\start_of_appendix\n";
+ parse_text(p, os, FLAG_END, outer, context);
+ context.check_end_layout(os);
+ p.skip_spaces();
+ break;
+ }
+
+ if (known_environments.find(name) != known_environments.end()) {
+ vector<ArgumentType> arguments = known_environments[name];
+ // The last "argument" denotes wether we may translate the
+ // environment contents to LyX
+ // The default required if no argument is given makes us
+ // compatible with the reLyXre environment.
+ ArgumentType contents = arguments.empty() ?
+ required :
+ arguments.back();
+ if (!arguments.empty())
+ arguments.pop_back();
+ // See comment in parse_unknown_environment()
+ bool const specialfont =
+ (parent_context.font != parent_context.normalfont);
+ bool const new_layout_allowed =
+ parent_context.new_layout_allowed;
+ if (specialfont)
+ parent_context.new_layout_allowed = false;
+ parse_arguments("\\begin{" + name + "}", arguments, p, os,
+ outer, parent_context);
+ if (contents == verbatim)
+ output_ert_inset(os, p.ertEnvironment(name),