X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ftex2lyx%2Ftable.cpp;h=411fd5b6ab5534f929684b38458cc727856d2f6a;hb=a2b21e3cd4bbfd42e59161143eba6e7681aaa93f;hp=79781c74951dd5ef46d426a2fd87982082f12e64;hpb=dda1bfc94961ea493ea483512969af7bf20f0d77;p=lyx.git diff --git a/src/tex2lyx/table.cpp b/src/tex2lyx/table.cpp index 79781c7495..411fd5b6ab 100644 --- a/src/tex2lyx/table.cpp +++ b/src/tex2lyx/table.cpp @@ -16,12 +16,14 @@ #include "tex2lyx.h" +#include "Context.h" #include "Preamble.h" #include "support/lassert.h" #include "support/convert.h" #include "support/lstrings.h" +#include #include #include #include @@ -31,15 +33,13 @@ using namespace std; namespace lyx { -// filled in preamble.cpp -map special_columns; - namespace { class ColInfo { public: - ColInfo() : align('n'), valign('n'), rightlines(0), leftlines(0) {} + ColInfo() : align('n'), valign('n'), rightlines(0), leftlines(0), + varwidth(false), decimal_point('\0'), vcolumn(false) {} /// column alignment char align; /// vertical alignment @@ -52,6 +52,12 @@ public: int rightlines; /// number of lines on the left int leftlines; + /// varwidth column + bool varwidth; + /// decimal separator + char decimal_point; + /// V column type + bool vcolumn; }; @@ -103,14 +109,18 @@ public: /// the numeric values are part of the file format! -enum Multicolumn { +enum Multi { /// A normal cell CELL_NORMAL = 0, /// A multicolumn cell. The number of columns is 1 + number /// of CELL_PART_OF_MULTICOLUMN cells that follow directly - CELL_BEGIN_OF_MULTICOLUMN = 1, + CELL_BEGIN_OF_MULTICOLUMN, /// This is a dummy cell (part of a multicolumn cell) - CELL_PART_OF_MULTICOLUMN = 2 + CELL_PART_OF_MULTICOLUMN, + /// + CELL_BEGIN_OF_MULTIROW, + /// + CELL_PART_OF_MULTIROW }; @@ -118,11 +128,13 @@ class CellInfo { public: CellInfo() : multi(CELL_NORMAL), align('n'), valign('n'), leftlines(0), rightlines(0), topline(false), - bottomline(false), rotate(0) {} + bottomline(false), topline_ltrim(false), + topline_rtrim(false), bottomline_ltrim(false), + bottomline_rtrim(false), rotate(0), mrxnum(0) {} /// cell content string content; /// multicolumn flag - Multicolumn multi; + Multi multi; /// cell alignment char align; /// vertical cell alignment @@ -135,19 +147,31 @@ public: bool topline; /// do we have a line below? bool bottomline; + /// Left trimming of top line + bool topline_ltrim; + /// Right trimming of top line + bool topline_rtrim; + /// Left trimming of bottom line + bool bottomline_ltrim; + /// Right trimming of top line + bool bottomline_rtrim; /// how is the cell rotated? int rotate; /// width for multicolumn cells string width; /// special formatting for multicolumn cells string special; + /// multirow offset + string mroffset; + /// number of further multirows + int mrxnum; }; class ltType { public: // constructor - ltType() : topDL(false), bottomDL(false), empty(false) {} + ltType() : set(false), topDL(false), bottomDL(false), empty(false) {} // we have this header type (is set in the getLT... functions) bool set; // double borders on top @@ -171,6 +195,8 @@ inline char const * verbose_align(char c) return "right"; case 'l': return "left"; + case 'd': + return "decimal"; default: return "none"; } @@ -258,19 +284,37 @@ void ci2special(ColInfo & ci) // this case. return; + if (ci.decimal_point != '\0') { + // we only support decimal point natively + // with 'l' alignment in or 'n' alignment + // with width in second row + if (ci.align != 'l' && ci.align != 'n') { + ci.decimal_point = '\0'; + return; + } else + ci.special.clear(); + } + if (!ci.width.empty()) { + string arraybackslash; + if (ci.varwidth) + arraybackslash = "\\arraybackslash"; switch (ci.align) { case 'l': - ci.special += ">{\\raggedright}"; + ci.special += ">{\\raggedright" + arraybackslash + "}"; break; case 'r': - ci.special += ">{\\raggedleft}"; + ci.special += ">{\\raggedleft" + arraybackslash + "}"; break; case 'c': - ci.special += ">{\\centering}"; + ci.special += ">{\\centering" + arraybackslash + "}"; break; } - if (ci.valign == 'n') + if (ci.vcolumn) + ci.special += 'V'; + else if (ci.varwidth) + ci.special += 'X'; + else if (ci.valign == 'n') ci.special += 'p'; else ci.special += ci.valign; @@ -324,24 +368,68 @@ void handle_colalign(Parser & p, vector & colinfo, } switch (t.character()) { + case ' ': + // whitespace, ignore. + break; case 'c': case 'l': case 'r': // new column, horizontal aligned next.align = t.character(); + if (!next.special.empty()) { + ci2special(next); + // handle decimal separator + if (next.decimal_point != '\0') { + if (!colinfo.empty() && colinfo.back().align == 'r') { + colinfo.back().align = 'd'; + colinfo.back().decimal_point = next.decimal_point; + } else + next.decimal_point = '\0'; + } + } + colinfo.push_back(next); + next = ColInfo(); + break; + case 'X': + // varwidth column + next.varwidth = true; if (!next.special.empty()) ci2special(next); colinfo.push_back(next); next = ColInfo(); break; + case 'V': { + // V column type (varwidth package) + string const s = trimSpaceAndEol(p.verbatim_item()); + // V{\linewidth} is treated as a normal column + // (which allows for line breaks). The V type is + // automatically set by LyX in this case + if (s != "\\linewidth" || !next.special.empty()) { + next.vcolumn = true; + next.width = s; + ci2special(next); + } + colinfo.push_back(next); + next = ColInfo(); + break; + } case 'p': case 'b': case 'm': // new column, vertical aligned box next.valign = t.character(); next.width = p.verbatim_item(); - if (!next.special.empty()) + if (!next.special.empty()) { ci2special(next); + // handle decimal separator + if (next.decimal_point != '\0') { + if (!colinfo.empty() && colinfo.back().align == 'r') { + colinfo.back().align = 'd'; + colinfo.back().decimal_point = next.decimal_point; + } else + next.decimal_point = '\0'; + } + } colinfo.push_back(next); next = ColInfo(); break; @@ -354,7 +442,7 @@ void handle_colalign(Parser & p, vector & colinfo, next.special += '|'; } else if (colinfo.back().special.empty()) ++colinfo.back().rightlines; - else if (next.special.empty()) + else if (next.special.empty() && p.next_token().cat() != catEnd) ++next.leftlines; else colinfo.back().special += '|'; @@ -367,11 +455,11 @@ void handle_colalign(Parser & p, vector & colinfo, // Maybe this can be converted to a // horizontal alignment setting for // fixed width columns - if (s == "\\raggedleft") + if (s == "\\raggedleft" || s == "\\raggedleft\\arraybackslash") next.align = 'r'; - else if (s == "\\raggedright") + else if (s == "\\raggedright" || s == "\\raggedright\\arraybackslash") next.align = 'l'; - else if (s == "\\centering") + else if (s == "\\centering" || s == "\\centering\\arraybackslash") next.align = 'c'; else next.special = ">{" + s + '}'; @@ -394,6 +482,8 @@ void handle_colalign(Parser & p, vector & colinfo, break; } case '*': { + if (p.next_token().character() != '{') + continue; // *{n}{arg} means 'n' columns of type 'arg' string const num = p.verbatim_item(); string const arg = p.verbatim_item(); @@ -415,30 +505,32 @@ void handle_colalign(Parser & p, vector & colinfo, } case '@': // text instead of the column spacing - case '!': + case '!': { // text in addition to the column spacing + string const arg = p.verbatim_item(); next.special += t.character(); - next.special += '{' + p.verbatim_item() + '}'; + next.special += '{' + arg + '}'; + string const sarg = arg.size() > 2 ? arg.substr(0, arg.size() - 1) : string(); + if (t.character() == '@' && sarg == "\\extracolsep{0pt}") + next.decimal_point = arg.back(); break; - default: + } + default: { // try user defined column types - if (special_columns.find(t.character()) != - special_columns.end()) { - ci2special(next); - next.special += t.character(); - int const nargs = - special_columns[t.character()]; - for (int i = 0; i < nargs; ++i) - next.special += '{' + - p.verbatim_item() + - '}'; - colinfo.push_back(next); - next = ColInfo(); - } else - cerr << "Ignoring column specification" - " '" << t << "'." << endl; + // unknown column types (nargs == -1) are + // assumed to consume no arguments + ci2special(next); + next.special += t.character(); + int const nargs = + preamble.getSpecialTableColumnArguments(t.character()); + for (int i = 0; i < nargs; ++i) + next.special += '{' + + p.verbatim_item() + '}'; + colinfo.push_back(next); + next = ColInfo(); break; } + } } // Maybe we have some column separators that need to be added to the @@ -519,14 +611,21 @@ bool parse_hlines(Parser & p, Token const & t, string & hlines, hlines += "\\cline{" + p.verbatim_item() + '}'; else if (t.cs() == "cmidrule") { - // We cannot handle the \cmidrule(l){3-4} form p.pushPosition(); p.skip_spaces(true); - bool const hasParentheses(p.getFullArg('(', ')').first); - p.popPosition(); - if (hasParentheses) + // We do not support the optional height argument + if (p.hasOpt()) return false; - hlines += "\\cmidrule{" + p.verbatim_item() + '}'; + // We support the \cmidrule(l){3-4} form but + // not the trim length parameters (l{}r{}) + string const trim = p.getFullParentheseArg(); + string const range = p.verbatim_item(); + if (!trim.empty()) { + if (support::contains(trim, "{")) + return false; + hlines += "\\cmidrule" + trim + "{" + range + "}"; + } else + hlines += "\\cmidrule{" + range + '}'; } else if (t.cs() == "addlinespace") { @@ -734,13 +833,13 @@ void parse_table(Parser & p, ostream & os, bool is_long_tabular, } } - else if (t.cat() == catSpace + else if (t.cat() == catSpace || t.cat() == catNewline - || t.cat() == catLetter - || t.cat() == catSuper - || t.cat() == catSub - || t.cat() == catOther - || t.cat() == catActive + || t.cat() == catLetter + || t.cat() == catSuper + || t.cat() == catSub + || t.cat() == catOther + || t.cat() == catActive || t.cat() == catParameter) os << t.cs(); @@ -786,7 +885,7 @@ void parse_table(Parser & p, ostream & os, bool is_long_tabular, // treat the nested environment as a block, don't // parse &, \\ etc, because they don't belong to our // table if they appear. - os << p.verbatimEnvironment(name); + os << p.ertEnvironment(name); os << "\\end{" << name << '}'; active_environments.pop_back(); } @@ -839,18 +938,15 @@ void handle_hline_below(RowInfo & ri, vector & ci) void handle_tabular(Parser & p, ostream & os, string const & name, - string const & tabularwidth, Context & context) + string const & tabularwidth, string const & halign, + Context & context) { - bool const is_long_tabular(name == "longtable"); + bool const is_long_tabular(name == "longtable" || name == "xltabular"); bool booktabs = false; string tabularvalignment("middle"); string posopts = p.getOpt(); if (!posopts.empty()) { - // FIXME: Convert this to ERT - if (is_long_tabular) - cerr << "horizontal longtable positioning '" - << posopts << "' ignored\n"; - else if (posopts == "[t]") + if (posopts == "[t]") tabularvalignment = "top"; else if (posopts == "[b]") tabularvalignment = "bottom"; @@ -911,7 +1007,7 @@ void handle_tabular(Parser & p, ostream & os, string const & name, for (int i = 0; i <= 2; i += 2) { //cerr << " reading from line string '" << dummy[i] << "'\n"; - Parser p1(dummy[i]); + Parser p1(dummy[size_type(i)]); while (p1.good()) { Token t = p1.get_token(); //cerr << "read token: " << t << "\n"; @@ -937,10 +1033,13 @@ void handle_tabular(Parser & p, ostream & os, string const & name, handle_hline_below(rowinfo[row], cellinfo[row]); } } else if (t.cs() == "cline" || t.cs() == "cmidrule") { - if (t.cs() == "cmidrule") + string trim; + if (t.cs() == "cmidrule") { booktabs = true; + trim = p1.getFullParentheseArg(); + } string arg = p1.verbatim_item(); - //cerr << "read " << t.cs() << " arg: '" << arg << "'\n"; + //cerr << "read " << t.cs() << " arg: '" << arg << "', trim: '" << trim << "'\n"; vector cols; split(arg, cols, '-'); cols.resize(2); @@ -977,9 +1076,25 @@ void handle_tabular(Parser & p, ostream & os, string const & name, if (i == 0) { rowinfo[row].topline = true; cellinfo[row][col].topline = true; + if (support::contains(trim, 'l') && col == from) { + //rowinfo[row].topline_ltrim = true; + cellinfo[row][col].topline_ltrim = true; + } + else if (support::contains(trim, 'r') && col == to) { + //rowinfo[row].topline_rtrim = true; + cellinfo[row][col].topline_rtrim = true; + } } else { rowinfo[row].bottomline = true; cellinfo[row][col].bottomline = true; + if (support::contains(trim, 'l') && col == from) { + //rowinfo[row].bottomline_ltrim = true; + cellinfo[row][col].bottomline_ltrim = true; + } + else if (support::contains(trim, 'r') && col == to) { + //rowinfo[row].bottomline_rtrim = true; + cellinfo[row][col].bottomline_rtrim = true; + } } } } else if (t.cs() == "addlinespace") { @@ -1122,19 +1237,74 @@ void handle_tabular(Parser & p, ostream & os, string const & name, << cells[cell] << "'." << endl; continue; } - Parser p(cells[cell]); - p.skip_spaces(); + string cellcont = cells[cell]; + // For decimal cells, ass the content of the second one to the first one + // of a pair. + if (colinfo[col].decimal_point != '\0' && colinfo[col].align == 'd' && cell < cells.size() - 1) + cellcont += colinfo[col].decimal_point + cells[cell + 1]; + Parser parse(cellcont); + parse.skip_spaces(); //cells[cell] << "'\n"; - if (p.next_token().cs() == "multicolumn") { + if (parse.next_token().cs() == "multirow") { + // We do not support the vpos arg yet. + if (parse.hasOpt()) { + string const vpos = parse.getArg('[', ']'); + parse.skip_spaces(true); + cerr << "Ignoring multirow's vpos arg '" + << vpos << "'!" << endl; + } // how many cells? - p.get_token(); + parse.get_token(); size_t const ncells = - convert(p.verbatim_item()); + convert(parse.verbatim_item()); + // We do not support the bigstrut arg yet. + if (parse.hasOpt()) { + string const bs = parse.getArg('[', ']'); + parse.skip_spaces(true); + cerr << "Ignoring multirow's bigstrut arg '" + << bs << "'!" << endl; + } + // the width argument + string const width = parse.getArg('{', '}'); + // the vmove arg + string vmove; + if (parse.hasOpt()) { + vmove = parse.getArg('[', ']'); + parse.skip_spaces(true); + } + + if (width != "*") + colinfo[col].width = width; + if (!vmove.empty()) + cellinfo[row][col].mroffset = vmove; + cellinfo[row][col].multi = CELL_BEGIN_OF_MULTIROW; + cellinfo[row][col].leftlines = colinfo[col].leftlines; + cellinfo[row][col].rightlines = colinfo[col].rightlines; + cellinfo[row][col].mrxnum = ncells - 1; + + ostringstream os2; + parse_text_in_inset(parse, os2, FLAG_ITEM, false, context); + if (!cellinfo[row][col].content.empty()) { + // This may or may not work in LaTeX, + // but it does not work in LyX. + // FIXME: Handle it correctly! + cerr << "Moving cell content '" + << cells[cell] + << "' into a multirow cell. " + "This will probably not work." + << endl; + } + cellinfo[row][col].content += os2.str(); + } else if (parse.next_token().cs() == "multicolumn") { + // how many cells? + parse.get_token(); + size_t const ncells = + convert(parse.verbatim_item()); // special cell properties alignment vector t; - handle_colalign(p, t, ColInfo()); - p.skip_spaces(true); + handle_colalign(parse, t, ColInfo()); + parse.skip_spaces(true); ColInfo & ci = t.front(); // The logic of LyX for multicolumn vertical @@ -1149,8 +1319,8 @@ void handle_tabular(Parser & p, ostream & os, string const & name, cellinfo[row][col].special = ci.special; cellinfo[row][col].leftlines = ci.leftlines; cellinfo[row][col].rightlines = ci.rightlines; - ostringstream os; - parse_text_in_inset(p, os, FLAG_ITEM, false, context); + ostringstream os2; + parse_text_in_inset(parse, os2, FLAG_ITEM, false, context); if (!cellinfo[row][col].content.empty()) { // This may or may not work in LaTeX, // but it does not work in LyX. @@ -1161,7 +1331,7 @@ void handle_tabular(Parser & p, ostream & os, string const & name, "This will probably not work." << endl; } - cellinfo[row][col].content += os.str(); + cellinfo[row][col].content += os2.str(); // add dummy cells for multicol for (size_t i = 0; i < ncells - 1; ++i) { @@ -1184,7 +1354,7 @@ void handle_tabular(Parser & p, ostream & os, string const & name, } else if (col == 0 && colinfo.size() > 1 && is_long_tabular && - p.next_token().cs() == "caption") { + parse.next_token().cs() == "caption") { // longtable caption support in LyX is a hack: // Captions require a row of their own with // the caption flag set to true, having only @@ -1207,51 +1377,51 @@ void handle_tabular(Parser & p, ostream & os, string const & name, cells.resize(1); cellinfo[row][col].align = colinfo[col].align; cellinfo[row][col].multi = CELL_BEGIN_OF_MULTICOLUMN; - ostringstream os; - parse_text_in_inset(p, os, FLAG_CELL, false, context); - cellinfo[row][col].content += os.str(); + ostringstream os2; + parse_text_in_inset(parse, os2, FLAG_CELL, false, context); + cellinfo[row][col].content += os2.str(); // add dummy multicolumn cells for (size_t c = 1; c < colinfo.size(); ++c) cellinfo[row][c].multi = CELL_PART_OF_MULTICOLUMN; } else { bool turn = false; int rotate = 0; - if (p.next_token().cs() == "begin") { - p.pushPosition(); - p.get_token(); - string const env = p.getArg('{', '}'); + if (parse.next_token().cs() == "begin") { + parse.pushPosition(); + parse.get_token(); + string const env = parse.getArg('{', '}'); if (env == "sideways" || env == "turn") { string angle = "90"; if (env == "turn") { turn = true; - angle = p.getArg('{', '}'); + angle = parse.getArg('{', '}'); } active_environments.push_back(env); - p.verbatimEnvironment(env); + parse.ertEnvironment(env); active_environments.pop_back(); - p.skip_spaces(); - if (!p.good() && support::isStrInt(angle)) + parse.skip_spaces(); + if (!parse.good() && support::isStrInt(angle)) rotate = convert(angle); } - p.popPosition(); + parse.popPosition(); } cellinfo[row][col].leftlines = colinfo[col].leftlines; cellinfo[row][col].rightlines = colinfo[col].rightlines; cellinfo[row][col].align = colinfo[col].align; - ostringstream os; + ostringstream os2; if (rotate != 0) { cellinfo[row][col].rotate = rotate; - p.get_token(); - active_environments.push_back(p.getArg('{', '}')); + parse.get_token(); + active_environments.push_back(parse.getArg('{', '}')); if (turn) - p.getArg('{', '}'); - parse_text_in_inset(p, os, FLAG_END, false, context); + parse.getArg('{', '}'); + parse_text_in_inset(parse, os2, FLAG_END, false, context); active_environments.pop_back(); preamble.registerAutomaticallyLoadedPackage("rotating"); } else { - parse_text_in_inset(p, os, FLAG_CELL, false, context); + parse_text_in_inset(parse, os2, FLAG_CELL, false, context); } - cellinfo[row][col].content += os.str(); + cellinfo[row][col].content += os2.str(); } } @@ -1274,13 +1444,23 @@ void handle_tabular(Parser & p, ostream & os, string const & name, // and cellinfo. // Unfortunately LyX has some limitations that we need to work around. - // Convert cells with special content to multicolumn cells - // (LyX ignores the special field for non-multicolumn cells). + // Some post work for (size_t row = 0; row < rowinfo.size(); ++row) { for (size_t col = 0; col < cellinfo[row].size(); ++col) { + // Convert cells with special content to multicolumn cells + // (LyX ignores the special field for non-multicolumn cells). if (cellinfo[row][col].multi == CELL_NORMAL && !cellinfo[row][col].special.empty()) cellinfo[row][col].multi = CELL_BEGIN_OF_MULTICOLUMN; + // Add multirow dummy cells + if (row > 1 && (cellinfo[row - 1][col].multi == CELL_PART_OF_MULTIROW + || cellinfo[row - 1][col].multi == CELL_BEGIN_OF_MULTIROW) + && cellinfo[row - 1][col].mrxnum > 0) { + // add dummy cells for multirow + cellinfo[row][col].multi = CELL_PART_OF_MULTIROW; + cellinfo[row][col].align = 'c'; + cellinfo[row][col].mrxnum = cellinfo[row - 1][col].mrxnum - 1; + } } } @@ -1310,22 +1490,40 @@ void handle_tabular(Parser & p, ostream & os, string const & name, cellinfo[row][col].rightlines = colinfo[col].rightlines; if (col > 0 && cellinfo[row][col-1].multi == CELL_NORMAL) cellinfo[row][col].leftlines = colinfo[col].leftlines; + } else if (cellinfo[row][col].multi == CELL_BEGIN_OF_MULTIROW) { + size_t s = row + 1; + while (s < rowinfo.size() && + cellinfo[s][col].multi == CELL_PART_OF_MULTIROW) + s++; + if (s < cellinfo[row].size() && + cellinfo[s][col].multi != CELL_BEGIN_OF_MULTIROW) + cellinfo[row][col].bottomline = rowinfo[row].bottomline; + if (row > 0 && cellinfo[row - 1][col].multi == CELL_NORMAL) + cellinfo[row][col].topline = rowinfo[row].topline; } } } if (booktabs) preamble.registerAutomaticallyLoadedPackage("booktabs"); - if (is_long_tabular) + if (name == "longtable") preamble.registerAutomaticallyLoadedPackage("longtable"); + else if (name == "xltabular") + preamble.registerAutomaticallyLoadedPackage("xltabular"); + else if (name == "tabularx") + preamble.registerAutomaticallyLoadedPackage("tabularx"); //cerr << "// output what we have\n"; // output what we have - string const rotate = "0"; + size_type cols = colinfo.size(); + for (size_t col = 0; col < colinfo.size(); ++col) { + if (colinfo[col].decimal_point != '\0' && colinfo[col].align != 'd') + --cols; + } os << "\n\n"; + << "\" columns=\"" << cols << "\">\n"; os << "\n"; + os << write_attribute("tabularvalignment", tabularvalignment); + + os << write_attribute("tabularwidth", tabularwidth) << ">\n"; //cerr << "// after header\n"; for (size_t col = 0; col < colinfo.size(); ++col) { + if (colinfo[col].decimal_point != '\0' && colinfo[col].align != 'd') + continue; os << "\n"; } //cerr << "// after cols\n"; @@ -1374,18 +1579,31 @@ void handle_tabular(Parser & p, ostream & os, string const & name, << ">\n"; for (size_t col = 0; col < colinfo.size(); ++col) { CellInfo const & cell = cellinfo[row][col]; + if (colinfo[col].decimal_point != '\0' && colinfo[col].align != 'd') + // These are the second columns in a salign pair. Skip. + continue; os << " 0) << write_attribute("rightline", cell.rightlines > 0) - << write_attribute("rotate", cell.rotate); + << write_attribute("rotate", cell.rotate) + << write_attribute("mroffset", cell.mroffset); //cerr << "\nrow: " << row << " col: " << col; //if (cell.topline) // cerr << " topline=\"true\"";