X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Finsets%2FInsetTabular.cpp;h=69f9e73e59f0f0f11e125f2c80e4ba5449048455;hb=8124e6c02ea1fd6779bb6c47ffe2bca2c8bd2d97;hp=4dcb1c2bbaacb4cba263684c3ac60f250e82b164;hpb=5cb89a8f818537cf997625c7c7fd7b5399a677a6;p=lyx.git diff --git a/src/insets/InsetTabular.cpp b/src/insets/InsetTabular.cpp index 4dcb1c2bba..69f9e73e59 100644 --- a/src/insets/InsetTabular.cpp +++ b/src/insets/InsetTabular.cpp @@ -22,6 +22,7 @@ #include "InsetTabular.h" +#include "Author.h" #include "buffer_funcs.h" #include "Buffer.h" #include "BufferParams.h" @@ -41,10 +42,9 @@ #include "LyX.h" #include "LyXRC.h" #include "MetricsInfo.h" -#include "OutputParams.h" +#include "xml.h" #include "output_xhtml.h" #include "Paragraph.h" -#include "ParagraphParameters.h" #include "ParIterator.h" #include "TexRow.h" #include "texstream.h" @@ -57,6 +57,7 @@ #include "frontends/Painter.h" #include "frontends/Selection.h" +#include "support/Changer.h" #include "support/convert.h" #include "support/debug.h" #include "support/docstream.h" @@ -163,6 +164,7 @@ TabularFeature tabularFeature[] = { Tabular::UNSET_MULTIROW, "unset-multirow", false }, { Tabular::SET_MROFFSET, "set-mroffset", true }, { Tabular::SET_ALL_LINES, "set-all-lines", false }, + { Tabular::TOGGLE_ALL_LINES, "toggle-all-lines", false }, { Tabular::RESET_FORMAL_DEFAULT, "reset-formal-default", false }, { Tabular::UNSET_ALL_LINES, "unset-all-lines", false }, { Tabular::TOGGLE_LONGTABULAR, "toggle-longtabular", false }, @@ -200,6 +202,7 @@ TabularFeature tabularFeature[] = { Tabular::SET_BOTTOM_SPACE, "set-bottom-space", true }, { Tabular::SET_INTERLINE_SPACE, "set-interline-space", true }, { Tabular::SET_BORDER_LINES, "set-border-lines", false }, + { Tabular::TOGGLE_BORDER_LINES, "toggle-border-lines", false }, { Tabular::TABULAR_VALIGN_TOP, "tabular-valign-top", false}, { Tabular::TABULAR_VALIGN_MIDDLE, "tabular-valign-middle", false}, { Tabular::TABULAR_VALIGN_BOTTOM, "tabular-valign-bottom", false}, @@ -209,6 +212,7 @@ TabularFeature tabularFeature[] = { Tabular::SET_DECIMAL_POINT, "set-decimal-point", true }, { Tabular::SET_TABULAR_WIDTH, "set-tabular-width", true }, { Tabular::SET_INNER_LINES, "set-inner-lines", false }, + { Tabular::TOGGLE_INNER_LINES, "toggle-inner-lines", false }, { Tabular::LAST_ACTION, "", false } }; @@ -559,7 +563,7 @@ string const write_attribute(string const & name, int const & i) template <> -string const write_attribute(string const & name, Tabular::idx_type const & i) +string const write_attribute(string const & name, idx_type const & i) { // we write only true attribute values so we remove a bit of the // file format bloat for tabulars. @@ -616,9 +620,9 @@ DocIterator separatorPos(InsetTableCell const * cell, docstring const & align_d) InsetTableCell splitCell(InsetTableCell & head, docstring const & align_d, bool & hassep) { - InsetTableCell tail = InsetTableCell(head); + InsetTableCell tail = head; DocIterator const dit = separatorPos(&head, align_d); - hassep = (bool)dit; + hassep = static_cast(dit); if (hassep) { pos_type const psize = head.paragraphs().front().size(); head.paragraphs().front().eraseChars(dit.pos(), psize, false); @@ -811,7 +815,7 @@ void Tabular::deleteRow(row_type const row, bool const force) if (row + 1 < nrows() && cell_info[row][c].multirow == CELL_BEGIN_OF_MULTIROW && cell_info[row + 1][c].multirow == CELL_PART_OF_MULTIROW) { - cell_info[row + 1][c].multirow = CELL_BEGIN_OF_MULTIROW; + cell_info[row + 1][c] = cell_info[row][c]; } } if (ct) @@ -838,13 +842,13 @@ void Tabular::appendRow(row_type row) void Tabular::insertRow(row_type const row, bool copy) { - row_info.insert(row_info.begin() + row + 1, RowData(row_info[row])); + row_info.insert(row_info.begin() + row + 1, row_info[row]); cell_info.insert(cell_info.begin() + row + 1, cell_vector(0, CellData(buffer_))); for (col_type c = 0; c < ncols(); ++c) { cell_info[row + 1].insert(cell_info[row + 1].begin() + c, - copy ? CellData(cell_info[row][c]) : CellData(buffer_)); + copy ? cell_info[row][c] : CellData(buffer_)); if (cell_info[row][c].multirow == CELL_BEGIN_OF_MULTIROW) cell_info[row + 1][c].multirow = CELL_PART_OF_MULTIROW; } @@ -871,49 +875,92 @@ void Tabular::insertRow(row_type const row, bool copy) } -void Tabular::moveColumn(col_type col, ColDirection direction) +void Tabular::moveColumn(col_type col_start, col_type col_end, + ColDirection direction) { - if (direction == Tabular::LEFT) - col = col - 1; - - std::swap(column_info[col], column_info[col + 1]); - - for (row_type r = 0; r < nrows(); ++r) { - std::swap(cell_info[r][col], cell_info[r][col + 1]); - std::swap(cell_info[r][col].left_line, cell_info[r][col + 1].left_line); - std::swap(cell_info[r][col].right_line, cell_info[r][col + 1].right_line); - - idx_type const i = cellIndex(r, col); - idx_type const j = cellIndex(r, col + 1); - if (buffer().params().track_changes) { - cellInfo(i).inset->setChange(Change(Change::INSERTED)); - cellInfo(j).inset->setChange(Change(Change::INSERTED)); + if (direction == Tabular::LEFT) { + for (col_type col = col_start; col <= col_end; ++col) { + std::swap(column_info[col - 1], column_info[col]); + for (row_type r = 0; r < nrows(); ++r) { + std::swap(cell_info[r][col - 1], cell_info[r][col]); + std::swap(cell_info[r][col - 1].left_line, cell_info[r][col].left_line); + std::swap(cell_info[r][col - 1].right_line, cell_info[r][col].right_line); + + if (buffer().params().track_changes) { + idx_type const i = cellIndex(r, col - 1); + idx_type const j = cellIndex(r, col); + cellInfo(i).inset->setChange(Change(Change::INSERTED)); + cellInfo(j).inset->setChange(Change(Change::INSERTED)); + } + } + updateIndexes(); + if (col == ncols()) + break; + } + } else { + for (col_type col = col_end; col >= col_start; --col) { + std::swap(column_info[col], column_info[col + 1]); + for (row_type r = 0; r < nrows(); ++r) { + std::swap(cell_info[r][col], cell_info[r][col + 1]); + std::swap(cell_info[r][col].left_line, cell_info[r][col + 1].left_line); + std::swap(cell_info[r][col].right_line, cell_info[r][col + 1].right_line); + + if (buffer().params().track_changes) { + idx_type const i = cellIndex(r, col); + idx_type const j = cellIndex(r, col + 1); + cellInfo(i).inset->setChange(Change(Change::INSERTED)); + cellInfo(j).inset->setChange(Change(Change::INSERTED)); + } + } + updateIndexes(); + if (col == 0) + break; } } - updateIndexes(); } -void Tabular::moveRow(row_type row, RowDirection direction) +void Tabular::moveRow(row_type row_start, row_type row_end, RowDirection direction) { - if (direction == Tabular::UP) - row = row - 1; - - std::swap(row_info[row], row_info[row + 1]); - - for (col_type c = 0; c < ncols(); ++c) { - std::swap(cell_info[row][c], cell_info[row + 1][c]); - std::swap(cell_info[row][c].top_line, cell_info[row + 1][c].top_line); - std::swap(cell_info[row][c].bottom_line, cell_info[row + 1][c].bottom_line); - - idx_type const i = cellIndex(row, c); - idx_type const j = cellIndex(row + 1, c); - if (buffer().params().track_changes) { - cellInfo(i).inset->setChange(Change(Change::INSERTED)); - cellInfo(j).inset->setChange(Change(Change::INSERTED)); + if (direction == Tabular::UP) { + for (row_type row = row_start; row <= row_end; ++row) { + std::swap(row_info[row - 1], row_info[row]); + for (col_type c = 0; c < ncols(); ++c) { + std::swap(cell_info[row - 1][c], cell_info[row][c]); + std::swap(cell_info[row - 1][c].top_line, cell_info[row][c].top_line); + std::swap(cell_info[row - 1][c].bottom_line, cell_info[row][c].bottom_line); + + idx_type const i = cellIndex(row - 1, c); + idx_type const j = cellIndex(row, c); + if (buffer().params().track_changes) { + cellInfo(i).inset->setChange(Change(Change::INSERTED)); + cellInfo(j).inset->setChange(Change(Change::INSERTED)); + } + } + updateIndexes(); + if (row == nrows()) + break; + } + } else { + for (row_type row = row_end; row >= row_start; --row) { + std::swap(row_info[row], row_info[row + 1]); + for (col_type c = 0; c < ncols(); ++c) { + std::swap(cell_info[row][c], cell_info[row + 1][c]); + std::swap(cell_info[row][c].top_line, cell_info[row + 1][c].top_line); + std::swap(cell_info[row][c].bottom_line, cell_info[row + 1][c].bottom_line); + + idx_type const i = cellIndex(row, c); + idx_type const j = cellIndex(row + 1, c); + if (buffer().params().track_changes) { + cellInfo(i).inset->setChange(Change(Change::INSERTED)); + cellInfo(j).inset->setChange(Change(Change::INSERTED)); + } + } + updateIndexes(); + if (row == 0) + break; } } - updateIndexes(); } @@ -930,7 +977,7 @@ void Tabular::deleteColumn(col_type const col, bool const force) if (col + 1 < ncols() && cell_info[r][col].multicolumn == CELL_BEGIN_OF_MULTICOLUMN && cell_info[r][col + 1].multicolumn == CELL_PART_OF_MULTICOLUMN) { - cell_info[r][col + 1].multicolumn = CELL_BEGIN_OF_MULTICOLUMN; + cell_info[r][col + 1] = cell_info[r][col]; } if (!ct) cell_info[r].erase(cell_info[r].begin() + col); @@ -958,11 +1005,11 @@ void Tabular::appendColumn(col_type col) void Tabular::insertColumn(col_type const col, bool copy) { bool const ct = buffer().params().track_changes; - column_info.insert(column_info.begin() + col + 1, ColumnData(column_info[col])); + column_info.insert(column_info.begin() + col + 1, column_info[col]); for (row_type r = 0; r < nrows(); ++r) { cell_info[r].insert(cell_info[r].begin() + col + 1, - copy ? CellData(cell_info[r][col]) : CellData(buffer_)); + copy ? cell_info[r][col] : CellData(buffer_)); if (cell_info[r][col].multicolumn == CELL_BEGIN_OF_MULTICOLUMN) cell_info[r][col + 1].multicolumn = CELL_PART_OF_MULTICOLUMN; } @@ -1039,7 +1086,7 @@ void Tabular::updateIndexes() } -Tabular::idx_type Tabular::numberOfCellsInRow(row_type const row) const +idx_type Tabular::numberOfCellsInRow(row_type const row) const { idx_type result = 0; for (col_type c = 0; c < ncols(); ++c) @@ -1077,6 +1124,76 @@ bool Tabular::rightLine(idx_type cell, bool const ignore_bt) const } +bool Tabular::outsideBorders( + row_type const sel_row_start, row_type const sel_row_end, + col_type const sel_col_start, col_type const sel_col_end) const +{ + if (!use_booktabs) + for (row_type r = sel_row_start; r <= sel_row_end; ++r) { + if (!leftLine(cellIndex(r, sel_col_start)) + || !rightLine(cellIndex(r, sel_col_end))) + return false; + } + for (col_type c = sel_col_start; c <= sel_col_end; ++c) { + if (!topLine(cellIndex(sel_row_start, c)) + || !bottomLine(cellIndex(sel_row_end, c))) + return false; + } + return true; +} + + +bool Tabular::innerBorders( + row_type const sel_row_start, row_type const sel_row_end, + col_type const sel_col_start, col_type const sel_col_end) const +{ + // Single cell has no inner borders + if (sel_row_start == sel_row_end && sel_col_start == sel_col_end) + return false; + for (row_type r = sel_row_start; r <= sel_row_end; ++r) + for (col_type c = sel_col_start; c <= sel_col_end; ++c) { + idx_type const cell = cellIndex(r, c); + if ((r != sel_row_start && !topLine(cell) + && cell_info[r][c].multirow != CELL_PART_OF_MULTIROW) + || (!use_booktabs + && c != sel_col_start && !leftLine(cell) + && cell_info[r][c].multicolumn != CELL_PART_OF_MULTICOLUMN)) + return false; + } + return true; +} + + +void Tabular::setLines( + row_type const sel_row_start, row_type const sel_row_end, + col_type const sel_col_start, col_type const sel_col_end, + bool setLinesInnerOnly, bool setLines) +{ + for (row_type r = sel_row_start; r <= sel_row_end; ++r) + for (col_type c = sel_col_start; c <= sel_col_end; ++c) { + idx_type const cell = cellIndex(r, c); + if (!(setLinesInnerOnly && r == sel_row_start) + // for multirows, cell is taken care of at beginning + && cell_info[r][c].multirow != CELL_PART_OF_MULTIROW) + setTopLine(cell, setLines); + if (!(setLinesInnerOnly && r == sel_row_end) + && (r == sel_row_end || (!setLines + // for multirows, cell is taken care of at the last part + && cell_info[r + 1][c].multirow != CELL_PART_OF_MULTIROW))) + setBottomLine(cell, setLines); + if (!(setLinesInnerOnly && c == sel_col_start) + // for multicolumns, cell is taken care of at beginning + && cell_info[r][c].multicolumn != CELL_PART_OF_MULTICOLUMN) + setLeftLine(cell, setLines); + if (!(setLinesInnerOnly && c == sel_col_end) + && (c == sel_col_end || (!setLines + // for multicolumns, cell is taken care of at the last part + && cell_info[r][c + 1].multicolumn != CELL_PART_OF_MULTICOLUMN))) + setRightLine(cell, setLines); + } +} + + pair Tabular::topLineTrim(idx_type const cell) const { if (!use_booktabs) @@ -1175,7 +1292,7 @@ bool Tabular::updateColumnWidths(MetricsInfo & mi) if (!tab_width.zero()) { restwidth = mi.base.inPixels(tab_width); // Subtract the fixed widths from the table width - for (auto const w : max_pwidth) + for (auto const & w : max_pwidth) restwidth -= w.second; } @@ -1189,7 +1306,7 @@ bool Tabular::updateColumnWidths(MetricsInfo & mi) // Now consider that some variable width columns exceed the vcolwidth if (vcolwidth > 0) { bool changed = false; - for (auto const w : max_width) { + for (auto const & w : max_width) { if (tabularx || w.second > vcolwidth) { --restcols; restwidth -= w.second; @@ -1310,36 +1427,18 @@ void Tabular::setVAlignment(idx_type cell, VAlignment align, namespace { /** - * Allow line and paragraph breaks for fixed width multicol/multirow cells + * Allow line and paragraph breaks for fixed width multirow cells * or disallow them, merge cell paragraphs and reset layout to standard * for variable width multicol cells. */ void toggleFixedWidth(Cursor & cur, InsetTableCell * inset, - bool const fixedWidth, bool const multicol, - bool const multirow) + bool const fixedWidth, bool const multirow) { inset->toggleFixedWidth(fixedWidth); - if (!multirow && (fixedWidth || !multicol)) + if (!multirow) return; - // merge all paragraphs to one BufferParams const & bp = cur.bv().buffer().params(); - while (inset->paragraphs().size() > 1) - mergeParagraph(bp, inset->paragraphs(), 0); - - // This is relevant for multirows - if (fixedWidth) - return; - - // remove newlines - ParagraphList::iterator pit = inset->paragraphs().begin(); - for (; pit != inset->paragraphs().end(); ++pit) { - for (pos_type j = 0; j != pit->size(); ++j) { - if (pit->isNewline(j)) - pit->eraseChar(j, bp.track_changes); - } - } - // reset layout cur.push(*inset); // undo information has already been recorded @@ -1366,10 +1465,10 @@ void Tabular::setColumnPWidth(Cursor & cur, idx_type cell, idx_type const cidx = cellIndex(r, c); // because of multicolumns toggleFixedWidth(cur, cellInset(cidx).get(), - !getPWidth(cidx).zero(), isMultiColumn(cidx), - isMultiRow(cidx)); + !getPWidth(cidx).zero(), isMultiRow(cidx)); if (isMultiRow(cidx)) - setAlignment(cidx, LYX_ALIGN_LEFT, false); + setAlignment(cidx, column_info[c].alignment, + !column_info[c].p_width.zero()); } // cur can become invalid after paragraphs were merged cur.fixIfBroken(); @@ -1394,7 +1493,7 @@ bool Tabular::setMColumnPWidth(Cursor & cur, idx_type cell, cellInfo(cell).p_width = width; toggleFixedWidth(cur, cellInset(cell).get(), !width.zero(), - isMultiColumn(cell), isMultiRow(cell)); + isMultiRow(cell)); // cur can become invalid after paragraphs were merged cur.fixIfBroken(); return true; @@ -1404,6 +1503,7 @@ bool Tabular::setMColumnPWidth(Cursor & cur, idx_type cell, bool Tabular::toggleVarwidth(idx_type cell, bool const varwidth) { column_info[cellColumn(cell)].varwidth = varwidth; + cellInset(cell).get()->toggleVarWidth(varwidth); return true; } @@ -1647,7 +1747,7 @@ int Tabular::textVOffset(idx_type cell) const } -Tabular::idx_type Tabular::getFirstCellInRow(row_type row, bool const ct) const +idx_type Tabular::getFirstCellInRow(row_type row, bool const ct) const { col_type c = 0; idx_type const numcells = numberOfCellsInRow(row); @@ -1663,7 +1763,7 @@ Tabular::idx_type Tabular::getFirstCellInRow(row_type row, bool const ct) const } -Tabular::idx_type Tabular::getLastCellInRow(row_type row, bool const ct) const +idx_type Tabular::getLastCellInRow(row_type row, bool const ct) const { col_type c = ncols() - 1; // of course we check against 0 so we don't crash. but we have the same @@ -1678,7 +1778,7 @@ Tabular::idx_type Tabular::getLastCellInRow(row_type row, bool const ct) const } -Tabular::row_type Tabular::getFirstRow(bool const ct) const +row_type Tabular::getFirstRow(bool const ct) const { row_type r = 0; if (!ct) @@ -1690,7 +1790,7 @@ Tabular::row_type Tabular::getFirstRow(bool const ct) const } -Tabular::row_type Tabular::getLastRow(bool const ct) const +row_type Tabular::getLastRow(bool const ct) const { row_type r = nrows() - 1; if (!ct) @@ -1702,7 +1802,7 @@ Tabular::row_type Tabular::getLastRow(bool const ct) const } -Tabular::row_type Tabular::cellRow(idx_type cell) const +row_type Tabular::cellRow(idx_type cell) const { if (cell >= numberofcells) return nrows() - 1; @@ -1712,7 +1812,7 @@ Tabular::row_type Tabular::cellRow(idx_type cell) const } -Tabular::col_type Tabular::cellColumn(idx_type cell) const +col_type Tabular::cellColumn(idx_type cell) const { if (cell >= numberofcells) return ncols() - 1; @@ -1984,7 +2084,9 @@ bool Tabular::isVTypeColumn(col_type c) const { for (row_type r = 0; r < nrows(); ++r) { idx_type idx = cellIndex(r, c); - if (getRotateCell(idx) == 0 && useBox(idx) == BOX_VARWIDTH) + if (getRotateCell(idx) == 0 && useBox(idx) == BOX_VARWIDTH + && getAlignment(idx) == LYX_ALIGN_LEFT + && getVAlignment(idx) == LYX_VALIGN_TOP) return true; } return false; @@ -2003,7 +2105,7 @@ Tabular::CellData & Tabular::cellInfo(idx_type cell) } -Tabular::idx_type Tabular::setMultiColumn(Cursor & cur, idx_type cell, idx_type number, +idx_type Tabular::setMultiColumn(Cursor & cur, idx_type cell, idx_type number, bool const right_border) { idx_type const col = cellColumn(cell); @@ -2021,8 +2123,7 @@ Tabular::idx_type Tabular::setMultiColumn(Cursor & cur, idx_type cell, idx_type // non-fixed width multicolumns cannot have multiple paragraphs if (getPWidth(cell).zero()) { toggleFixedWidth(cur, cellInset(cell).get(), - !getPWidth(cell).zero(), isMultiColumn(cell), - isMultiRow(cell)); + !getPWidth(cell).zero(), isMultiRow(cell)); // cur can become invalid after paragraphs were merged cur.fixIfBroken(); } @@ -2054,7 +2155,7 @@ bool Tabular::hasMultiRow(row_type r) const return false; } -Tabular::idx_type Tabular::setMultiRow(Cursor & cur, idx_type cell, idx_type number, +idx_type Tabular::setMultiRow(Cursor & cur, idx_type cell, idx_type number, bool const bottom_border, LyXAlignment const halign) { @@ -2081,7 +2182,7 @@ Tabular::idx_type Tabular::setMultiRow(Cursor & cur, idx_type cell, idx_type num if (getPWidth(cell).zero()) { toggleFixedWidth(cur, cellInset(cell).get(), !getPWidth(cell).zero(), - isMultiColumn(cell), isMultiRow(cell)); + isMultiRow(cell)); // cur can become invalid after paragraphs were merged cur.fixIfBroken(); } @@ -2100,7 +2201,7 @@ Tabular::idx_type Tabular::setMultiRow(Cursor & cur, idx_type cell, idx_type num } -Tabular::idx_type Tabular::columnSpan(idx_type cell) const +idx_type Tabular::columnSpan(idx_type cell) const { row_type const row = cellRow(cell); col_type const col = cellColumn(cell); @@ -2112,7 +2213,7 @@ Tabular::idx_type Tabular::columnSpan(idx_type cell) const } -Tabular::idx_type Tabular::rowSpan(idx_type cell) const +idx_type Tabular::rowSpan(idx_type cell) const { col_type const column = cellColumn(cell); col_type row = cellRow(cell) + 1; @@ -2188,13 +2289,11 @@ bool Tabular::needRotating() const bool Tabular::isLastCell(idx_type cell) const { - if (cell + 1 < numberofcells) - return false; - return true; + return cell + 1 >= numberofcells; } -Tabular::idx_type Tabular::cellAbove(idx_type cell) const +idx_type Tabular::cellAbove(idx_type cell) const { if (cellRow(cell) == 0) return cell; @@ -2208,7 +2307,7 @@ Tabular::idx_type Tabular::cellAbove(idx_type cell) const } -Tabular::idx_type Tabular::cellBelow(idx_type cell) const +idx_type Tabular::cellBelow(idx_type cell) const { row_type const nextrow = cellRow(cell) + rowSpan(cell); if (nextrow < nrows()) @@ -2217,7 +2316,7 @@ Tabular::idx_type Tabular::cellBelow(idx_type cell) const } -Tabular::idx_type Tabular::cellIndex(row_type row, col_type column) const +idx_type Tabular::cellIndex(row_type row, col_type column) const { LASSERT(column != npos && column < ncols(), column = 0); LASSERT(row != npos && row < nrows(), row = 0); @@ -2368,7 +2467,7 @@ bool Tabular::haveLTLastFoot(bool withcaptions) const } -Tabular::idx_type Tabular::setLTCaption(Cursor & cur, row_type row, bool what) +idx_type Tabular::setLTCaption(Cursor & cur, row_type row, bool what) { idx_type i = getFirstCellInRow(row); if (what) { @@ -2579,7 +2678,7 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list columns, break; } - for (col_type j = cstart ; j < c ; ++j) + for (col_type j = cstart ; j <= c ; ++j) if (column_info[j].alignment == LYX_ALIGN_DECIMAL) ++offset; col_type lastcol = (*it1 == *it2) ? c + 1 + offset : columns.size() - c + offset; @@ -2715,7 +2814,7 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list colum break; } - for (col_type j = cstart ; j < c ; ++j) + for (col_type j = cstart ; j <= c ; ++j) if (column_info[j].alignment == LYX_ALIGN_DECIMAL) ++offset; col_type lastcol = (*it1 == *it2) ? c + 1 + offset : columns.size() - c + offset; @@ -2827,16 +2926,21 @@ void Tabular::TeXCellPreamble(otexstream & os, idx_type cell, << from_ascii(getPWidth(cell).asLatexString()) << '}'; } else { - switch (align) { - case LYX_ALIGN_LEFT: - os << 'l'; - break; - case LYX_ALIGN_RIGHT: - os << 'r'; - break; - default: - os << 'c'; - break; + if ((getRotateCell(cell) == 0 && useBox(cell) == BOX_VARWIDTH + && align == LYX_ALIGN_LEFT)) + os << "V{\\linewidth}"; + else { + switch (align) { + case LYX_ALIGN_LEFT: + os << 'l'; + break; + case LYX_ALIGN_RIGHT: + os << 'r'; + break; + default: + os << 'c'; + break; + } } } // end if else !getPWidth } // end if else !cellinfo_of_cell @@ -2854,9 +2958,14 @@ void Tabular::TeXCellPreamble(otexstream & os, idx_type cell, os << "\\multirow{" << rowSpan(cell) << "}{"; if (!getPWidth(cell).zero()) os << from_ascii(getPWidth(cell).asLatexString()); - else - // we need to set a default value - os << "*"; + else { + if (column_info[c].varwidth) + // this inherits varwidth size + os << "="; + else + // we need to set a default value + os << "*"; + } os << "}"; if (!getMROffset(cell).zero()) os << "[" << from_ascii(getMROffset(cell).asLatexString()) << "]"; @@ -2896,8 +3005,10 @@ void Tabular::TeXCellPreamble(otexstream & os, idx_type cell, } os << "]{" << from_ascii(getPWidth(cell).asLatexString()) << "}\n"; - } else if (getRotateCell(cell) != 0 && getUsebox(cell) == BOX_VARWIDTH) { - os << "\\begin{varwidth}["; + } else if (getUsebox(cell) == BOX_VARWIDTH + && (getRotateCell(cell) != 0 || align != LYX_ALIGN_LEFT + || valign != LYX_VALIGN_TOP || hasNewlines(cell))) { + os << "\\begin{cellvarwidth}["; switch (valign) { case LYX_VALIGN_TOP: os << 't'; @@ -2908,9 +3019,26 @@ void Tabular::TeXCellPreamble(otexstream & os, idx_type cell, case LYX_VALIGN_BOTTOM: os << 'b'; break; + } + os << "]\n"; + switch (align) { + case LYX_ALIGN_RIGHT: + os << "\\raggedleft\n"; + break; + case LYX_ALIGN_CENTER: + os << "\\centering\n"; + break; + case LYX_ALIGN_LEFT: + //os << "\\narrowragged\n"; + break; + case LYX_ALIGN_BLOCK: + case LYX_ALIGN_DECIMAL: + case LYX_ALIGN_NONE: + case LYX_ALIGN_LAYOUT: + case LYX_ALIGN_SPECIAL: + break; + } } - os << "]{\\linewidth}\n"; -} } @@ -2926,8 +3054,10 @@ void Tabular::TeXCellPostamble(otexstream & os, idx_type cell, os << '}'; else if (getUsebox(cell) == BOX_MINIPAGE) os << breakln << "\\end{minipage}"; - else if (getRotateCell(cell) != 0 && getUsebox(cell) == BOX_VARWIDTH) - os << breakln << "\\end{varwidth}"; + else if (getUsebox(cell) == BOX_VARWIDTH + && (getRotateCell(cell) != 0 || getAlignment(cell) != LYX_ALIGN_LEFT + || getVAlignment(cell) != LYX_VALIGN_TOP || hasNewlines(cell))) + os << breakln << "\\end{cellvarwidth}"; if (getRotateCell(cell) != 0) os << breakln << "\\end{turn}"; if (ismultirow) @@ -3078,7 +3208,7 @@ void Tabular::TeXRow(otexstream & os, row_type row, bool rtl = par.isRTL(buffer().params()) && !par.empty() && getPWidth(cell).zero() - && !runparams.use_polyglossia; + && !runparams.isFullUnicode(); if (rtl) { string const lang = @@ -3132,17 +3262,18 @@ void Tabular::TeXRow(otexstream & os, row_type row, } else if (ltCaption(row)) { // Inside longtable caption rows, we must only output the caption inset // with its content and omit anything outside of that (see #10791) - InsetIterator it = inset_iterator_begin(*const_cast(inset)); - InsetIterator i_end = inset_iterator_end(*const_cast(inset)); - for (; it != i_end; ++it) { - if (it->lyxCode() != CAPTION_CODE) + InsetTableCell & tc_inset = *const_cast(inset); + for (Inset const & it : tc_inset) { + if (it.lyxCode() != CAPTION_CODE) continue; - it->latex(os, runparams); + it.latex(os, runparams); break; } } else if (!isPartOfMultiRow(row, c)) { if (!runparams.nice) os.texrow().start(par.id(), 0); + if (isMultiRow(cell) && !LaTeXFeatures::isAvailableAtLeastFrom("multirow", 2021, 1, 29)) + newrp.isNonLong = true; inset->latex(os, newrp); } @@ -3407,26 +3538,9 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const break; } os << 'X'; - } else if (isVTypeColumn(c)) { - switch (column_info[c].alignment) { - case LYX_ALIGN_LEFT: - os << ">{\\raggedright}"; - break; - case LYX_ALIGN_RIGHT: - os << ">{\\raggedleft}"; - break; - case LYX_ALIGN_CENTER: - os << ">{\\centering}"; - break; - case LYX_ALIGN_NONE: - case LYX_ALIGN_BLOCK: - case LYX_ALIGN_LAYOUT: - case LYX_ALIGN_SPECIAL: - case LYX_ALIGN_DECIMAL: - break; - } + } else if (isVTypeColumn(c)) os << "V{\\linewidth}"; - } else { + else { switch (column_info[c].alignment) { case LYX_ALIGN_LEFT: os << 'l'; @@ -3498,215 +3612,361 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const } -int Tabular::docbookRow(odocstream & os, row_type row, - OutputParams const & runparams) const +std::string Tabular::getVAlignAsXmlAttribute(idx_type cell) const { - int ret = 0; - idx_type cell = getFirstCellInRow(row); + switch (getVAlignment(cell)) { + case LYX_VALIGN_TOP: + return "valign='top'"; + case LYX_VALIGN_BOTTOM: + return "valign='bottom'"; + case LYX_VALIGN_MIDDLE: + return "valign='middle'"; + default: + // This case only silences a compiler warning, as all the cases are covered above. + return ""; + } +} - os << "\n"; - for (col_type c = 0; c < ncols(); ++c) { - if (isPartOfMultiColumn(row, c)) - continue; - os << "decimalSeparator()) + "\""; + } + default: + return "text-align: center"; + } +} + + +Tabular::XmlRowWiseBorders Tabular::computeXmlBorders(row_type row) const +{ + Tabular::XmlRowWiseBorders borders; + + // Determine whether borders are required. + for (col_type c = 0; c < ncols(); ++c) { + if (row < nrows() - 1) { + if (!bottomLine(cellIndex(row, c)) + || !topLine(cellIndex(row + 1, c))) { + borders.completeBorder = false; + } + if (!bottomLine(cellIndex(row, c)) + && !topLine(cellIndex(row + 1, c))) { + borders.completeBorderBelow = false; + } + } else if (row == nrows() - 1 && !bottomLine(cellIndex(row, c))) { + borders.completeBorderBelow = false; } - os << '"'; - if (isMultiColumn(cell)) { - os << " namest=\"col" << c << "\" "; - os << "nameend=\"col" << c + columnSpan(cell) - 1 << '"'; + if ((row > 0 && !bottomLine(cellIndex(row - 1, c)) && !topLine(cellIndex(row, c))) || + (row == 0 && !topLine(cellIndex(row, c)))) { + borders.completeBorderAbove = false; } + } - os << '>'; - ret += cellInset(cell)->docbook(os, runparams); - os << "\n"; - ++cell; + // Size of booktabs borders. + if (use_booktabs) { + if (borders.completeBorderAbove) + borders.borderTopWidth = row == 0 ? 2 : 1.5; + if (borders.completeBorderBelow) { + borders.borderBottomWidth = row == nrows() - 1 ? 2 : 1.5; + borders.borderBottomWidthComplete = 3 * borders.borderBottomWidth; + } } - os << "\n"; - return ret; + + return borders; } -int Tabular::docbook(odocstream & os, OutputParams const & runparams) const +std::vector Tabular::computeCssStylePerCell(row_type row, col_type col, idx_type cell) const { - int ret = 0; + std::vector styles; - //+--------------------------------------------------------------------- - //+ first the opening preamble + - //+--------------------------------------------------------------------- + // Fixed width. + Length const col_width = column_info[col].p_width; + if (!col_width.zero()) + styles.emplace_back("width: " + col_width.asHTMLString()); - os << "\n"; + // Borders and booktabs. + const Tabular::XmlRowWiseBorders borders = computeXmlBorders(row); - for (col_type c = 0; c < ncols(); ++c) { - os << "\n"; - ++ret; + if (bottomLine(cell)) { + if (row < nrows() - 1 && borders.completeBorder) + styles.emplace_back("border-bottom: " + to_string(borders.borderBottomWidthComplete) + "px double"); + else + styles.emplace_back("border-bottom: " + to_string(borders.borderBottomWidth) + "px solid"); } + if (rightLine(cell)) { + if (col < ncols() - 1 && leftLine(cell + 1)) + styles.emplace_back("border-right: 3px double"); + else + styles.emplace_back("border-right: 1px solid"); + } + if (leftLine(cell)) + styles.emplace_back("border-left: 1px solid"); + if (topLine(cell)) + styles.emplace_back("border-top: " + to_string(borders.borderTopWidth) + "px solid"); - //+--------------------------------------------------------------------- - //+ Long Tabular case + - //+--------------------------------------------------------------------- + return styles; +} - // output caption info - // The caption flag wins over head/foot - if (haveLTCaption()) { - os << "\n"; - ++ret; - for (row_type r = 0; r < nrows(); ++r) { - if (row_info[r].caption) { - ret += docbookRow(os, r, runparams); - } + +docstring Tabular::xmlRow(XMLStream & xs, const row_type row, OutputParams const & runparams, + const bool header, const XmlOutputFormat output_format, BufferParams::TableOutput docbook_table_output) const +{ + docstring ret; + const bool is_xhtml_table = output_format == XmlOutputFormat::XHTML || + docbook_table_output == BufferParams::TableOutput::HTMLTable; + const bool is_cals_table = output_format == XmlOutputFormat::DOCBOOK && + docbook_table_output == BufferParams::TableOutput::CALSTable; + + std::string const row_tag = is_xhtml_table ? "tr" : "row"; + std::string const cell_tag = is_xhtml_table ? (header ? "th" : "td") : "entry"; + Tabular::XmlRowWiseBorders const borders = computeXmlBorders(row); + idx_type cell = getFirstCellInRow(row); + + std::string row_attr; + bool cals_row_has_rowsep = false; // TODO: is this required? Is it possible that only a/several cells request a row separator, but not the complete row? + // CALS only: all cases where there should be a line *below* this row. + if (is_cals_table && (row_info[row].bottom_space_default || bottomLine(cell))) { + if (borders.completeBorderBelow) { + row_attr = "rowsep='1'"; + cals_row_has_rowsep = true; } - os << "\n"; - ++ret; } - // output header info - if (haveLTHead(false) || haveLTFirstHead(false)) { - os << "\n"; - ++ret; - for (row_type r = 0; r < nrows(); ++r) { - if ((row_info[r].endhead || row_info[r].endfirsthead) && - !row_info[r].caption) { - ret += docbookRow(os, r, runparams); + + xs << xml::StartTag(row_tag, row_attr); + xs << xml::CR(); + for (col_type c = 0; c < ncols(); ++c, ++cell) { + if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c)) + continue; + + stringstream attr; // Tag-level attributes, both for HTML and CALS. + stringstream style; // Tag-level CSS, only for HTML tables output in HTML. + + if (is_cals_table) { + if (!cals_row_has_rowsep && bottomLine(cell)) + attr << "rowsep='1' "; + } + + if (output_format == XmlOutputFormat::XHTML) { + // In HTML5, prefer to use CSS instead of attributes for alignment + // (align and valign). + style << getHAlignAsCssAttribute(cell) << "; " + << getVAlignAsCssAttribute(cell); + } else { + // In DocBook, both for HTML and CALS tables, stick to attributes. + attr << getHAlignAsXmlAttribute(cell) << " " + << getVAlignAsXmlAttribute(cell); + } + + if (is_xhtml_table) { + if (isMultiColumn(cell)) + attr << " colspan='" << columnSpan(cell) << "'"; + else if (isMultiRow(cell)) + attr << " rowspan='" << rowSpan(cell) << "'"; + } else if (is_cals_table) { + if (isMultiColumn(cell)) + attr << " namest='c" << c << " nameend='c" << (c + columnSpan(cell)) << "'"; + else if (isMultiRow(cell)) + attr << " morerows='" << rowSpan(cell) << "'"; + else + attr << " colname='c" << (c + 1) << "'"; // CALS column numbering starts at 1. + } + + // Final step: prepend the CSS style. + std::string attr_str = attr.str(); + if (is_xhtml_table) { + const std::vector styles = computeCssStylePerCell(row, c, cell); + + std::string attr_str_prefix = "style='" + style.str(); + if (!styles.empty()) + attr_str_prefix += "; "; + for (auto it = styles.begin(); it != styles.end(); ++it) { + attr_str_prefix += *it; + if (it != styles.end() - 1) + attr_str_prefix += "; "; } + attr_str_prefix += "' "; + + attr_str.insert(0, attr_str_prefix); } - os << "\n"; - ++ret; + + // Render the cell as either XHTML or DocBook. + xs << xml::StartTag(cell_tag, attr_str, true); + if (output_format == XmlOutputFormat::XHTML) { + ret += cellInset(cell)->xhtml(xs, runparams); + } else if (output_format == XmlOutputFormat::DOCBOOK) { + // DocBook: no return value for this function. + OutputParams rp = runparams; + rp.docbook_in_par = false; + rp.docbook_force_pars = true; + cellInset(cell)->docbook(xs, rp); + } + xs << xml::EndTag(cell_tag); + xs << xml::CR(); } - // output footer info - if (haveLTFoot(false) || haveLTLastFoot(false)) { - os << "\n"; - ++ret; + xs << xml::EndTag(row_tag); + xs << xml::CR(); + + return ret; +} + + +void Tabular::xmlHeader(XMLStream & xs, OutputParams const & runparams, const XmlOutputFormat output_format) const +{ + // Output the header of the table. For both HTML and CALS, this is surrounded by a thead. + bool const have_first_head = haveLTFirstHead(false); + // if we have a first head, then we are going to ignore the + // headers for the additional pages, since there aren't any + // in HTML or DocBook. + bool const have_head = !have_first_head && haveLTHead(false); + + if (have_head || have_first_head) { + xs << xml::StartTag("thead") << xml::CR(); for (row_type r = 0; r < nrows(); ++r) { - if ((row_info[r].endfoot || row_info[r].endlastfoot) && + if (((have_first_head && row_info[r].endfirsthead) || + (have_head && row_info[r].endhead)) && !row_info[r].caption) { - ret += docbookRow(os, r, runparams); + xmlRow(xs, r, runparams, true, output_format, buffer().params().docbook_table_output); } } - os << "\n"; - ++ret; + xs << xml::EndTag("thead"); + xs << xml::CR(); } +} - //+--------------------------------------------------------------------- - //+ the single row and columns (cells) + - //+--------------------------------------------------------------------- - os << "\n"; - ++ret; - for (row_type r = 0; r < nrows(); ++r) { - if (isValidRow(r)) { - ret += docbookRow(os, r, runparams); +void Tabular::xmlFooter(XMLStream & xs, OutputParams const & runparams, const XmlOutputFormat output_format) const +{ + // Output the footer of the table. For both HTML and CALS, this is surrounded by a tfoot and output just after + // the header (and before the body). + bool const have_last_foot = haveLTLastFoot(false); + bool const have_foot = !have_last_foot && haveLTFoot(false); + + if (have_foot || have_last_foot) { + xs << xml::StartTag("tfoot") << xml::CR(); + for (row_type r = 0; r < nrows(); ++r) { + if (((have_last_foot && row_info[r].endlastfoot) || + (have_foot && row_info[r].endfoot)) && + !row_info[r].caption) { + xmlRow(xs, r, runparams, false, output_format, buffer().params().docbook_table_output); + } } + xs << xml::EndTag("tfoot"); + xs << xml::CR(); } - os << "\n"; - ++ret; - //+--------------------------------------------------------------------- - //+ the closing of the tabular + - //+--------------------------------------------------------------------- +} - os << ""; - ++ret; - return ret; +void Tabular::xmlBody(XMLStream & xs, OutputParams const & runparams, const XmlOutputFormat output_format) const +{ + // Output the main part of the table. The tbody container is mandatory for CALS, but optional for HTML (only if + // there is no header and no footer). It never hurts to have it, though. + xs << xml::StartTag("tbody"); + xs << xml::CR(); + for (row_type r = 0; r < nrows(); ++r) + if (isValidRow(r)) + xmlRow(xs, r, runparams, false, output_format, buffer().params().docbook_table_output); + xs << xml::EndTag("tbody"); + xs << xml::CR(); } -docstring Tabular::xhtmlRow(XMLStream & xs, row_type row, - OutputParams const & runparams, bool header) const +void Tabular::docbook(XMLStream & xs, OutputParams const & runparams) const { - docstring ret; - string const celltag = header ? "th" : "td"; - idx_type cell = getFirstCellInRow(row); + // Some tables are inline. Likely limitation: cannot output a table within a table; is that really a limitation? + if (!runparams.docbook_in_table) { // Check on the *outer* set of parameters, so that the table can be closed + // properly at the end of this function. + xs << xml::StartTag("informaltable"); + xs << xml::CR(); + } - xs << xml::StartTag("tr"); - for (col_type c = 0; c < ncols(); ++c) { - if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c)) - continue; + // "Formal" tables have a title and use the tag ; the distinction with is done outside. + // HTML has the caption first with titles forbidden, and CALS has a title first. + if (haveLTCaption()) { + std::string caption_tag = ((buffer().params().docbook_table_output) == BufferParams::HTMLTable) ? "caption" : "title"; - stringstream attr; + xs << xml::StartTag(caption_tag); + for (row_type r = 0; r < nrows(); ++r) + if (row_info[r].caption) + xmlRow(xs, r, runparams, false, XmlOutputFormat::DOCBOOK, buffer().params().docbook_table_output); + xs << xml::EndTag(caption_tag); + xs << xml::CR(); + } - Length const cwidth = column_info[c].p_width; - if (!cwidth.zero()) { - string const hwidth = cwidth.asHTMLString(); - attr << "style =\"width: " << hwidth << ";\" "; - } + // CALS header: describe all columns in this table. For names, take 'c' then the ID of the column. + // Start at one, as is customary with CALS! + if (buffer().params().docbook_table_output == BufferParams::CALSTable) { + for (col_type c = 0; c < ncols(); ++c) { + std::stringstream attr; + attr << "colnum='" << (c + 1) << "' "; + attr << "colname='c" << (c + 1) << "' "; + Length const cwidth = column_info[c].p_width; + if (!cwidth.zero()) + attr << "colwidth='" << cwidth.asHTMLString() << "' "; + attr << "rowheader='norowheader'"; // Last attribute, hence no space at the end. - attr << "align='"; - switch (getAlignment(cell)) { - case LYX_ALIGN_LEFT: - attr << "left"; - break; - case LYX_ALIGN_RIGHT: - attr << "right"; - break; - default: - attr << "center"; - break; + xs << xml::CompTag("colspec", attr.str()); + xs << xml::CR(); } - attr << "'"; - attr << " valign='"; - switch (getVAlignment(cell)) { - case LYX_VALIGN_TOP: - attr << "top"; - break; - case LYX_VALIGN_BOTTOM: - attr << "bottom"; - break; - case LYX_VALIGN_MIDDLE: - attr << "middle"; - } - attr << "'"; + } - if (isMultiColumn(cell)) - attr << " colspan='" << columnSpan(cell) << "'"; - else if (isMultiRow(cell)) - attr << " rowspan='" << rowSpan(cell) << "'"; + xmlHeader(xs, runparams, XmlOutputFormat::DOCBOOK); + xmlFooter(xs, runparams, XmlOutputFormat::DOCBOOK); + xmlBody(xs, runparams, XmlOutputFormat::DOCBOOK); - xs << xml::StartTag(celltag, attr.str(), true) << xml::CR(); - ret += cellInset(cell)->xhtml(xs, runparams); - xs << xml::EndTag(celltag) << xml::CR(); - ++cell; + // If this method started the table tag, also make it close it. + if (!runparams.docbook_in_table) { + xs << xml::EndTag("informaltable"); + xs << xml::CR(); } - xs << xml::EndTag("tr"); - return ret; } @@ -3715,7 +3975,7 @@ docstring Tabular::xhtml(XMLStream & xs, OutputParams const & runparams) const docstring ret; if (is_long_tabular) { - // we'll wrap it in a div, so as to deal with alignment + // We'll wrap it in a div to deal with alignment. string align; switch (longtabular_alignment) { case LYX_LONGTABULAR_ALIGN_LEFT: @@ -3728,66 +3988,34 @@ docstring Tabular::xhtml(XMLStream & xs, OutputParams const & runparams) const align = "right"; break; } - xs << xml::StartTag("div", "class='longtable' style='text-align: " + align + ";'") - << xml::CR(); - // The caption flag wins over head/foot + xs << xml::StartTag("div", "class='longtable' style='text-align: " + align + ";'"); + xs << xml::CR(); + + // The caption flag is output before header/footer. if (haveLTCaption()) { - xs << xml::StartTag("div", "class='longtable-caption' style='text-align: " + align + ";'") - << xml::CR(); + xs << xml::StartTag("div", "class='longtable-caption' style='text-align: " + align + ";'"); + xs << xml::CR(); for (row_type r = 0; r < nrows(); ++r) if (row_info[r].caption) - ret += xhtmlRow(xs, r, runparams); - xs << xml::EndTag("div") << xml::CR(); + ret += xmlRow(xs, r, runparams, false, XmlOutputFormat::XHTML); + xs << xml::EndTag("div"); + xs << xml::CR(); } } - xs << xml::StartTag("table") << xml::CR(); + xs << xml::StartTag("table"); + xs << xml::CR(); - // output header info - bool const havefirsthead = haveLTFirstHead(false); - // if we have a first head, then we are going to ignore the - // headers for the additional pages, since there aren't any - // in XHTML. this test accomplishes that. - bool const havehead = !havefirsthead && haveLTHead(false); - if (havehead || havefirsthead) { - xs << xml::StartTag("thead") << xml::CR(); - for (row_type r = 0; r < nrows(); ++r) { - if (((havefirsthead && row_info[r].endfirsthead) || - (havehead && row_info[r].endhead)) && - !row_info[r].caption) { - ret += xhtmlRow(xs, r, runparams, true); - } - } - xs << xml::EndTag("thead") << xml::CR(); - } - // output footer info - bool const havelastfoot = haveLTLastFoot(false); - // as before. - bool const havefoot = !havelastfoot && haveLTFoot(false); - if (havefoot || havelastfoot) { - xs << xml::StartTag("tfoot") << xml::CR(); - for (row_type r = 0; r < nrows(); ++r) { - if (((havelastfoot && row_info[r].endlastfoot) || - (havefoot && row_info[r].endfoot)) && - !row_info[r].caption) { - ret += xhtmlRow(xs, r, runparams); - } - } - xs << xml::EndTag("tfoot") << xml::CR(); - } + xmlHeader(xs, runparams, XmlOutputFormat::XHTML); + xmlFooter(xs, runparams, XmlOutputFormat::XHTML); + xmlBody(xs, runparams, XmlOutputFormat::XHTML); - xs << xml::StartTag("tbody") << xml::CR(); - for (row_type r = 0; r < nrows(); ++r) { - if (isValidRow(r)) { - ret += xhtmlRow(xs, r, runparams); - } + xs << xml::EndTag("table"); + xs << xml::CR(); + if (is_long_tabular) { + xs << xml::EndTag("div"); + xs << xml::CR(); } - xs << xml::EndTag("tbody") - << xml::CR() - << xml::EndTag("table") - << xml::CR(); - if (is_long_tabular) - xs << xml::EndTag("div") << xml::CR(); return ret; } @@ -4053,8 +4281,10 @@ void Tabular::validate(LaTeXFeatures & features) const for (idx_type cell = 0; cell < numberofcells; ++cell) { if (isMultiRow(cell)) features.require("multirow"); - if (getUsebox(cell) == BOX_VARWIDTH) + if (getUsebox(cell) == BOX_VARWIDTH) { features.require("varwidth"); + features.require("cellvarwidth"); + } if (getVAlignment(cell) != LYX_VALIGN_TOP || !getPWidth(cell).zero() || isVTypeColumn(cellColumn(cell))) @@ -4091,6 +4321,21 @@ Tabular::BoxType Tabular::useBox(idx_type cell) const } +bool Tabular::hasNewlines(idx_type cell) const +{ + ParagraphList const & parlist = cellInset(cell)->paragraphs(); + ParagraphList::const_iterator cit = parlist.begin(); + ParagraphList::const_iterator end = parlist.end(); + + for (; cit != end; ++cit) + for (int i = 0; i < cit->size(); ++i) + if (cit->isNewline(i)) + return true; + + return false; +} + + ///////////////////////////////////////////////////////////////////// // // InsetTableCell @@ -4098,17 +4343,10 @@ Tabular::BoxType Tabular::useBox(idx_type cell) const ///////////////////////////////////////////////////////////////////// InsetTableCell::InsetTableCell(Buffer * buf) - : InsetText(buf, InsetText::PlainLayout), isFixedWidth(false), + : InsetText(buf, InsetText::PlainLayout), isFixedWidth(false), isVarwidth(false), isMultiColumn(false), isMultiRow(false), contentAlign(LYX_ALIGN_CENTER) {} - -bool InsetTableCell::forcePlainLayout(idx_type) const -{ - return isMultiRow || (isMultiColumn && !isFixedWidth); -} - - bool InsetTableCell::allowParagraphCustomization(idx_type) const { return isFixedWidth; @@ -4126,6 +4364,7 @@ bool InsetTableCell::getStatus(Cursor & cur, FuncRequest const & cmd, { bool enabled = true; switch (cmd.action()) { + case LFUN_INSET_SPLIT: case LFUN_INSET_DISSOLVE: enabled = false; break; @@ -4175,6 +4414,12 @@ docstring InsetTableCell::xhtml(XMLStream & xs, OutputParams const & rp) const } +void InsetTableCell::docbook(XMLStream & xs, OutputParams const & runparams) const +{ + InsetText::docbook(xs, runparams); +} + + void InsetTableCell::metrics(MetricsInfo & mi, Dimension & dim) const { TextMetrics & tm = mi.base.bv->textMetrics(&text()); @@ -4192,10 +4437,11 @@ void InsetTableCell::metrics(MetricsInfo & mi, Dimension & dim) const // We tell metrics here not to expand on multiple pars // This is the difference to InsetText::Metrics + Changer changetight = changeVar(mi.tight_insets, true); if (hasFixedWidth()) - tm.metrics(mi, dim, mi.base.textwidth, false); + tm.metrics(mi, dim, mi.base.textwidth); else - tm.metrics(mi, dim, 0, false); + tm.metrics(mi, dim, 0); mi.base.textwidth += horiz_offset; dim.asc += topOffset(mi.base.bv); dim.des += bottomOffset(mi.base.bv); @@ -4255,6 +4501,18 @@ bool InsetTabular::insetAllowed(InsetCode code) const } +bool InsetTabular::allowMultiPar() const +{ + for (col_type c = 0; c < tabular.ncols(); ++c) { + for (row_type r = 0; r < tabular.nrows(); ++r) { + if (tabular.cellInset(r,c)->allowMultiPar()) + return true; + } + } + return false; +} + + bool InsetTabular::allowsCaptionVariation(std::string const & newtype) const { return tabular.is_long_tabular && @@ -4362,7 +4620,7 @@ void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const // determine horizontal offset because of decimal align (if necessary) int decimal_width = 0; if (tabular.getAlignment(cell) == LYX_ALIGN_DECIMAL) { - InsetTableCell tail = InsetTableCell(*tabular.cellInset(cell)); + InsetTableCell tail = *tabular.cellInset(cell); tail.setBuffer(tabular.buffer()); // we need to set macrocontext position everywhere // otherwise we crash with nested insets (e.g. footnotes) @@ -4388,9 +4646,10 @@ void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const tabular.cell_info[r][c].decimal_hoffset = tm.width() - decimal_width; tabular.cell_info[r][c].decimal_width = decimal_width; - // with LYX_VALIGN_BOTTOM the descent is relative to the last par - // = descent of text in last par + bottomOffset: - int const lastpardes = tm.last().second->descent() + // with LYX_VALIGN_BOTTOM the descent is relative to the last + // row of the last par (note that the par might have multile rows!) + // = descent of text in last row + bottomOffset: + int const lastpardes = tm.last().second->rows().back().descent() + bottomOffset(mi.base.bv); int offset = 0; switch (tabular.getVAlignment(cell)) { @@ -4614,7 +4873,7 @@ void InsetTabular::drawCellLines(PainterInfo & pi, int x, int y, Color colour = Color_tabularline; if (tabular.column_info[col].change.changed() || tabular.row_info[row].change.changed()) - colour = InsetTableCell(*tabular.cellInset(cell)).paragraphs().front().lookupChange(0).color(); + colour = tabular.cellInset(cell)->paragraphs().front().lookupChange(0).color(); // Top bool drawline = tabular.topLine(cell) @@ -4850,7 +5109,9 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd) if (bvcur.selIsMultiCell()) { bvcur.pit() = bvcur.lastpit(); bvcur.pos() = bvcur.lastpos(); - } + } else + // Let InsetTableCell do it + cell(cur.idx())->dispatch(cur, cmd); } break; @@ -5100,6 +5361,13 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd) } break; + case LFUN_COPY: + if (cur.selIsMultiCell()) + copySelection(cur); + else + cell(cur.idx())->dispatch(cur, cmd); + break; + case LFUN_CUT: if (cur.selIsMultiCell()) { if (copySelection(cur)) { @@ -5135,16 +5403,6 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd) cell(cur.idx())->dispatch(cur, cmd); break; - case LFUN_COPY: - if (!cur.selection()) - break; - if (cur.selIsMultiCell()) { - cur.finishUndo(); - copySelection(cur); - } else - cell(cur.idx())->dispatch(cur, cmd); - break; - case LFUN_CLIPBOARD_PASTE: case LFUN_PRIMARY_SELECTION_PASTE: { docstring const clip = (act == LFUN_CLIPBOARD_PASTE) ? @@ -5159,8 +5417,7 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd) if (insertPlaintextString(cur.bv(), clip, false)) { // content has been replaced, // so cursor might be invalid - cur.pos() = cur.lastpos(); - cur.pit() = cur.lastpit(); + cur.fixIfBroken(); bvcur.setCursor(cur); break; } @@ -5184,6 +5441,17 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd) break; } } + else if (!theClipboard().isInternal() + && theClipboard().hasTextContents(Clipboard::LyXTextType)) { + // This might be tabular data from another LyX instance. Check! + docstring const clip = + theClipboard().getAsText(Clipboard::PlainTextType); + if (clip.find_first_of(from_ascii("\t\n")) != docstring::npos) { + FuncRequest ncmd = FuncRequest(LFUN_CLIPBOARD_PASTE, cmd.argument()); + doDispatch(cur, ncmd); + break; + } + } if (!cur.selIsMultiCell()) cell(cur.idx())->dispatch(cur, cmd); break; @@ -5196,6 +5464,7 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd) case LFUN_CHANGE_ACCEPT: case LFUN_CHANGE_REJECT: + case LFUN_FONT_DEFAULT: case LFUN_FONT_EMPH: case LFUN_FONT_BOLD: case LFUN_FONT_BOLDSYMBOL: @@ -5377,8 +5646,6 @@ bool InsetTabular::getFeatureStatus(Cursor & cur, string const & s, case Tabular::SET_SPECIAL_MULTICOLUMN: case Tabular::APPEND_ROW: case Tabular::APPEND_COLUMN: - case Tabular::DELETE_ROW: - case Tabular::DELETE_COLUMN: case Tabular::COPY_ROW: case Tabular::COPY_COLUMN: case Tabular::SET_TOP_SPACE: @@ -5387,6 +5654,13 @@ bool InsetTabular::getFeatureStatus(Cursor & cur, string const & s, status.clear(); return true; + case Tabular::DELETE_ROW: + status.setEnabled(tabular.nrows() > 1); + break; + case Tabular::DELETE_COLUMN: + status.setEnabled(tabular.ncols() > 1); + break; + case Tabular::SET_TABULAR_WIDTH: status.setEnabled(!tabular.rotate && tabular.tabular_valignment == Tabular::LYX_VALIGN_MIDDLE); @@ -5396,43 +5670,57 @@ bool InsetTabular::getFeatureStatus(Cursor & cur, string const & s, case Tabular::MOVE_COLUMN_LEFT: case Tabular::MOVE_ROW_DOWN: case Tabular::MOVE_ROW_UP: { - if (cur.selection()) { - status.message(_("Selections not supported.")); - status.setEnabled(false); - break; + row_type rs, re; + col_type cs, ce; + if (cur.selIsMultiCell()) + getSelection(cur, rs, re, cs, ce); + else { + rs = tabular.cellRow(cur.idx()); + re = rs; + cs = tabular.cellColumn(cur.idx()); + ce = cs; } - - if ((action == Tabular::MOVE_COLUMN_RIGHT && - tabular.ncols() == tabular.cellColumn(cur.idx()) + 1) || - (action == Tabular::MOVE_COLUMN_LEFT && - tabular.cellColumn(cur.idx()) == 0) || - (action == Tabular::MOVE_ROW_DOWN && - tabular.nrows() == tabular.cellRow(cur.idx()) + 1) || - (action == Tabular::MOVE_ROW_UP && - tabular.cellRow(cur.idx()) == 0)) { + if ((action == Tabular::MOVE_COLUMN_RIGHT + && tabular.ncols() == ce + 1) + || (action == Tabular::MOVE_COLUMN_LEFT && cs == 0) + || (action == Tabular::MOVE_ROW_DOWN + && tabular.nrows() == re + 1) + || (action == Tabular::MOVE_ROW_UP && rs == 0)) { status.setEnabled(false); break; } - if (action == Tabular::MOVE_COLUMN_RIGHT || - action == Tabular::MOVE_COLUMN_LEFT) { - if (tabular.hasMultiColumn(tabular.cellColumn(cur.idx())) || - tabular.hasMultiColumn(tabular.cellColumn(cur.idx()) + - (action == Tabular::MOVE_COLUMN_RIGHT ? 1 : -1))) { - status.message(_("Multi-column in current or" - " destination column.")); + if (action == Tabular::MOVE_COLUMN_RIGHT + || action == Tabular::MOVE_COLUMN_LEFT) { + bool has_multicol = (action == Tabular::MOVE_COLUMN_RIGHT) + ? tabular.hasMultiColumn(ce + 1) + : tabular.hasMultiColumn(cs - 1); + for (col_type c = cs; c <= ce; ++c) { + if (tabular.hasMultiColumn(c)) { + has_multicol = true; + break; + } + } + if (has_multicol) { + status.message(_("Column movement not supported with multi-columns.")); status.setEnabled(false); break; } } - if (action == Tabular::MOVE_ROW_DOWN || - action == Tabular::MOVE_ROW_UP) { - if (tabular.hasMultiRow(tabular.cellRow(cur.idx())) || - tabular.hasMultiRow(tabular.cellRow(cur.idx()) + - (action == Tabular::MOVE_ROW_DOWN ? 1 : -1))) { - status.message(_("Multi-row in current or" - " destination row.")); + if (action == Tabular::MOVE_ROW_DOWN + || action == Tabular::MOVE_ROW_UP) { + bool has_multirow = (action == Tabular::MOVE_ROW_DOWN) + ? tabular.hasMultiRow(re + 1) + : tabular.hasMultiRow(rs - 1); + for (row_type r = rs; r <= re; ++r) { + if (tabular.hasMultiRow(r)) { + has_multirow = true; + break; + } + } + if (has_multirow) { + status.message(_("Row movement not supported with multi-rows.")); status.setEnabled(false); break; } @@ -5467,9 +5755,28 @@ bool InsetTabular::getFeatureStatus(Cursor & cur, string const & s, status.setOnOff(tabular.isMultiRow(cur.idx())); break; + case Tabular::TOGGLE_INNER_LINES: + status.setOnOff(tabular.innerBorders(sel_row_start, sel_row_end, + sel_col_start, sel_col_end)); + status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx()))); + break; + case Tabular::TOGGLE_ALL_LINES: + status.setOnOff(tabular.innerBorders(sel_row_start, sel_row_end, + sel_col_start, sel_col_end) + && tabular.outsideBorders(sel_row_start, sel_row_end, + sel_col_start, sel_col_end)); + status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx()))); + break; case Tabular::SET_ALL_LINES: case Tabular::UNSET_ALL_LINES: case Tabular::SET_INNER_LINES: + status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx()))); + break; + + case Tabular::TOGGLE_BORDER_LINES: + status.setOnOff(tabular.outsideBorders(sel_row_start, sel_row_end, + sel_col_start, sel_col_end)); + // fall through case Tabular::SET_BORDER_LINES: status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx()))); break; @@ -5593,8 +5900,8 @@ bool InsetTabular::getFeatureStatus(Cursor & cur, string const & s, flag = false; // fall through case Tabular::VALIGN_TOP: - status.setEnabled(!tabular.getPWidth(cur.idx()).zero() - && !tabular.isMultiRow(cur.idx())); + status.setEnabled((!tabular.getPWidth(cur.idx()).zero() + || tabular.getUsebox(cur.idx()) == Tabular::BOX_VARWIDTH)); status.setOnOff( tabular.getVAlignment(cur.idx(), flag) == Tabular::LYX_VALIGN_TOP); break; @@ -5603,8 +5910,8 @@ bool InsetTabular::getFeatureStatus(Cursor & cur, string const & s, flag = false; // fall through case Tabular::VALIGN_BOTTOM: - status.setEnabled(!tabular.getPWidth(cur.idx()).zero() - && !tabular.isMultiRow(cur.idx())); + status.setEnabled((!tabular.getPWidth(cur.idx()).zero() + || tabular.getUsebox(cur.idx()) == Tabular::BOX_VARWIDTH)); status.setOnOff( tabular.getVAlignment(cur.idx(), flag) == Tabular::LYX_VALIGN_BOTTOM); break; @@ -5613,8 +5920,8 @@ bool InsetTabular::getFeatureStatus(Cursor & cur, string const & s, flag = false; // fall through case Tabular::VALIGN_MIDDLE: - status.setEnabled(!tabular.getPWidth(cur.idx()).zero() - && !tabular.isMultiRow(cur.idx())); + status.setEnabled((!tabular.getPWidth(cur.idx()).zero() + || tabular.getUsebox(cur.idx()) == Tabular::BOX_VARWIDTH)); status.setOnOff( tabular.getVAlignment(cur.idx(), flag) == Tabular::LYX_VALIGN_MIDDLE); break; @@ -5622,8 +5929,8 @@ bool InsetTabular::getFeatureStatus(Cursor & cur, string const & s, case Tabular::SET_LONGTABULAR: case Tabular::TOGGLE_LONGTABULAR: // setting as longtable is not allowed when table is inside a float - if (cur.innerInsetOfType(FLOAT_CODE) != 0 - || cur.innerInsetOfType(WRAP_CODE) != 0) + if (cur.innerInsetOfType(FLOAT_CODE) != nullptr + || cur.innerInsetOfType(WRAP_CODE) != nullptr) status.setEnabled(false); else status.setEnabled(true); @@ -5833,7 +6140,7 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd, } // check if there is already a caption bool have_caption = false; - InsetTableCell itc = InsetTableCell(*tabular.cellInset(cur.idx())); + InsetTableCell itc = *tabular.cellInset(cur.idx()); ParagraphList::const_iterator pit = itc.paragraphs().begin(); ParagraphList::const_iterator pend = itc.paragraphs().end(); for (; pit != pend; ++pit) { @@ -5915,20 +6222,8 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd, return cell(cur.idx())->getStatus(cur, cmd, status); } - // disable in non-fixed-width cells case LFUN_PARAGRAPH_BREAK: - // multirow does not allow paragraph breaks - if (tabular.isMultiRow(cur.idx())) { - status.setEnabled(false); - return true; - } - // fall through case LFUN_NEWLINE_INSERT: - if ((tabular.isMultiColumn(cur.idx()) || tabular.isMultiRow(cur.idx())) - && tabular.getPWidth(cur.idx()).zero()) { - status.setEnabled(false); - return true; - } return cell(cur.idx())->getStatus(cur, cmd, status); case LFUN_NEWPAGE_INSERT: @@ -5966,21 +6261,21 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd, } -Inset::RowFlags InsetTabular::rowFlags() const +int InsetTabular::rowFlags() const { - if (tabular.is_long_tabular) { - switch (tabular.longtabular_alignment) { - case Tabular::LYX_LONGTABULAR_ALIGN_LEFT: - return Display | AlignLeft; - case Tabular::LYX_LONGTABULAR_ALIGN_CENTER: - return Display; - case Tabular::LYX_LONGTABULAR_ALIGN_RIGHT: - return Display | AlignRight; - default: - return Display; - } - } else - return Inline; + if (tabular.is_long_tabular) { + switch (tabular.longtabular_alignment) { + case Tabular::LYX_LONGTABULAR_ALIGN_LEFT: + return Display | AlignLeft; + case Tabular::LYX_LONGTABULAR_ALIGN_CENTER: + return Display; + case Tabular::LYX_LONGTABULAR_ALIGN_RIGHT: + return Display | AlignRight; + default: + return Display; + } + } else + return Inline; } @@ -6000,30 +6295,9 @@ int InsetTabular::plaintext(odocstringstream & os, } -int InsetTabular::docbook(odocstream & os, OutputParams const & runparams) const +void InsetTabular::docbook(XMLStream & xs, OutputParams const & runparams) const { - int ret = 0; - Inset * master = 0; - - // FIXME: Why not pass a proper DocIterator here? -#if 0 - // if the table is inside a float it doesn't need the informaltable - // wrapper. Search for it. - for (master = owner(); master; master = master->owner()) - if (master->lyxCode() == FLOAT_CODE) - break; -#endif - - if (!master) { - os << ""; - ++ret; - } - ret += tabular.docbook(os, runparams); - if (!master) { - os << ""; - ++ret; - } - return ret; + tabular.docbook(xs, runparams); } @@ -6116,7 +6390,7 @@ void InsetTabular::setCursorFromCoordinates(Cursor & cur, int x, int y) const } -InsetTabular::idx_type InsetTabular::getNearestCell(BufferView & bv, int x, int y) const +idx_type InsetTabular::getNearestCell(BufferView & bv, int x, int y) const { idx_type idx_min = 0; int dist_min = numeric_limits::max(); @@ -6354,7 +6628,7 @@ void InsetTabular::tabularFeatures(Cursor & cur, row_type sel_row_start; row_type sel_row_end; bool setLines = false; - bool setLinesInnerOnly = false; + bool toggle = false; LyXAlignment setAlign = LYX_ALIGN_LEFT; Tabular::VAlignment setVAlign = Tabular::LYX_VALIGN_TOP; @@ -6512,23 +6786,51 @@ void InsetTabular::tabularFeatures(Cursor & cur, break; case Tabular::MOVE_COLUMN_RIGHT: - tabular.moveColumn(column, Tabular::RIGHT); - cur.idx() = tabular.cellIndex(row, column + 1); + tabular.moveColumn(sel_col_start, sel_col_end, Tabular::RIGHT); + if (cur.selection()) { + cur.selection(false); + cur.idx() = tabular.cellIndex(sel_row_start, sel_col_start + 1); + cur.resetAnchor(); + cur.idx() = tabular.cellIndex(sel_row_end, sel_col_end + 1); + cur.setSelection(); + } else + cur.idx() = tabular.cellIndex(row, column + 1); break; case Tabular::MOVE_COLUMN_LEFT: - tabular.moveColumn(column, Tabular::LEFT); - cur.idx() = tabular.cellIndex(row, column - 1); + tabular.moveColumn(sel_col_start, sel_col_end, Tabular::LEFT); + if (cur.selection()) { + cur.selection(false); + cur.idx() = tabular.cellIndex(sel_row_start, sel_col_start - 1); + cur.resetAnchor(); + cur.idx() = tabular.cellIndex(sel_row_end, sel_col_end - 1); + cur.setSelection(); + } else + cur.idx() = tabular.cellIndex(row, column - 1); break; case Tabular::MOVE_ROW_DOWN: - tabular.moveRow(row, Tabular::DOWN); - cur.idx() = tabular.cellIndex(row + 1, column); + tabular.moveRow(sel_row_start, sel_row_end, Tabular::DOWN); + if (cur.selection()) { + cur.selection(false); + cur.idx() = tabular.cellIndex(sel_row_start + 1, sel_col_start); + cur.resetAnchor(); + cur.idx() = tabular.cellIndex(sel_row_end + 1, sel_col_end); + cur.setSelection(); + } else + cur.idx() = tabular.cellIndex(row + 1, column); break; case Tabular::MOVE_ROW_UP: - tabular.moveRow(row, Tabular::UP); - cur.idx() = tabular.cellIndex(row - 1, column); + tabular.moveRow(sel_row_start, sel_row_end, Tabular::UP); + if (cur.selection()) { + cur.selection(false); + cur.idx() = tabular.cellIndex(sel_row_start - 1, sel_col_start); + cur.resetAnchor(); + cur.idx() = tabular.cellIndex(sel_row_end - 1, sel_col_end); + cur.setSelection(); + } else + cur.idx() = tabular.cellIndex(row - 1, column); break; case Tabular::SET_LINE_TOP: @@ -6750,27 +7052,36 @@ void InsetTabular::tabularFeatures(Cursor & cur, break; } + case Tabular::TOGGLE_INNER_LINES: + toggle = true; + // fall through case Tabular::SET_INNER_LINES: - setLinesInnerOnly = true; + if (toggle) + setLines = !tabular.innerBorders(sel_row_start, sel_row_end, + sel_col_start, sel_col_end); + else + setLines = true; + tabular.setLines(sel_row_start, sel_row_end, + sel_col_start, sel_col_end, + true, setLines); + break; + + case Tabular::TOGGLE_ALL_LINES: + toggle = true; // fall through case Tabular::SET_ALL_LINES: - setLines = true; + if (toggle) + setLines = !tabular.innerBorders(sel_row_start, sel_row_end, + sel_col_start, sel_col_end) + || !tabular.outsideBorders(sel_row_start, sel_row_end, + sel_col_start, sel_col_end); + else + setLines = true; // fall through case Tabular::UNSET_ALL_LINES: - for (row_type r = sel_row_start; r <= sel_row_end; ++r) - for (col_type c = sel_col_start; c <= sel_col_end; ++c) { - idx_type const cell = tabular.cellIndex(r, c); - if (!setLinesInnerOnly || r != sel_row_start) - tabular.setTopLine(cell, setLines); - if ((!setLinesInnerOnly || r != sel_row_end) - && (!setLines || r == sel_row_end)) - tabular.setBottomLine(cell, setLines); - if ((!setLinesInnerOnly || c != sel_col_end) - && (!setLines || c == sel_col_end)) - tabular.setRightLine(cell, setLines); - if ((!setLinesInnerOnly || c != sel_col_start)) - tabular.setLeftLine(cell, setLines); - } + tabular.setLines(sel_row_start, sel_row_end, + sel_col_start, sel_col_end, + false, setLines); break; case Tabular::RESET_FORMAL_DEFAULT: @@ -6784,16 +7095,24 @@ void InsetTabular::tabularFeatures(Cursor & cur, } break; - case Tabular::SET_BORDER_LINES: + case Tabular::TOGGLE_BORDER_LINES: + toggle = true; + // fall through + case Tabular::SET_BORDER_LINES: { + bool const border = toggle && + tabular.outsideBorders(sel_row_start, sel_row_end, + sel_col_start, sel_col_end) + ? false : true; for (row_type r = sel_row_start; r <= sel_row_end; ++r) { - tabular.setLeftLine(tabular.cellIndex(r, sel_col_start), true); - tabular.setRightLine(tabular.cellIndex(r, sel_col_end), true); + tabular.setLeftLine(tabular.cellIndex(r, sel_col_start), border); + tabular.setRightLine(tabular.cellIndex(r, sel_col_end), border); } for (col_type c = sel_col_start; c <= sel_col_end; ++c) { - tabular.setTopLine(tabular.cellIndex(sel_row_start, c), true); - tabular.setBottomLine(tabular.cellIndex(sel_row_end, c), true); + tabular.setTopLine(tabular.cellIndex(sel_row_start, c), border); + tabular.setBottomLine(tabular.cellIndex(sel_row_end, c), border); } break; + } case Tabular::TOGGLE_LONGTABULAR: if (tabular.is_long_tabular) @@ -7079,7 +7398,7 @@ bool InsetTabular::copySelection(Cursor & cur) paste_tabular->setBuffer(tabular.buffer()); odocstringstream os; - OutputParams const runparams(0); + OutputParams const runparams(nullptr); paste_tabular->plaintext(os, runparams, 0, true, '\t', INT_MAX); // Needed for the "Edit->Paste recent" menu and the system clipboard. cap::copySelection(cur, os.str()); @@ -7107,12 +7426,15 @@ bool InsetTabular::pasteClipboard(Cursor & cur) getSelection(cur, actrow, re, actcol, ce); } - for (row_type r1 = 0, r2 = actrow; - r1 < paste_tabular->nrows() && r2 < tabular.nrows(); - ++r1, ++r2) { - for (col_type c1 = 0, c2 = actcol; - c1 < paste_tabular->ncols() && c2 < tabular.ncols(); - ++c1, ++c2) { + col_type const oldncols = tabular.ncols(); + for (row_type r1 = 0, r2 = actrow; r1 < paste_tabular->nrows(); ++r1, ++r2) { + // Append rows if needed + if (r2 == tabular.nrows()) + tabular.insertRow(r2 - 1, false); + for (col_type c1 = 0, c2 = actcol; c1 < paste_tabular->ncols(); ++c1, ++c2) { + // Append columns if needed + if (c2 == tabular.ncols()) + tabular.insertColumn(c2 - 1, false); if (paste_tabular->isPartOfMultiColumn(r1, c1) && tabular.isPartOfMultiColumn(r2, c2)) continue; @@ -7130,16 +7452,21 @@ bool InsetTabular::pasteClipboard(Cursor & cur) // FIXME?: why do we need to do this explicitly? (EL) tabular.cellInset(r2, c2)->setBuffer(tabular.buffer()); - if (!lyxrc.ct_markup_copied) { - // do not paste deleted text - inset->acceptChanges(); + if (lyxrc.ct_markup_copied) { + // Only change to inserted if ct is active, + // otherwise leave markup as is + if (buffer().params().track_changes) + inset->setChange(Change(Change::INSERTED)); + } else + // Resolve all markup to inserted or unchanged inset->setChange(Change(buffer().params().track_changes ? - Change::INSERTED : Change::UNCHANGED)); - } + Change::INSERTED : Change::UNCHANGED)); cur.pos() = 0; cur.pit() = 0; } } + // amend cursor position if cols have been appended + cur.idx() += actrow * (tabular.ncols() - oldncols); return true; } @@ -7189,10 +7516,15 @@ docstring InsetTabular::asString(idx_type stidx, idx_type enidx, { LASSERT(stidx <= enidx, return docstring()); docstring retval; - col_type const col1 = tabular.cellColumn(stidx); - col_type const col2 = tabular.cellColumn(enidx); - row_type const row1 = tabular.cellRow(stidx); - row_type const row2 = tabular.cellRow(enidx); + col_type col1 = tabular.cellColumn(stidx); + col_type col2 = tabular.cellColumn(enidx); + row_type row1 = tabular.cellRow(stidx); + row_type row2 = tabular.cellRow(enidx); + // stidx might be in a later column or row than enidx + if (col1 > col2) + swap(col1, col2); + if (row1 > row2) + swap(row1, row2); bool first = true; for (col_type col = col1; col <= col2; col++) for (row_type row = row1; row <= row2; row++) { @@ -7210,13 +7542,18 @@ ParagraphList InsetTabular::asParList(idx_type stidx, idx_type enidx) { LASSERT(stidx <= enidx, return ParagraphList()); ParagraphList retval; - col_type const col1 = tabular.cellColumn(stidx); - col_type const col2 = tabular.cellColumn(enidx); - row_type const row1 = tabular.cellRow(stidx); - row_type const row2 = tabular.cellRow(enidx); + col_type col1 = tabular.cellColumn(stidx); + col_type col2 = tabular.cellColumn(enidx); + row_type row1 = tabular.cellRow(stidx); + row_type row2 = tabular.cellRow(enidx); + // stidx might be in a later column or row than enidx + if (col1 > col2) + swap(col1, col2); + if (row1 > row2) + swap(row1, row2); for (col_type col = col1; col <= col2; col++) for (row_type row = row1; row <= row2; row++) - for (auto par : tabular.cellInset(row, col)->paragraphs()) + for (auto const & par : tabular.cellInset(row, col)->paragraphs()) retval.push_back(par); return retval; } @@ -7241,7 +7578,7 @@ void InsetTabular::getSelection(Cursor & cur, Text * InsetTabular::getText(int idx) const { - return size_t(idx) < nargs() ? cell(idx)->getText(0) : 0; + return size_t(idx) < nargs() ? cell(idx)->getText(0) : nullptr; } @@ -7312,12 +7649,6 @@ bool InsetTabular::allowParagraphCustomization(idx_type cell) const } -bool InsetTabular::forcePlainLayout(idx_type cell) const -{ - return tabular.isMultiColumn(cell) && !tabular.getPWidth(cell).zero(); -} - - bool InsetTabular::insertPlaintextString(BufferView & bv, docstring const & buf, bool usePaste) { @@ -7471,13 +7802,14 @@ bool InsetTabular::automaticPopupCompletion() const bool InsetTabular::showCompletionCursor() const { - return lyxrc.completion_cursor_text; + return lyxrc.completion_cursor_text && + (lyxrc.completion_inline_text || lyxrc.completion_popup_text); } CompletionList const * InsetTabular::createCompletionList(Cursor const & cur) const { - return completionSupported(cur) ? cur.text()->createCompletionList(cur) : 0; + return completionSupported(cur) ? cur.text()->createCompletionList(cur) : nullptr; } @@ -7489,12 +7821,12 @@ docstring InsetTabular::completionPrefix(Cursor const & cur) const } -bool InsetTabular::insertCompletion(Cursor & cur, docstring const & s, bool finished) +bool InsetTabular::insertCompletion(Cursor & cur, docstring const & s, bool /*finished*/) { if (!completionSupported(cur)) return false; - return cur.text()->insertCompletion(cur, s, finished); + return cur.text()->insertCompletion(cur, s); } @@ -7546,8 +7878,8 @@ string InsetTabular::params2string(InsetTabular const & inset) void InsetTabular::setLayoutForHiddenCells(DocumentClass const & dc) { - for (Tabular::col_type c = 0; c < tabular.ncols(); ++c) { - for (Tabular::row_type r = 0; r < tabular.nrows(); ++r) { + for (col_type c = 0; c < tabular.ncols(); ++c) { + for (row_type r = 0; r < tabular.nrows(); ++r) { if (!tabular.isPartOfMultiColumn(r,c) && !tabular.isPartOfMultiRow(r,c)) continue;