From f38816bf16fa27504e40e33beb9b20d8095c988a Mon Sep 17 00:00:00 2001 From: Juergen Spitzmueller Date: Sun, 24 Jun 2018 10:05:15 +0200 Subject: [PATCH] Add support for tabularx/xltabular I.e., variable width columns in single and multiple page tables Files format change. Fixes: #4154, #4155 --- development/FORMAT | 5 + lib/chkconfig.ltx | 2 + lib/doc/LaTeXConfig.lyx | 60 +- lib/lyx2lyx/lyx_2_4.py | 22 +- src/LaTeXFeatures.cpp | 2 + src/LyXAction.cpp | 3 +- src/frontends/qt4/GuiTabular.cpp | 35 +- src/frontends/qt4/ui/TabularUi.ui | 1491 +++++++++++++++-------------- src/insets/InsetTabular.cpp | 92 +- src/insets/InsetTabular.h | 8 + src/tex2lyx/Preamble.cpp | 4 +- src/tex2lyx/table.cpp | 51 +- src/tex2lyx/tex2lyx.h | 3 +- src/tex2lyx/text.cpp | 17 +- src/version.h | 4 +- 15 files changed, 1029 insertions(+), 770 deletions(-) diff --git a/development/FORMAT b/development/FORMAT index 170d0875e5..1a160e0502 100644 --- a/development/FORMAT +++ b/development/FORMAT @@ -7,6 +7,11 @@ changes happened in particular if possible. A good example would be ----------------------- +2018-06-23 Jürgen Spitzmüller + * format incremented to 554: Support tabularx and xltabular: + - add column flag "varwidth=true", which will output column type 'X' + with either tabularx or xltabular (for multi-page tables) environment. + 2018-05-21 Jürgen Spitzmüller * format incremented to 553: Support \cite command in tufte classes. This builds on an extension of the natbib CiteEngine by tufte. diff --git a/lib/chkconfig.ltx b/lib/chkconfig.ltx index 95a72ff7df..ec140bb8cd 100644 --- a/lib/chkconfig.ltx +++ b/lib/chkconfig.ltx @@ -377,6 +377,7 @@ \TestPackage{subscript} \TestPackage{Sweave} \TestPackage{tablefootnote} +\TestPackage{tabularx} \TestPackage{tcolorbox} \TestPackage{textcomp} \TestPackage{thswitch} @@ -394,6 +395,7 @@ \TestPackage{xcolor} \TestPackage[xetex.def]{xetex-def} \TestPackage{xkeyval} +\TestPackage{xltabular} % Packages used by LyX's documentation files \TestPackage{arydshln} diff --git a/lib/doc/LaTeXConfig.lyx b/lib/doc/LaTeXConfig.lyx index 51b1d9d4b5..341e273056 100644 --- a/lib/doc/LaTeXConfig.lyx +++ b/lib/doc/LaTeXConfig.lyx @@ -5309,7 +5309,7 @@ Notes: The package longtable \family default is needed by \SpecialChar LyX - to be able to output correctly multipage tables. + to output multi-page tables. \end_layout \begin_layout Subsection @@ -5341,6 +5341,35 @@ tablefootnote to be able to output footnotes in floating tables. \end_layout +\begin_layout Subsection +tabularx +\end_layout + +\begin_layout Description +Found: +\begin_inset Info +type "package" +arg "tabularx" +\end_inset + + +\end_layout + +\begin_layout Description +CTAN: +\family typewriter +macros/latex/required/tools/ +\end_layout + +\begin_layout Description +Notes: The package +\family sans +tabularx +\family default + is needed by \SpecialChar LyX + to output variable width columns for single-page tables. +\end_layout + \begin_layout Subsection textcomp \end_layout @@ -5402,6 +5431,35 @@ varioref the page of the referred label. \end_layout +\begin_layout Subsection +xltabular +\end_layout + +\begin_layout Description +Found: +\begin_inset Info +type "package" +arg "xltabular" +\end_inset + + +\end_layout + +\begin_layout Description +CTAN: +\family typewriter +macros/latex/contrib/xltabular/ +\end_layout + +\begin_layout Description +Notes: The package +\family sans +xltabular +\family default + is needed by \SpecialChar LyX + to output variable width columns for multi-page tables. +\end_layout + \begin_layout Section Paper layout packages \end_layout diff --git a/lib/lyx2lyx/lyx_2_4.py b/lib/lyx2lyx/lyx_2_4.py index 2b03ada0d8..32c6c33af4 100644 --- a/lib/lyx2lyx/lyx_2_4.py +++ b/lib/lyx2lyx/lyx_2_4.py @@ -327,6 +327,24 @@ def revert_tuftecite(document): i = j + 1 +def revert_stretchcolumn(document): + " We remove the column varwidth flags or everything else will become a mess. " + i = 0 + while True: + i = find_token(document.body, "\\begin_inset Tabular", i) + if i == -1: + return + j = find_end_of_inset(document.body, i + 1) + if j == -1: + document.warning("Malformed LyX document: Could not find end of tabular.") + continue + for k in range(i, j): + if re.search('^$', document.body[k]): + document.warning("Converting 'tabularx'/'xltabular' table to normal table.") + document.body[k] = document.body[k].replace(' varwidth="true"', '') + i = i + 1 + + ## # Conversion hub # @@ -341,10 +359,12 @@ convert = [ [550, [convert_fontenc]], [551, []], [552, []], - [553, []] + [553, []], + [554, []] ] revert = [ + [553, [revert_stretchcolumn]], [552, [revert_tuftecite]], [551, [revert_floatpclass, revert_floatalignment]], [550, [revert_nospellcheck]], diff --git a/src/LaTeXFeatures.cpp b/src/LaTeXFeatures.cpp index 17d7ae4047..dc961e2e4b 100644 --- a/src/LaTeXFeatures.cpp +++ b/src/LaTeXFeatures.cpp @@ -984,6 +984,8 @@ char const * simplefeatures[] = { "footnote", "tablefootnote", "afterpage", + "tabularx", + "xltabular" }; char const * bibliofeatures[] = { diff --git a/src/LyXAction.cpp b/src/LyXAction.cpp index 6518013450..73fe5cc44d 100644 --- a/src/LyXAction.cpp +++ b/src/LyXAction.cpp @@ -3744,7 +3744,8 @@ void LyXAction::init() set-special-column|set-special-multicolumn|set-special-multirow|\n toggle-booktabs|set-booktabs|unset-booktabs|set-top-space|set-bottom-space|\n set-interline-space|set-border-lines|tabular-valign-top|\n - tabular-valign-middle|tabular-valign-bottom|set-tabular-width\n + tabular-valign-middle|tabular-valign-bottom|set-tabular-width|\n + toggle-varwidth-column Various math-environment features are handled as well, e.g. add-vline-left/right for\n the Grid/Array environment.\n : additional argument for some commands, use debug mode to explore its values. diff --git a/src/frontends/qt4/GuiTabular.cpp b/src/frontends/qt4/GuiTabular.cpp index 71be308574..78c237d332 100644 --- a/src/frontends/qt4/GuiTabular.cpp +++ b/src/frontends/qt4/GuiTabular.cpp @@ -131,6 +131,8 @@ GuiTabular::GuiTabular(QWidget * parent) this, SLOT(checkEnabled())); connect(specialAlignmentED, SIGNAL(textEdited(const QString &)), this, SLOT(checkEnabled())); + connect(columnTypeCO, SIGNAL(activated(int)), + this, SLOT(checkEnabled())); connect(columnWidthED, SIGNAL(textEdited(const QString &)), this, SLOT(checkEnabled())); connect(columnWidthUnitLC, SIGNAL(selectionChanged(lyx::Length::UNIT)), @@ -205,15 +207,17 @@ void GuiTabular::enableWidgets() const { // if there is a LaTeX argument, the width and alignment will be overwritten // therefore disable them in this case - columnWidthED->setEnabled(specialAlignmentED->text().isEmpty()); - columnWidthUnitLC->setEnabled(specialAlignmentED->text().isEmpty()); + bool const fixed = specialAlignmentED->text().isEmpty() + && columnTypeCO->currentIndex() == 2; + columnWidthED->setEnabled(fixed); + columnWidthUnitLC->setEnabled(fixed); // if the column has a width, multirows are always left-aligned // therefore disable hAlignCB in this case hAlignCO->setEnabled(!(multirowCB->isChecked() && !widgetsToLength(columnWidthED, columnWidthUnitLC).empty()) && specialAlignmentED->text().isEmpty()); // decimal alignment is only possible for non-multicol and non-multirow cells - if ((multicolumnCB->isChecked() || multirowCB->isChecked()) + if ((multicolumnCB->isChecked() || multirowCB->isChecked() || columnTypeCO->currentIndex() == 1) && hAlignCO->findData(toqstr("decimal"))) hAlignCO->removeItem(hAlignCO->findData(toqstr("decimal"))); else if (!multicolumnCB->isChecked() && !multirowCB->isChecked() @@ -224,8 +228,7 @@ void GuiTabular::enableWidgets() const decimalPointED->setEnabled(dalign); decimalLA->setEnabled(dalign); - bool const setwidth = TableAlignCO->currentText() == qt_("Middle") - && !longTabularCB->isChecked(); + bool const setwidth = TableAlignCO->currentText() == qt_("Middle"); tabularWidthLA->setEnabled(setwidth); tabularWidthED->setEnabled(setwidth); tabularWidthUnitLC->setEnabled(setwidth); @@ -465,13 +468,20 @@ docstring GuiTabular::dialogToParams() const // this must be done before applying the column alignment // because its value influences the alignment of multirow cells string width = widgetsToLength(columnWidthED, columnWidthUnitLC); - if (width.empty()) + if (width.empty() || columnTypeCO->currentIndex() != 2) width = "0pt"; if (multicolumnCB->isChecked()) setParam(param_str, Tabular::SET_MPWIDTH, width); else setParam(param_str, Tabular::SET_PWIDTH, width); + bool const varwidth = specialAlignmentED->text().isEmpty() + && columnTypeCO->currentIndex() == 1; + if (varwidth) + setParam(param_str, Tabular::TOGGLE_VARWIDTH_COLUMN, "on"); + else + setParam(param_str, Tabular::TOGGLE_VARWIDTH_COLUMN, "off"); + // apply the column alignment // multirows inherit the alignment from the column; if a column width // is set, multirows are always left-aligned so that in this case @@ -604,7 +614,7 @@ docstring GuiTabular::dialogToParams() const // if (newpageCB->isChecked()) setParam(param_str, Tabular::SET_LTNEWPAGE); - else + else setParam(param_str, Tabular::UNSET_LTNEWPAGE); // if (captionStatusCB->isChecked()) @@ -782,14 +792,20 @@ void GuiTabular::paramsToDialog(Inset const * inset) Tabular::SET_SPECIAL_COLUMN); pwidth = getColumnPWidth(tabular, cell); } + bool const varwidth = tabular.column_info[tabular.cellColumn(cell)].varwidth; + if (varwidth) + columnTypeCO->setCurrentIndex(1); string colwidth; if (pwidth.zero() - && !(columnWidthED->hasFocus() && columnWidthED->text() == "0")) + && !(columnWidthED->hasFocus() && columnWidthED->text() == "0")) { columnWidthED->clear(); - else { + if (!varwidth) + columnTypeCO->setCurrentIndex(0); + } else { colwidth = pwidth.asString(); lengthToWidgets(columnWidthED, columnWidthUnitLC, colwidth, default_unit); + columnTypeCO->setCurrentIndex(2); } Length mroffset; if (multirow) @@ -1054,6 +1070,7 @@ bool GuiTabular::checkWidgets(bool readonly) const borders->setEnabled(false); tabularWidthUnitLC->setEnabled(false); columnWidthUnitLC->setEnabled(false); + columnTypeCO->setEnabled(false); multirowOffsetUnitLC->setEnabled(false); setBordersGB->setEnabled(false); allBordersGB->setEnabled(false); diff --git a/src/frontends/qt4/ui/TabularUi.ui b/src/frontends/qt4/ui/TabularUi.ui index 15af270497..c6c629504d 100644 --- a/src/frontends/qt4/ui/TabularUi.ui +++ b/src/frontends/qt4/ui/TabularUi.ui @@ -1,222 +1,149 @@ - + + TabularUi - - + + 0 0 - 497 - 427 + 726 + 585 - + - + false - - - 0 - - - - - - - Current cell: - - - - - - - - 0 - 0 - - - - - 40 - 32767 - - - - Qt::NoFocus - - - Current row position - - - - - - - - 0 - 0 - - - - - 40 - 32767 - - - - Qt::NoFocus - - - Current column position - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 20 - 20 - - - - - - - - - + + + + - + QTabWidget::Rounded - + 0 - - + + &Table Settings - - - - + + + + Row setting - - - - + + + + Merge cells of different rows - + M&ultirow - - - - - + + + + + - + &Vertical Offset: - + multirowOffsetED - - - + + + true - + Optional vertical offset - + - - + + - - - + + + Cell setting - - - + + + - - + + Rotate this cell by 90 degrees - + Rotate - - + + 51 20 - + rotation angle - + -180 - + 180 - + 5 - + 90 - - - degrees + + + de&grees - + rotateCellAngleSB - - + + Qt::Horizontal - + 13 41 @@ -229,75 +156,75 @@ - - - + + + true - + Table-wide settings - - - - + + + + W&idth: - + tabularWidthED - - + + - - + + - - - + + + Verti&cal alignment: - + TableAlignCO - - - + + + Vertical alignment of the table - + 1 - + Top - + Middle - + Bottom - - - + + + Qt::Horizontal - + QSizePolicy::Expanding - + 153 20 @@ -305,52 +232,52 @@ - - + + - - + + true - + Rotate the table by 90 degrees - + &Rotate - - + + 51 20 - + rotation angle - + -180 - + 180 - + 5 - + 90 - - + + degrees - + rotateTabularAngleSB @@ -360,394 +287,437 @@ - - - + + + Column settings - - - - - &Horizontal alignment: - - - hAlignCO - - - - - - - Horizontal alignment in column - - - - Justified - - - - - Left - - - - - Center - - - - - Right - - - - - At Decimal Separator - - - - - - - - Qt::Horizontal - - - - 4 - 20 - - - - - - - - - - false + + + + + + + <html><head/><body><p>Column width type:</p><p>* Text Length: Stretch to text width</p><p>* Variable: Adjust to match table width</p><p>* Custom: Fixed custom width</p></body></html> - - &Decimal separator: + + + Text length + + + + + Variable[[Width]] + + + + + Custom[[Width]] + + + + + + + + Horizontal alignment in column - - decimalPointED + + + Justified + + + + + Left + + + + + Center + + + + + Right + + + + + At Decimal Separator + + + + + + + + &Width: + + + columnTypeCO - - - - false + + + + Qt::Horizontal - - - 0 - 0 - - - + - 20 - 16777215 + 109 + 20 - - + + + + + + Hori&zontal alignment: - - - - - 32767 - - - Qt::AlignCenter + + hAlignCO - - - - - - &Width: - - - columnWidthED - - - - - - - - - true + + + + Specifies the vertical alignment of this cell in relation to the baseline of the row. - - Fixed width of the column + + + Top + + + + + Middle + + + + + Bottom + + + + + + + + &Vertical alignment in row: - - + + vAlignCO - - + + + + + + true + + + Custom width of the column + + + + + + + + + + + + + + + + + false + + + &Decimal separator: + + + decimalPointED + + + + + + + false + + + + 0 + 0 + + + + + 20 + 16777215 + + + + + + + + + + 32767 + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 78 + 20 + + + + + - - - + + + Qt::Horizontal - + - 109 + 31 20 - - - - &Vertical alignment in row: - - - vAlignCO - - - - - - - Specifies the vertical alignment of this cell in relation to the baseline of the row. - - - - Top - - - - - Middle - - - - - Bottom - - - - - - - + + + Merge cells of different columns - + Mu&lticolumn - - - - LaTe&X argument: - - - specialAlignmentED - - - - - - - Custom column format (LaTeX) - - - - - + + + + + + LaTe&X argument: + + + specialAlignmentED + + + + + + + Custom column format (LaTeX) + + + + + + + - - + + &Borders - - - - + + + + Set Borders - - - - - + + + + + 0 0 - + - - - + + + 0 0 0 - - - + + + 230 240 249 - - - + + + 255 255 255 - - - + + + 242 247 252 - - - + + + 115 120 124 - - - + + + 154 160 166 - - - + + + 0 0 0 - - - + + + 255 255 255 - - - + + + 0 0 0 - - - + + + 255 255 255 - - - + + + 255 255 255 - - - + + + 0 0 0 - - - + + + 16 145 210 - - - + + + 255 255 255 - - - + + + 0 0 255 - - - + + + 255 0 255 - - - + + + 232 232 232 @@ -756,153 +726,153 @@ - - - + + + 0 0 0 - - - + + + 230 240 249 - - - + + + 255 255 255 - - - + + + 255 255 255 - - - + + + 115 120 124 - - - + + + 154 160 166 - - - + + + 0 0 0 - - - + + + 255 255 255 - - - + + + 0 0 0 - - - + + + 255 255 255 - - - + + + 255 255 255 - - - + + + 0 0 0 - - - + + + 16 145 210 - - - + + + 255 255 255 - - - + + + 0 0 255 - - - + + + 255 0 255 - - - + + + 232 232 232 @@ -911,153 +881,153 @@ - - - + + + 128 128 128 - - - + + + 230 240 249 - - - + + + 255 255 255 - - - + + + 255 255 255 - - - + + + 115 120 124 - - - + + + 154 160 166 - - - + + + 0 0 0 - - - + + + 255 255 255 - - - + + + 128 128 128 - - - + + + 255 255 255 - - - + + + 255 255 255 - - - + + + 0 0 0 - - - + + + 16 145 210 - - - + + + 255 255 255 - - - + + + 0 0 255 - - - + + + 255 0 255 - - - + + + 232 232 232 @@ -1067,22 +1037,22 @@ - + QFrame::StyledPanel - + QFrame::Sunken - - - - - + + + + + 0 0 - + Set borders of the current (selected) cell(s) @@ -1093,28 +1063,28 @@ - - - + + + All Borders - - - - + + + + Set all borders of the current (selected) cell(s) - + &Set - - - + + + Unset all borders of the current (selected) cell(s) - + C&lear @@ -1122,31 +1092,31 @@ - - - + + + Style - - - - + + + + Use formal (a.k.a. booktabs) border style (no vertical borders) - + Fo&rmal - + true - - - + + + Use default (grid-like) border style - + De&fault @@ -1154,15 +1124,15 @@ - + - + Qt::Horizontal - + QSizePolicy::Expanding - + 63 20 @@ -1170,126 +1140,126 @@ - - - + + + Additional Space - - - - + + + + T&op of row: - + topspaceCO - - + + - - + + - - + + - + None - + Default - + Custom - - + + - + None - + Default - + Custom - - + + - - + + - - - + + + Botto&m of row: - + bottomspaceCO - - + + - - - + + + Bet&ween rows: - + interlinespaceCO - - + + - + None - + Default - + Custom - - + + - - - + + + Qt::Vertical - + 20 90 @@ -1299,231 +1269,231 @@ - - - &Multi-page table + + + &Multi-Page Table - - - - + + + + Select for tables that span multiple pages - + &Use multi-page table - - - + + + false - + Row settings - - - - + + + + Status - - - + + + Border above - - - + + + Border below - - - + + + Contents - - - + + + Header: - - - + + + Repeat this row as header on every (except the first) page - + on - - - + + + - + double - - - + + + double - - - + + + First header: - - - + + + This row is the header of the first page - + - + on - - - + + + double - - - + + + double - - - + + + Don't output the first header - + is empty - - - + + + Footer: - - - + + + Repeat this row as footer on every (except the last) page - + on - - - + + + double - - - + + + double - - - + + + Last footer: - - - + + + This row is the footer of the last page - + on - - - + + + double - - - + + + double - - - + + + Don't output the last footer - + is empty - - - + + + Caption: - - - + + + on - - - + + + false - + Set a page break on the current row - + Page &break on current row @@ -1531,38 +1501,38 @@ - - - + + + false - + Horizontal alignment of the multi-page table - + Multi-page table alignment - - - - + + + + Left - - - - Center + + + + &Center - + true - - - + + + Right @@ -1570,12 +1540,12 @@ - - - + + + Qt::Vertical - + 20 91 @@ -1587,6 +1557,77 @@ + + + + + + Current cell: + + + + + + + + 0 + 0 + + + + + 40 + 32767 + + + + Qt::NoFocus + + + Current row position + + + + + + + + 0 + 0 + + + + + 40 + 32767 + + + + Qt::NoFocus + + + Current column position + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 20 + 20 + + + + + + @@ -1655,7 +1696,7 @@ TabWidget - qt_i18n.h + qt_i18n.h diff --git a/src/insets/InsetTabular.cpp b/src/insets/InsetTabular.cpp index 67da03ec5c..0146f3ab0e 100644 --- a/src/insets/InsetTabular.cpp +++ b/src/insets/InsetTabular.cpp @@ -161,6 +161,7 @@ TabularFeature tabularFeature[] = { Tabular::UNSET_LONGTABULAR, "unset-longtabular", false }, { Tabular::SET_PWIDTH, "set-pwidth", true }, { Tabular::SET_MPWIDTH, "set-mpwidth", true }, + { Tabular::TOGGLE_VARWIDTH_COLUMN, "toggle-varwidth-column", true }, { Tabular::SET_ROTATE_TABULAR, "set-rotate-tabular", true }, { Tabular::UNSET_ROTATE_TABULAR, "unset-rotate-tabular", true }, { Tabular::TOGGLE_ROTATE_TABULAR, "toggle-rotate-tabular", true }, @@ -653,7 +654,8 @@ Tabular::RowData::RowData() Tabular::ColumnData::ColumnData() : alignment(LYX_ALIGN_CENTER), valignment(LYX_VALIGN_TOP), - width(0) + width(0), + varwidth(false) { } @@ -1206,6 +1208,13 @@ bool Tabular::setMColumnPWidth(Cursor & cur, idx_type cell, } +bool Tabular::toggleVarwidth(idx_type cell, bool const varwidth) +{ + column_info[cellColumn(cell)].varwidth = varwidth; + return true; +} + + bool Tabular::setMROffset(Cursor &, idx_type cell, Length const & mroffset) { cellInfo(cell).mroffset = mroffset; @@ -1496,6 +1505,7 @@ void Tabular::write(ostream & os) const os << write_attribute("decimal_point", column_info[c].decimal_point); os << write_attribute("valignment", column_info[c].valignment) << write_attribute("width", column_info[c].p_width.asString()) + << write_attribute("varwidth", column_info[c].varwidth) << write_attribute("special", column_info[c].align_special) << ">\n"; } @@ -1608,6 +1618,7 @@ void Tabular::read(Lexer & lex) getTokenValue(line, "valignment", column_info[c].valignment); getTokenValue(line, "width", column_info[c].p_width); getTokenValue(line, "special", column_info[c].align_special); + getTokenValue(line, "varwidth", column_info[c].varwidth); } for (row_type i = 0; i < nrows(); ++i) { @@ -1693,6 +1704,17 @@ bool Tabular::hasMultiColumn(col_type c) const } +bool Tabular::hasVarwidthColumn() const +{ + for (col_type c = 0; c < ncols(); ++c) { + if (column_info[c].varwidth) + return true; + } + return false; +} + + + Tabular::CellData const & Tabular::cellInfo(idx_type cell) const { return cell_info[cellRow(cell)][cellColumn(cell)]; @@ -2752,7 +2774,7 @@ void Tabular::TeXRow(otexstream & os, row_type row, void Tabular::latex(otexstream & os, OutputParams const & runparams) const { - bool const is_tabular_star = !tabular_width.zero(); + bool const is_tabular_star = !tabular_width.zero() && !hasVarwidthColumn(); TexRow::RowEntry pos = TexRow::textEntry(runparams.lastid, runparams.lastpos); //+--------------------------------------------------------------------- @@ -2771,7 +2793,10 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const } if (is_long_tabular) { - os << "\\begin{longtable}"; + if (hasVarwidthColumn()) + os << "\\begin{xltabular}"; + else + os << "\\begin{longtable}"; switch (longtabular_alignment) { case LYX_LONGTABULAR_ALIGN_LEFT: os << "[l]"; @@ -2783,10 +2808,22 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const os << "[r]"; break; } + if (hasVarwidthColumn()) { + if (tabular_width.zero()) + os << "{" << from_ascii("\\columnwidth") << "}"; + else + os << "{" << from_ascii(tabular_width.asLatexString()) << "}"; + } } else { if (is_tabular_star) os << "\\begin{tabular*}{" << from_ascii(tabular_width.asLatexString()) << "}"; - else + else if (hasVarwidthColumn()) { + os << "\\begin{tabularx}{"; + if (tabular_width.zero()) + os << from_ascii("\\columnwidth") << "}"; + else + os << from_ascii(tabular_width.asLatexString()) << "}"; + } else os << "\\begin{tabular}"; switch (tabular_valignment) { case LYX_VALIGN_TOP: @@ -2887,6 +2924,25 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const os << '{' << from_ascii(column_info[c].p_width.asLatexString()) << '}'; + } else if (column_info[c].varwidth) { + switch (column_info[c].alignment) { + case LYX_ALIGN_LEFT: + os << ">{\\raggedright\\arraybackslash}"; + break; + case LYX_ALIGN_RIGHT: + os << ">{\\raggedleft\\arraybackslash}"; + break; + case LYX_ALIGN_CENTER: + os << ">{\\centering\\arraybackslash}"; + break; + case LYX_ALIGN_NONE: + case LYX_ALIGN_BLOCK: + case LYX_ALIGN_LAYOUT: + case LYX_ALIGN_SPECIAL: + case LYX_ALIGN_DECIMAL: + break; + } + os << 'X'; } else { switch (column_info[c].alignment) { case LYX_ALIGN_LEFT: @@ -2927,11 +2983,16 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const //+ the closing of the tabular + //+--------------------------------------------------------------------- - if (is_long_tabular) - os << "\\end{longtable}"; - else { + if (is_long_tabular) { + if (hasVarwidthColumn()) + os << "\\end{xltabular}"; + else + os << "\\end{longtable}"; + } else { if (is_tabular_star) os << "\\end{tabular*}"; + else if (hasVarwidthColumn()) + os << "\\end{tabularx}"; else os << "\\end{tabular}"; } @@ -3484,12 +3545,18 @@ void Tabular::validate(LaTeXFeatures & features) const features.require("NeedTabularnewline"); if (use_booktabs) features.require("booktabs"); - if (is_long_tabular) + if (is_long_tabular && !hasVarwidthColumn()) features.require("longtable"); if (rotate && is_long_tabular) features.require("lscape"); if (needRotating()) features.require("rotating"); + if (hasVarwidthColumn()) { + if (is_long_tabular) + features.require("xltabular"); + else + features.require("tabularx"); + } for (idx_type cell = 0; cell < numberofcells; ++cell) { if (isMultiRow(cell)) features.require("multirow"); @@ -4619,6 +4686,7 @@ bool InsetTabular::getFeatureStatus(Cursor & cur, string const & s, switch (action) { case Tabular::SET_PWIDTH: case Tabular::SET_MPWIDTH: + case Tabular::TOGGLE_VARWIDTH_COLUMN: case Tabular::SET_SPECIAL_COLUMN: case Tabular::SET_SPECIAL_MULTICOLUMN: case Tabular::APPEND_ROW: @@ -4634,7 +4702,7 @@ bool InsetTabular::getFeatureStatus(Cursor & cur, string const & s, return true; case Tabular::SET_TABULAR_WIDTH: - status.setEnabled(!tabular.rotate && !tabular.is_long_tabular + status.setEnabled(!tabular.rotate && tabular.tabular_valignment == Tabular::LYX_VALIGN_MIDDLE); break; @@ -5597,6 +5665,12 @@ void InsetTabular::tabularFeatures(Cursor & cur, tabular.setMColumnPWidth(cur, cur.idx(), Length(value)); break; + case Tabular::TOGGLE_VARWIDTH_COLUMN: { + bool const varwidth = value == "on"; + tabular.toggleVarwidth(cur.idx(), varwidth); + break; + } + case Tabular::SET_MROFFSET: tabular.setMROffset(cur, cur.idx(), Length(value)); break; diff --git a/src/insets/InsetTabular.h b/src/insets/InsetTabular.h index 6a05ce3454..46f8cfd628 100644 --- a/src/insets/InsetTabular.h +++ b/src/insets/InsetTabular.h @@ -228,6 +228,8 @@ public: /// SET_MPWIDTH, /// + TOGGLE_VARWIDTH_COLUMN, + /// SET_ROTATE_TABULAR, /// UNSET_ROTATE_TABULAR, @@ -460,6 +462,8 @@ public: /// bool setMColumnPWidth(Cursor &, idx_type, Length const &); /// + bool toggleVarwidth(idx_type, bool const); + /// bool setMROffset(Cursor &, idx_type, Length const &); /// void setAlignSpecial(idx_type cell, docstring const & special, @@ -526,6 +530,8 @@ public: /// bool hasMultiColumn(col_type cell) const; /// + bool hasVarwidthColumn() const; + /// idx_type setMultiColumn(idx_type cell, idx_type number, bool const right_border); /// @@ -741,6 +747,8 @@ public: docstring align_special; /// docstring decimal_point; + /// + bool varwidth; }; /// typedef std::vector column_vector; diff --git a/src/tex2lyx/Preamble.cpp b/src/tex2lyx/Preamble.cpp index 8517b0fe09..fa3fb1699d 100644 --- a/src/tex2lyx/Preamble.cpp +++ b/src/tex2lyx/Preamble.cpp @@ -206,8 +206,8 @@ const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb", "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color", "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable", "makeidx", "minted", "nomencl", "pdfpages", "prettyref", "refstyle", -"rotating", "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", -"tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", +"rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa", +"tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular", "xunicode", 0}; // codes used to remove packages that are loaded automatically by LyX. diff --git a/src/tex2lyx/table.cpp b/src/tex2lyx/table.cpp index 0344d54bc4..6f2563231d 100644 --- a/src/tex2lyx/table.cpp +++ b/src/tex2lyx/table.cpp @@ -37,7 +37,7 @@ namespace { class ColInfo { public: - ColInfo() : align('n'), valign('n'), rightlines(0), leftlines(0) {} + ColInfo() : align('n'), valign('n'), rightlines(0), leftlines(0), varwidth(false) {} /// column alignment char align; /// vertical alignment @@ -50,6 +50,8 @@ public: int rightlines; /// number of lines on the left int leftlines; + /// varwidth column + bool varwidth; }; @@ -257,18 +259,23 @@ void ci2special(ColInfo & ci) return; 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.varwidth) + ci.special += 'X'; + else if (ci.valign == 'n') ci.special += 'p'; else ci.special += ci.valign; @@ -335,6 +342,14 @@ void handle_colalign(Parser & p, vector & colinfo, 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 'p': case 'b': case 'm': @@ -368,11 +383,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 + '}'; @@ -837,18 +852,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"; @@ -1314,8 +1326,12 @@ void handle_tabular(Parser & p, ostream & os, string const & name, 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 @@ -1337,6 +1353,8 @@ void handle_tabular(Parser & p, ostream & os, string const & name, << write_attribute("lastFootTopDL", endlastfoot.topDL) << write_attribute("lastFootBottomDL", endlastfoot.bottomDL) << write_attribute("lastFootEmpty", endlastfoot.empty); + if (!halign.empty()) + os << write_attribute("longtabularalignment", halign); } else os << write_attribute("tabularvalignment", tabularvalignment) << write_attribute("tabularwidth", tabularwidth); @@ -1350,6 +1368,7 @@ void handle_tabular(Parser & p, ostream & os, string const & name, << verbose_valign(colinfo[col].valign) << "\"" << write_attribute("width", translate_len(colinfo[col].width)) << write_attribute("special", colinfo[col].special) + << write_attribute("varwidth", colinfo[col].varwidth) << ">\n"; } //cerr << "// after cols\n"; diff --git a/src/tex2lyx/tex2lyx.h b/src/tex2lyx/tex2lyx.h index 0ac2b3bfbc..0591be7c74 100644 --- a/src/tex2lyx/tex2lyx.h +++ b/src/tex2lyx/tex2lyx.h @@ -81,7 +81,8 @@ void parse_math(Parser & p, std::ostream & os, unsigned flags, mode_type mode); /// in table.cpp void handle_tabular(Parser & p, std::ostream & os, std::string const & name, - std::string const & width, Context & context); + std::string const & width, std::string const & halign, + Context & context); /// in tex2lyx.cpp diff --git a/src/tex2lyx/text.cpp b/src/tex2lyx/text.cpp index a3214af41c..55d2d9fe7c 100644 --- a/src/tex2lyx/text.cpp +++ b/src/tex2lyx/text.cpp @@ -1614,16 +1614,27 @@ void parse_environment(Parser & p, ostream & os, bool outer, p.skip_spaces(); } - else if (unstarred_name == "tabular" || name == "longtable") { + else if (unstarred_name == "tabular" || name == "longtable" + || name == "tabularx" || name == "xltabular") { eat_whitespace(p, os, parent_context, false); string width = "0pt"; - if (name == "tabular*") { + string halign; + if ((name == "longtable" || name == "xltabular") && p.hasOpt()) { + string const opt = p.getArg('[', ']'); + if (opt == "c") + halign = "center"; + else if (opt == "l") + halign = "left"; + else if (opt == "r") + halign = "right"; + } + if (name == "tabular*" || name == "tabularx" || name == "xltabular") { width = lyx::translate_len(p.getArg('{', '}')); eat_whitespace(p, os, parent_context, false); } parent_context.check_layout(os); begin_inset(os, "Tabular "); - handle_tabular(p, os, name, width, parent_context); + handle_tabular(p, os, name, width, halign, parent_context); end_inset(os); p.skip_spaces(); } diff --git a/src/version.h b/src/version.h index 1b93a17477..1020e3ef55 100644 --- a/src/version.h +++ b/src/version.h @@ -32,8 +32,8 @@ extern char const * const lyx_version_info; // Do not remove the comment below, so we get merge conflict in // independent branches. Instead add your own. -#define LYX_FORMAT_LYX 553 // spitz: tufte \cite -#define LYX_FORMAT_TEX2LYX 553 +#define LYX_FORMAT_LYX 554 // spitz: tabularx/xltabular +#define LYX_FORMAT_TEX2LYX 554 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX #ifndef _MSC_VER