#include "context.h"
#include "FloatList.h"
#include "lengthcommon.h"
-#include "support/FileInfo.h"
#include "support/lstrings.h"
-#include "support/tostr.h"
+#include "support/convert.h"
#include "support/filetools.h"
+#include <boost/filesystem/operations.hpp>
#include <boost/tuple/tuple.hpp>
#include <iostream>
#include <sstream>
#include <vector>
-using lyx::support::FileInfo;
+using lyx::support::ChangeExtension;
using lyx::support::MakeAbsPath;
+using lyx::support::MakeRelPath;
using lyx::support::rtrim;
using lyx::support::suffixIs;
using lyx::support::contains;
using std::string;
using std::vector;
-
-/// thin wrapper around parse_text using a string
-string parse_text(Parser & p, unsigned flags, const bool outer,
- Context & context)
-{
- ostringstream os;
- parse_text(p, os, flags, outer, context);
- return os.str();
-}
+namespace fs = boost::filesystem;
void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
- Context & context)
+ Context const & context)
{
Context newcontext(true, context.textclass);
newcontext.font = context.font;
}
-/// parses a paragraph snippet, useful for example for \emph{...}
+namespace {
+
+/// parses a paragraph snippet, useful for example for \\emph{...}
void parse_text_snippet(Parser & p, ostream & os, unsigned flags, bool outer,
Context & context)
{
- Context newcontext(false, context.textclass);
- newcontext.font = context.font;
+ Context newcontext(context);
+ // Don't inherit the extra stuff
+ newcontext.extra_stuff.clear();
parse_text(p, os, flags, outer, newcontext);
- // should not be needed
- newcontext.check_end_layout(os);
+ // Make sure that we don't create invalid .lyx files
+ context.need_layout = newcontext.need_layout;
+ context.need_end_layout = newcontext.need_end_layout;
}
-namespace {
+/*!
+ * Thin wrapper around parse_text_snippet() using a string.
+ *
+ * We completely ignore \c context.need_layout and \c context.need_end_layout,
+ * because our return value is not used directly (otherwise the stream version
+ * of parse_text_snippet() could be used). That means that the caller needs
+ * to do layout management manually.
+ * This is intended to parse text that does not create any layout changes.
+ */
+string parse_text_snippet(Parser & p, unsigned flags, const bool outer,
+ Context & context)
+{
+ Context newcontext(context);
+ newcontext.need_layout = false;
+ newcontext.need_end_layout = false;
+ newcontext.new_layout_allowed = false;
+ // Avoid warning by Context::~Context()
+ newcontext.extra_stuff.clear();
+ ostringstream os;
+ parse_text_snippet(p, os, flags, outer, newcontext);
+ return os.str();
+}
+
char const * const known_latex_commands[] = { "ref", "cite", "label", "index",
"printindex", "pageref", "url", "vref", "vpageref", "prettyref", "eqref", 0 };
/*!
* Graphics file extensions known by the pdftex driver of the graphics package.
- * \see known_dvips_graphics_formats
+ * \sa known_dvips_graphics_formats
*/
char const * const known_pdftex_graphics_formats[] = {"png", "pdf", "jpg",
"mps", "tif", 0};
+/*!
+ * Known file extensions for TeX files as used by \\include.
+ */
+char const * const known_tex_extensions[] = {"tex", 0};
+
/// splits "x=z, y=b" into a map
map<string, string> split_map(string const & s)
* The latter can be a real unit like "pt", or a latex length variable
* like "\textwidth". The unit may contain additional stuff like glue
* lengths, but we don't care, because such lengths are ERT anyway.
- * \return true if \param value and \param unit are valid.
+ * \returns true if \p value and \p unit are valid.
*/
bool splitLatexLength(string const & len, string & value, string & unit)
{
/*!
- * Translates a LaTeX length into \param value, \param unit and
- * \param special parts suitable for a box inset.
+ * Translates a LaTeX length into \p value, \p unit and
+ * \p special parts suitable for a box inset.
* The difference from translate_len() is that a box inset knows about
- * some special "units" that are stored in \param special.
+ * some special "units" that are stored in \p special.
*/
void translate_box_len(string const & length, string & value, string & unit, string & special)
{
// We don't use ChangeExtension() because it does the wrong
// thing if name contains a dot.
string const trial = name + '.' + (*what);
- if (FileInfo(MakeAbsPath(trial, path)).exist())
+ if (fs::exists(MakeAbsPath(trial, path)))
return trial;
}
return string();
}
-void handle_ert(ostream & os, string const & s, Context & context, bool check_layout = true)
+void handle_ert(ostream & os, string const & s, Context & context)
{
- if (check_layout) {
- // We must have a valid layout before outputting the ERT inset.
- context.check_layout(os);
- }
+ // We must have a valid layout before outputting the ERT inset.
+ context.check_layout(os);
Context newcontext(true, context.textclass);
begin_inset(os, "ERT");
os << "\nstatus collapsed\n";
for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
if (*it == '\\')
os << "\n\\backslash\n";
- else if (*it == '\n')
- os << "\n\\newline\n";
- else
+ else if (*it == '\n') {
+ newcontext.new_paragraph(os);
+ newcontext.check_layout(os);
+ } else
os << *it;
}
newcontext.check_end_layout(os);
os << *it;
}
// make sure that our comment is the last thing on the line
- os << "\n\\newline";
+ newcontext.new_paragraph(os);
+ newcontext.check_layout(os);
newcontext.check_end_layout(os);
end_inset(os);
}
eat_whitespace(p, os, context, false);
}
}
- parse_text_snippet(p, os, FLAG_ITEM, outer, context);
+ parse_text(p, os, FLAG_ITEM, outer, context);
context.check_end_layout(os);
if (parent_context.deeper_paragraph) {
// We must suppress the "end deeper" because we
}
+/*!
+ * Parse all arguments of \p command
+ */
+void parse_arguments(string const & command,
+ vector<ArgumentType> const & template_arguments,
+ Parser & p, ostream & os, bool outer, Context & context)
+{
+ string ert = command;
+ size_t no_arguments = template_arguments.size();
+ for (size_t i = 0; i < no_arguments; ++i) {
+ switch (template_arguments[i]) {
+ case required:
+ // This argument contains regular LaTeX
+ handle_ert(os, ert + '{', context);
+ parse_text(p, os, FLAG_ITEM, outer, context);
+ ert = "}";
+ break;
+ case verbatim:
+ // This argument may contain special characters
+ ert += '{' + p.verbatim_item() + '}';
+ break;
+ case optional:
+ ert += p.getOpt();
+ break;
+ }
+ }
+ handle_ert(os, ert, context);
+}
+
+
/*!
* Check whether \p command is a known command. If yes,
* handle the command with all arguments.
bool outer, Context & context)
{
if (known_commands.find(command) != known_commands.end()) {
- vector<ArgumentType> const & template_arguments = known_commands[command];
- string ert = command;
- size_t no_arguments = template_arguments.size();
- for (size_t i = 0; i < no_arguments; ++i) {
- switch (template_arguments[i]) {
- case required:
- // This argument contains regular LaTeX
- handle_ert(os, ert + '{', context);
- parse_text(p, os, FLAG_ITEM, outer, context);
- ert = "}";
- break;
- case verbatim:
- // This argument may contain special characters
- ert += '{' + p.verbatim_item() + '}';
- break;
- case optional:
- ert += p.getOpt();
- break;
- }
- }
- handle_ert(os, ert, context);
+ parse_arguments(command, known_commands[command], p, os,
+ outer, context);
return true;
}
return false;
}
+/// parse an unknown environment
+void parse_unknown_environment(Parser & p, string const & name, ostream & os,
+ unsigned flags, bool outer,
+ Context & parent_context)
+{
+ if (name == "tabbing")
+ // We need to remember that we have to handle '\=' specially
+ flags |= FLAG_TABBING;
+
+ // We need to translate font changes and paragraphs inside the
+ // environment to ERT if we have a non standard font.
+ // Otherwise things like
+ // \large\begin{foo}\huge bar\end{foo}
+ // will not work.
+ 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;
+ handle_ert(os, "\\begin{" + name + "}", parent_context);
+ parse_text_snippet(p, os, flags, outer, parent_context);
+ handle_ert(os, "\\end{" + name + "}", parent_context);
+ if (specialfont)
+ parent_context.new_layout_allowed = new_layout_allowed;
+}
+
+
void parse_environment(Parser & p, ostream & os, bool outer,
- Context & parent_context)
+ Context & parent_context)
{
LyXLayout_ptr newlayout;
string const name = p.getArg('{', '}');
const bool is_starred = suffixIs(name, '*');
string const unstarred_name = rtrim(name, "*");
- eat_whitespace(p, os, parent_context, false);
active_environments.push_back(name);
if (is_math_env(name)) {
end_inset(os);
}
- else if (name == "tabular") {
+ else if (name == "tabular" || name == "longtable") {
+ eat_whitespace(p, os, parent_context, false);
parent_context.check_layout(os);
begin_inset(os, "Tabular ");
- handle_tabular(p, os, parent_context);
+ handle_tabular(p, os, name == "longtable", parent_context);
end_inset(os);
+ p.skip_spaces();
}
else if (parent_context.textclass.floats().typeExist(unstarred_name)) {
+ 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';
}
- os << "wide " << tostr(is_starred)
+ os << "wide " << convert<string>(is_starred)
<< "\nsideways false"
<< "\nstatus open\n\n";
parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
// 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")
+ else if (name == "minipage") {
+ eat_whitespace(p, os, parent_context, false);
parse_box(p, os, FLAG_END, outer, parent_context, false);
+ p.skip_spaces();
+ }
+
+ else if (name == "comment") {
+ eat_whitespace(p, os, parent_context, false);
+ parent_context.check_layout(os);
+ begin_inset(os, "Note Comment\n");
+ os << "status open\n";
+ parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+ end_inset(os);
+ p.skip_spaces();
+ }
+
+ else if (name == "lyxgreyedout") {
+ eat_whitespace(p, os, parent_context, false);
+ parent_context.check_layout(os);
+ begin_inset(os, "Note Greyedout\n");
+ os << "status open\n";
+ parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+ end_inset(os);
+ p.skip_spaces();
+ }
+
+ else if (!parent_context.new_layout_allowed)
+ parse_unknown_environment(p, name, os, FLAG_END, outer,
+ parent_context);
// Alignment settings
else if (name == "center" || name == "flushleft" || name == "flushright" ||
name == "centering" || name == "raggedright" || name == "raggedleft") {
+ 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 ");
+ parent_context.add_extra_stuff("\\align left\n");
else if (name == "flushright" || name == "raggedleft")
- parent_context.add_extra_stuff("\\align right ");
+ parent_context.add_extra_stuff("\\align right\n");
else
- parent_context.add_extra_stuff("\\align center ");
+ parent_context.add_extra_stuff("\\align center\n");
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();
}
// The single '=' is meant here.
else if ((newlayout = findLayout(parent_context.textclass, name)).get() &&
- newlayout->isEnvironment()) {
+ newlayout->isEnvironment()) {
+ 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) {
}
context.check_end_deeper(os);
parent_context.new_paragraph(os);
+ p.skip_spaces();
}
else 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);
os << "\\start_of_appendix\n";
parse_text(p, os, FLAG_END, outer, context);
context.check_end_layout(os);
+ p.skip_spaces();
}
- else if (name == "comment") {
- parent_context.check_layout(os);
- begin_inset(os, "Note Comment\n");
- os << "status open\n";
- parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
- end_inset(os);
- }
-
- else if (name == "lyxgreyedout") {
- parent_context.check_layout(os);
- begin_inset(os, "Note Greyedout\n");
- os << "status open\n";
- parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
- end_inset(os);
- }
-
- else if (name == "tabbing") {
- // We need to remember that we have to handle '\=' specially
- handle_ert(os, "\\begin{" + name + "}", parent_context);
- parse_text_snippet(p, os, FLAG_END | FLAG_TABBING, outer, parent_context);
+ else 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)
+ handle_ert(os, p.verbatimEnvironment(name),
+ parent_context);
+ else
+ parse_text_snippet(p, os, FLAG_END, outer,
+ parent_context);
handle_ert(os, "\\end{" + name + "}", parent_context);
+ if (specialfont)
+ parent_context.new_layout_allowed = new_layout_allowed;
}
- else {
- handle_ert(os, "\\begin{" + name + "}", parent_context);
- parse_text_snippet(p, os, FLAG_END, outer, parent_context);
- handle_ert(os, "\\end{" + name + "}", parent_context);
- }
+ else
+ parse_unknown_environment(p, name, os, FLAG_END, outer,
+ parent_context);
active_environments.pop_back();
- if (name != "math")
- p.skip_spaces();
}
+
/// parses a comment and outputs it to \p os.
void parse_comment(Parser & p, ostream & os, Token const & t, Context & context)
{
BOOST_ASSERT(t.cat() == catComment);
- context.check_layout(os);
if (!t.cs().empty()) {
+ context.check_layout(os);
handle_comment(os, '%' + t.cs(), context);
if (p.next_token().cat() == catNewline) {
// A newline after a comment line starts a new
// paragraph
- if(!context.atParagraphStart()) {
- // Only start a new paragraph if not already
- // done (we might get called recursively)
- context.new_paragraph(os);
- }
+ if (context.new_layout_allowed) {
+ if(!context.atParagraphStart())
+ // Only start a new paragraph if not already
+ // done (we might get called recursively)
+ context.new_paragraph(os);
+ } else
+ handle_ert(os, "\n", context);
eat_whitespace(p, os, context, true);
}
} else {
string & currentvalue, string const & newvalue)
{
context.check_layout(os);
- string oldvalue = currentvalue;
+ string const oldvalue = currentvalue;
currentvalue = newvalue;
os << '\n' << attribute << ' ' << newvalue << "\n";
parse_text_snippet(p, os, flags, outer, context);
- currentvalue = oldvalue;
+ context.check_layout(os);
os << '\n' << attribute << ' ' << oldvalue << "\n";
+ currentvalue = oldvalue;
}
/// get the arguments of a natbib or jurabib citation command
-std::pair<string, string> getCiteArguments(Parser & p, ostream & os,
- Context & context, bool natbibOrder)
+std::pair<string, string> getCiteArguments(Parser & p, bool natbibOrder)
{
// We need to distinguish "" and "[]", so we can't use p.getOpt().
// text before the citation
string before;
// text after the citation
- string after;
+ string after = p.getFullOpt();
- eat_whitespace(p, os, context, false);
- if (p.next_token().asInput() == "[") {
- after = '[' + p.getArg('[', ']') + ']';
- eat_whitespace(p, os, context, false);
- if (natbibOrder) {
- if (p.next_token().asInput() == "[") {
- before = after;
- after = '[' + p.getArg('[', ']') + ']';
- }
- } else {
- if (p.next_token().asInput() == "[")
- before = '[' + p.getArg('[', ']') + ']';
- }
+ if (!after.empty()) {
+ before = p.getFullOpt();
+ if (natbibOrder && !before.empty())
+ std::swap(before, after);
}
return std::make_pair(before, after);
}
+
+/// Convert filenames with TeX macros and/or quotes to something LyX can understand
+string const normalize_filename(string const & name)
+{
+ Parser p(trim(name, "\""));
+ ostringstream os;
+ while (p.good()) {
+ Token const & t = p.get_token();
+ if (t.cat() != catEscape)
+ os << t.asInput();
+ else if (t.cs() == "lyxdot") {
+ // This is used by LyX for simple dots in relative
+ // names
+ os << '.';
+ p.skip_spaces();
+ } else if (t.cs() == "space") {
+ os << ' ';
+ p.skip_spaces();
+ } else
+ os << t.asInput();
+ }
+ return os.str();
+}
+
+
+/// Convert \p name from TeX convention (relative to master file) to LyX
+/// convention (relative to .lyx file) if it is relative
+void fix_relative_filename(string & name)
+{
+ if (lyx::support::AbsolutePath(name))
+ return;
+ name = MakeRelPath(MakeAbsPath(name, getMasterFilePath()),
+ getParentFilePath());
+}
+
} // anonymous namespace
t.cat() == catOther ||
t.cat() == catAlign ||
t.cat() == catParameter) {
+ // This translates "&" to "\\&" which may be wrong...
context.check_layout(os);
os << t.character();
}
else if (p.isParagraph()) {
- context.new_paragraph(os);
+ if (context.new_layout_allowed)
+ context.new_paragraph(os);
+ else
+ handle_ert(os, "\\par ", context);
eat_whitespace(p, os, context, true);
}
os << t.character();
}
+ else if (t.cat() == catBegin &&
+ p.next_token().cat() == catEnd) {
+ // {}
+ Token const prev = p.prev_token();
+ p.get_token();
+ if (p.next_token().character() == '`' ||
+ (prev.character() == '-' &&
+ p.next_token().character() == '-'))
+ ; // ignore it in {}`` or -{}-
+ else
+ handle_ert(os, "{}", context);
+
+ }
+
else if (t.cat() == catBegin) {
context.check_layout(os);
// special handling of font attribute changes
Token const prev = p.prev_token();
Token const next = p.next_token();
Font const oldFont = context.font;
- string const s = parse_text(p, FLAG_BRACE_LAST, outer,
- context);
- context.font = oldFont;
- if (s.empty() && (p.next_token().character() == '`' ||
- (prev.character() == '-' &&
- p.next_token().character() == '-')))
- ; // ignore it in {}`` or -{}-
- else if (s == "[" || s == "]" || s == "*")
- os << s;
- else if (is_known(next.cs(), known_sizes))
- // s will change the size, so we must reset
- // it here
- os << s << "\n\\size " << context.font.size
- << "\n";
- else if (is_known(next.cs(), known_font_families))
- // s will change the font family, so we must
- // reset it here
- os << s << "\n\\family "
- << context.font.family << "\n";
- else if (is_known(next.cs(), known_font_series))
- // s will change the font series, so we must
- // reset it here
- os << s << "\n\\series "
- << context.font.series << "\n";
- else if (is_known(next.cs(), known_font_shapes))
- // s will change the font shape, so we must
+ if (next.character() == '[' ||
+ next.character() == ']' ||
+ next.character() == '*') {
+ p.get_token();
+ if (p.next_token().cat() == catEnd) {
+ os << next.character();
+ p.get_token();
+ } else {
+ p.putback();
+ handle_ert(os, "{", context);
+ parse_text_snippet(p, os,
+ FLAG_BRACE_LAST,
+ outer, context);
+ handle_ert(os, "}", context);
+ }
+ } else if (! context.new_layout_allowed) {
+ handle_ert(os, "{", context);
+ parse_text_snippet(p, os, FLAG_BRACE_LAST,
+ outer, context);
+ handle_ert(os, "}", context);
+ } else if (is_known(next.cs(), known_sizes)) {
+ // next will change the size, so we must
// reset it here
- os << s << "\n\\shape "
- << context.font.shape << "\n";
- else if (is_known(next.cs(), known_old_font_families) ||
- is_known(next.cs(), known_old_font_series) ||
- is_known(next.cs(), known_old_font_shapes))
- // s will change the font family, series
+ parse_text_snippet(p, os, FLAG_BRACE_LAST,
+ outer, context);
+ if (!context.atParagraphStart())
+ os << "\n\\size "
+ << context.font.size << "\n";
+ } else if (is_known(next.cs(), known_font_families)) {
+ // next will change the font family, so we
+ // must reset it here
+ parse_text_snippet(p, os, FLAG_BRACE_LAST,
+ outer, context);
+ if (!context.atParagraphStart())
+ os << "\n\\family "
+ << context.font.family << "\n";
+ } else if (is_known(next.cs(), known_font_series)) {
+ // next will change the font series, so we
+ // must reset it here
+ parse_text_snippet(p, os, FLAG_BRACE_LAST,
+ outer, context);
+ if (!context.atParagraphStart())
+ os << "\n\\series "
+ << context.font.series << "\n";
+ } else if (is_known(next.cs(), known_font_shapes)) {
+ // next will change the font shape, so we
+ // must reset it here
+ parse_text_snippet(p, os, FLAG_BRACE_LAST,
+ outer, context);
+ if (!context.atParagraphStart())
+ os << "\n\\shape "
+ << context.font.shape << "\n";
+ } else if (is_known(next.cs(), known_old_font_families) ||
+ is_known(next.cs(), known_old_font_series) ||
+ is_known(next.cs(), known_old_font_shapes)) {
+ // next will change the font family, series
// and shape, so we must reset it here
- os << s
- << "\n\\family " << context.font.family
- << "\n\\series " << context.font.series
- << "\n\\shape " << context.font.shape
- << "\n";
- else {
- handle_ert(os, "{", context, false);
- // s will end the current layout and begin a
- // new one if necessary
- os << s;
+ parse_text_snippet(p, os, FLAG_BRACE_LAST,
+ outer, context);
+ if (!context.atParagraphStart())
+ os << "\n\\family "
+ << context.font.family
+ << "\n\\series "
+ << context.font.series
+ << "\n\\shape "
+ << context.font.shape << "\n";
+ } else {
+ handle_ert(os, "{", context);
+ parse_text_snippet(p, os, FLAG_BRACE_LAST,
+ outer, context);
handle_ert(os, "}", context);
}
}
bool optarg = false;
if (p.next_token().character() == '[') {
p.get_token(); // eat '['
- Context newcontext(false, context.textclass);
- newcontext.font = context.font;
- s = parse_text(p, FLAG_BRACK_LAST, outer, newcontext);
+ s = parse_text_snippet(p, FLAG_BRACK_LAST,
+ outer, context);
optarg = true;
}
context.set_item();
else if (t.cs() == "noindent") {
p.skip_spaces();
- context.add_extra_stuff("\\noindent ");
+ context.add_extra_stuff("\\noindent\n");
}
else if (t.cs() == "appendix") {
- p.skip_spaces();
- context.add_extra_stuff("\\start_of_appendix ");
+ context.add_extra_stuff("\\start_of_appendix\n");
+ // We need to start a new paragraph. Otherwise the
+ // appendix in 'bla\appendix\chapter{' would start
+ // too late.
+ context.new_paragraph(os);
+ // We need to make sure that the paragraph is
+ // generated even if it is empty. Otherwise the
+ // appendix in '\par\appendix\par\chapter{' would
+ // start too late.
+ context.check_layout(os);
+ // FIXME: This is a hack to prevent paragraph
+ // deletion if it is empty. Handle this better!
+ handle_comment(os,
+ "%dummy comment inserted by tex2lyx to "
+ "ensure that this paragraph is not empty",
+ context);
+ // Both measures above may generate an additional
+ // empty paragraph, but that does not hurt, because
+ // whitespace does not matter here.
+ eat_whitespace(p, os, context, true);
}
// Must attempt to parse "Section*" before "Section".
else if ((p.next_token().asInput() == "*") &&
+ context.new_layout_allowed &&
// The single '=' is meant here.
(newlayout = findLayout(context.textclass,
t.cs() + '*')).get() &&
}
// The single '=' is meant here.
- else if ((newlayout = findLayout(context.textclass, t.cs())).get() &&
+ else if (context.new_layout_allowed &&
+ (newlayout = findLayout(context.textclass, t.cs())).get() &&
newlayout->isCommand()) {
output_command_layout(os, p, outer, context, newlayout);
p.skip_spaces();
}
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 name = subst(p.verbatim_item(), "\\lyxdot ", ".");
+ if (clip)
+ opts["clip"] = string();
+ 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 (!FileInfo(MakeAbsPath(name, path)).exist()) {
+ if (!fs::exists(MakeAbsPath(name, path))) {
// The file extension is probably missing.
// Now try to find it out.
string const dvips_name =
name = dvips_name;
} else if (!pdftex_name.empty())
name = pdftex_name;
-
- if (!FileInfo(MakeAbsPath(name, path)).exist())
- cerr << "Warning: Could not find graphics file '"
- << name << "'." << endl;
}
+ if (fs::exists(MakeAbsPath(name, path)))
+ fix_relative_filename(name);
+ else
+ cerr << "Warning: Could not find graphics file '"
+ << name << "'." << endl;
+
context.check_layout(os);
begin_inset(os, "Graphics ");
os << "\n\tfilename " << name << '\n';
end_inset(os);
}
- else if (t.cs() == "footnote") {
+ else if (t.cs() == "footnote" ||
+ (t.cs() == "thanks" && context.layout->intitle)) {
p.skip_spaces();
context.check_layout(os);
begin_inset(os, "Foot\n");
else if (t.cs() == "ensuremath") {
p.skip_spaces();
context.check_layout(os);
- Context newcontext(false, context.textclass);
- newcontext.font = context.font;
- string s = parse_text(p, FLAG_ITEM, false, newcontext);
+ string const s = p.verbatim_item();
if (s == "±" || s == "³" || s == "²" || s == "µ")
os << s;
else
}
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
}
os << "\n\\shape " << context.font.shape << "\n";
if (t.cs() == "textnormal") {
parse_text_snippet(p, os, FLAG_ITEM, outer, context);
+ output_font_change(os, context.font, oldFont);
context.font = oldFont;
- os << "\n\\shape " << oldFont.shape << "\n";
- os << "\n\\series " << oldFont.series << "\n";
- os << "\n\\family " << oldFont.family << "\n";
} else
eat_whitespace(p, os, context, false);
}
else if (t.cs() == "underbar") {
+ // Do NOT handle \underline.
+ // \underbar cuts through y, g, q, p etc.,
+ // \underline does not.
context.check_layout(os);
os << "\n\\bar under\n";
parse_text_snippet(p, os, FLAG_ITEM, outer, context);
+ context.check_layout(os);
os << "\n\\bar default\n";
}
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";
}
else if (use_natbib &&
is_known(t.cs(), known_natbib_commands) &&
((t.cs() != "citefullauthor" &&
- t.cs() != "citeyear" &&
- t.cs() != "citeyearpar") ||
+ t.cs() != "citeyear" &&
+ t.cs() != "citeyearpar") ||
p.next_token().asInput() != "*")) {
context.check_layout(os);
// tex lyx
// text after the citation
string after;
- boost::tie(before, after) =
- getCiteArguments(p, os, context, true);
+ boost::tie(before, after) = getCiteArguments(p, true);
if (command == "\\cite") {
// \cite without optional argument means
// \citet, \cite with at least one optional
string after;
boost::tie(before, after) =
- getCiteArguments(p, os, context,
- argumentOrder != 'j');
+ getCiteArguments(p, argumentOrder != 'j');
string const citation = p.verbatim_item();
if (!before.empty() && argumentOrder == '\0') {
cerr << "Warning: Assuming argument order "
- << "of jurabib version 0.6 for\n'"
+ "of jurabib version 0.6 for\n'"
<< command << before << after << '{'
<< citation << "}'.\n"
- << "Add 'jurabiborder' to the jurabib "
- << "package options if you used an\n"
- << "earlier jurabib version." << endl;
+ "Add 'jurabiborder' to the jurabib "
+ "package options if you used an\n"
+ "earlier jurabib version." << endl;
}
begin_inset(os, "LatexCommand ");
os << command << after << before
skip_braces(p);
}
- else if (is_known(t.cs(), known_sizes)) {
+ else if (is_known(t.cs(), known_sizes) &&
+ context.new_layout_allowed) {
char const * const * where = is_known(t.cs(), known_sizes);
context.check_layout(os);
+ Font const oldFont = context.font;
context.font.size = known_coded_sizes[where - known_sizes];
- os << "\n\\size " << context.font.size << '\n';
+ output_font_change(os, oldFont, context.font);
eat_whitespace(p, os, context, false);
}
- else if (is_known(t.cs(), known_font_families)) {
+ else if (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);
+ Font const oldFont = context.font;
context.font.family =
known_coded_font_families[where - known_font_families];
- os << "\n\\family " << context.font.family << '\n';
+ output_font_change(os, oldFont, context.font);
eat_whitespace(p, os, context, false);
}
- else if (is_known(t.cs(), known_font_series)) {
+ else if (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);
+ Font const oldFont = context.font;
context.font.series =
known_coded_font_series[where - known_font_series];
- os << "\n\\series " << context.font.series << '\n';
+ output_font_change(os, oldFont, context.font);
eat_whitespace(p, os, context, false);
}
- else if (is_known(t.cs(), known_font_shapes)) {
+ else if (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);
+ Font const oldFont = context.font;
context.font.shape =
known_coded_font_shapes[where - known_font_shapes];
- os << "\n\\shape " << context.font.shape << '\n';
+ 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 (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);
- string oldsize = context.font.size;
+ Font const oldFont = context.font;
context.font.init();
- context.font.size = oldsize;
+ context.font.size = oldFont.size;
context.font.family =
known_coded_font_families[where - known_old_font_families];
- os << "\n\\family " << context.font.family << "\n"
- << "\\series " << context.font.series << "\n"
- << "\\shape " << context.font.shape << "\n";
+ output_font_change(os, oldFont, context.font);
eat_whitespace(p, os, context, false);
}
- else if (is_known(t.cs(), known_old_font_series)) {
+ else if (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);
- string oldsize = context.font.size;
+ Font const oldFont = context.font;
context.font.init();
- context.font.size = oldsize;
+ context.font.size = oldFont.size;
context.font.series =
known_coded_font_series[where - known_old_font_series];
- os << "\n\\family " << context.font.family << "\n"
- << "\\series " << context.font.series << "\n"
- << "\\shape " << context.font.shape << "\n";
+ output_font_change(os, oldFont, context.font);
eat_whitespace(p, os, context, false);
}
- else if (is_known(t.cs(), known_old_font_shapes)) {
+ else if (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);
- string oldsize = context.font.size;
+ Font const oldFont = context.font;
context.font.init();
- context.font.size = oldsize;
+ context.font.size = oldFont.size;
context.font.shape =
known_coded_font_shapes[where - known_old_font_shapes];
- os << "\n\\family " << context.font.family << "\n"
- << "\\series " << context.font.series << "\n"
- << "\\shape " << context.font.shape << "\n";
+ output_font_change(os, oldFont, context.font);
eat_whitespace(p, os, context, false);
}
// we need the trim as the LyX parser chokes on such spaces
context.check_layout(os);
os << "\n\\i \\" << t.cs() << "{"
- << trim(parse_text(p, FLAG_ITEM, outer, context), " ") << "}\n";
+ << trim(parse_text_snippet(p, FLAG_ITEM, outer, context), " ")
+ << "}\n";
}
else if (t.cs() == "ss") {
context.check_layout(os);
os << "ß";
+ skip_braces(p); // eat {}
}
else if (t.cs() == "i" || t.cs() == "j") {
context.check_layout(os);
os << "\\" << t.cs() << ' ';
+ skip_braces(p); // eat {}
}
else if (t.cs() == "\\") {
name += p.get_token().asInput();
context.check_layout(os);
begin_inset(os, "Include ");
- string filename(p.getArg('{', '}'));
- string lyxname(lyx::support::ChangeExtension(filename, ".lyx"));
- if (tex2lyx(filename, lyxname)) {
- os << name << '{' << lyxname << "}\n";
+ string filename(normalize_filename(p.getArg('{', '}')));
+ string const path = getMasterFilePath();
+ // We want to preserve relative / absolute filenames,
+ // therefore path is only used for testing
+ if (t.cs() == "include" &&
+ !fs::exists(MakeAbsPath(filename, path))) {
+ // The file extension is probably missing.
+ // Now try to find it out.
+ string const tex_name =
+ find_file(filename, path,
+ known_tex_extensions);
+ if (!tex_name.empty())
+ filename = tex_name;
+ }
+ if (fs::exists(MakeAbsPath(filename, path))) {
+ string const abstexname =
+ MakeAbsPath(filename, path);
+ string const abslyxname =
+ ChangeExtension(abstexname, ".lyx");
+ fix_relative_filename(filename);
+ string const lyxname =
+ ChangeExtension(filename, ".lyx");
+ if (t.cs() != "verbatiminput" &&
+ tex2lyx(abstexname, abslyxname)) {
+ os << name << '{' << lyxname << "}\n";
+ } else {
+ os << name << '{' << filename << "}\n";
+ }
} else {
+ cerr << "Warning: Could not find included file '"
+ << filename << "'." << endl;
os << name << '{' << filename << "}\n";
}
os << "preview false\n";
end_inset(os);
}
- else if (t.cs() == "fancyhead") {
- context.check_layout(os);
- ostringstream ss;
- ss << "\\fancyhead";
- ss << p.getOpt();
- ss << '{' << p.verbatim_item() << "}\n";
- handle_ert(os, ss.str(), context);
- }
-
else if (t.cs() == "bibliographystyle") {
// store new bibliographystyle
bibliographystyle = p.verbatim_item();
skip_braces(p);
}
+ else if (t.cs() == "newpage") {
+ context.check_layout(os);
+ // FIXME: what about \\clearpage and \\pagebreak?
+ os << "\n\\newpage\n";
+ skip_braces(p); // eat {}
+ }
+
+ 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.
+ string name = t.asInput();
+ if (p.next_token().asInput() == "*") {
+ // Starred form. Eat '*'
+ p.get_token();
+ name += '*';
+ }
+ string const command = p.verbatim_item();
+ string const opt1 = p.getOpt();
+ string const opt2 = p.getFullOpt();
+ add_known_command(command, opt1, !opt2.empty());
+ string const ert = name + '{' + command + '}' +
+ opt1 + opt2 +
+ '{' + p.verbatim_item() + '}';
+ handle_ert(os, ert, context);
+ }
+
else if (t.cs() == "vspace") {
bool starred = false;
if (p.next_token().asInput() == "*") {
}
}
-
// }])