#include "frontends/Painter.h"
#include "frontends/Selection.h"
+#include "support/Changer.h"
#include "support/convert.h"
#include "support/debug.h"
#include "support/docstream.h"
{ 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 },
{ 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},
{ 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 }
};
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 = static_cast<bool>(dit);
if (hassep) {
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;
}
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;
}
}
+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<bool, bool> Tabular::topLineTrim(idx_type const cell) const
{
if (!use_booktabs)
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;
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;
<< "}\n";
} else if (getUsebox(cell) == BOX_VARWIDTH
&& (getRotateCell(cell) != 0 || align != LYX_ALIGN_LEFT
- || valign != LYX_VALIGN_TOP)) {
+ || valign != LYX_VALIGN_TOP || hasNewlines(cell))) {
os << "\\begin{cellvarwidth}[";
switch (valign) {
case LYX_VALIGN_TOP:
os << breakln << "\\end{minipage}";
else if (getUsebox(cell) == BOX_VARWIDTH
&& (getRotateCell(cell) != 0 || getAlignment(cell) != LYX_ALIGN_LEFT
- || getVAlignment(cell) != LYX_VALIGN_TOP))
+ || getVAlignment(cell) != LYX_VALIGN_TOP || hasNewlines(cell)))
os << breakln << "\\end{cellvarwidth}";
if (getRotateCell(cell) != 0)
os << breakln << "\\end{turn}";
}
-void Tabular::docbookRow(XMLStream & xs, row_type row,
- OutputParams const & runparams, bool header) const
+std::string Tabular::getVAlignAsXmlAttribute(idx_type cell) const
{
- switch (buffer().params().docbook_table_output) {
- case BufferParams::HTMLTable:
- docbookRowAsHTML(xs, row, runparams, header);
- break;
- case BufferParams::CALSTable:
- docbookRowAsCALS(xs, row, runparams);
- break;
+ 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 "";
}
}
-void Tabular::docbookRowAsHTML(XMLStream & xs, row_type row,
- OutputParams const & runparams, bool header) const
+std::string Tabular::getHAlignAsXmlAttribute(idx_type cell, const XmlOutputFormat output_format) const
{
- string const celltag = header ? "th" : "td";
- idx_type cell = getFirstCellInRow(row);
+ switch (getAlignment(cell)) {
+ case LYX_ALIGN_LEFT:
+ return "align='left'";
+ case LYX_ALIGN_RIGHT:
+ return "align='right'";
- xs << xml::StartTag("tr");
- xs << xml::CR();
- for (col_type c = 0; c < ncols(); ++c) {
- if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c))
- continue;
+ default:
+ // HTML only supports left, right, and center.
+ if (output_format == XmlOutputFormat::XHTML)
+ return "align='center'";
+
+ // DocBook also has justify and decimal.
+ if (getAlignment(cell) == LYX_ALIGN_BLOCK) {
+ return "align='justify'";
+ } else if (getAlignment(cell) == LYX_ALIGN_DECIMAL) {
+ Language const *tlang = buffer().paragraphs().front().getParLanguage(buffer().params());
+ return "align='char' char='" + to_utf8(tlang->decimalSeparator()) + "'";
+ } else {
+ return "align='center'";
+ }
+ }
+}
- stringstream attr;
+Tabular::XmlRowWiseBorders Tabular::computeXmlBorders(row_type row) const
+{
+ Tabular::XmlRowWiseBorders borders;
- Length const cwidth = column_info[c].p_width;
- if (!cwidth.zero()) {
- string const hwidth = cwidth.asHTMLString();
- attr << "style=\"width: " << hwidth << ";\" ";
+ // 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;
}
- attr << "align='";
- switch (getAlignment(cell)) {
- case LYX_ALIGN_BLOCK:
- attr << "justify";
- break;
- case LYX_ALIGN_DECIMAL: {
- Language const *tlang = buffer().paragraphs().front().getParLanguage(buffer().params());
- attr << "char' char='" << to_utf8(tlang->decimalSeparator());
+ if ((row > 0 && !bottomLine(cellIndex(row - 1, c)) && !topLine(cellIndex(row, c))) ||
+ (row == 0 && !topLine(cellIndex(row, c)))) {
+ borders.completeBorderAbove = false;
}
- break;
- case LYX_ALIGN_LEFT:
- attr << "left";
- break;
- case LYX_ALIGN_RIGHT:
- attr << "right";
- break;
- default:
- attr << "center";
- break;
- }
- 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";
+ }
+
+ // 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;
}
- attr << "'";
+ }
+
+ return borders;
+}
- if (isMultiColumn(cell))
- attr << " colspan='" << columnSpan(cell) << "'";
- else if (isMultiRow(cell))
- attr << " rowspan='" << rowSpan(cell) << "'";
- OutputParams rp = runparams;
- rp.docbook_in_par = false;
- rp.docbook_force_pars = true;
- xs << xml::StartTag(celltag, attr.str(), true);
- cellInset(cell)->docbook(xs, rp);
- xs << xml::EndTag(celltag);
- xs << xml::CR();
- ++cell;
+std::vector<std::string> Tabular::computeCssStylePerCell(row_type row, col_type col, idx_type cell) const
+{
+ std::vector<std::string> styles;
+
+ // Fixed width.
+ Length const col_width = column_info[col].p_width;
+ if (!col_width.zero())
+ styles.emplace_back("width: " + col_width.asHTMLString());
+
+ // Borders and booktabs.
+ const Tabular::XmlRowWiseBorders borders = computeXmlBorders(row);
+
+ 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");
}
- xs << xml::EndTag("tr");
- xs << xml::CR();
+ 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");
+
+ return styles;
}
-void Tabular::docbookRowAsCALS(XMLStream & xs, row_type row,
- OutputParams const & runparams) const
+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);
- xs << xml::StartTag("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;
+ }
+ }
+
+ xs << xml::StartTag(row_tag, row_attr);
xs << xml::CR();
- for (col_type c = 0; c < ncols(); ++c) {
+ for (col_type c = 0; c < ncols(); ++c, ++cell) {
if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c))
continue;
stringstream attr;
- attr << "align='";
- switch (getAlignment(cell)) {
- case LYX_ALIGN_BLOCK:
- attr << "justify";
- break;
- case LYX_ALIGN_DECIMAL: {
- Language const *tlang = buffer().paragraphs().front().getParLanguage(buffer().params());
- attr << "char' char='" << to_utf8(tlang->decimalSeparator());
+ if (is_xhtml_table) {
+ const std::vector<std::string> styles = computeCssStylePerCell(row, c, cell);
+ attr << "style='" ;
+ for (auto it = styles.begin(); it != styles.end(); ++it) {
+ attr << *it;
+ if (it != styles.end() - 1)
+ attr << "; ";
+ }
+ attr << "' ";
+ }
+
+ if (is_cals_table) {
+ if (!cals_row_has_rowsep && bottomLine(cell))
+ attr << "rowsep='1' ";
+ }
+
+ attr << getHAlignAsXmlAttribute(cell, output_format) << " " << 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.
}
- break;
- case LYX_ALIGN_LEFT:
- attr << "left";
- break;
- case LYX_ALIGN_RIGHT:
- attr << "right";
- break;
- default:
- attr << "center";
- break;
+ // 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);
}
- 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";
+ xs << xml::EndTag(cell_tag);
+ xs << xml::CR();
+ }
+ 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 (((have_first_head && row_info[r].endfirsthead) ||
+ (have_head && row_info[r].endhead)) &&
+ !row_info[r].caption) {
+ xmlRow(xs, r, runparams, true, output_format, buffer().params().docbook_table_output);
+ }
}
- attr << "'";
+ xs << xml::EndTag("thead");
+ xs << xml::CR();
+ }
+}
- if (isMultiColumn(cell))
- attr << " colspan='" << columnSpan(cell) << "'";
- else if (isMultiRow(cell))
- attr << " rowspan='" << rowSpan(cell) << "'";
- else
- attr << " colname='c" << (c + 1) << "'"; // Column numbering starts at 1.
- // All cases where there should be a line *below* this row.
- if (row_info[row].bottom_space_default)
- attr << " rowsep='1'";
+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);
- xs << xml::StartTag("entry", attr.str(), true);
- cellInset(cell)->docbook(xs, runparams);
- xs << xml::EndTag("entry");
+ 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();
- ++cell;
}
- xs << xml::EndTag("row");
+}
+
+
+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();
}
void Tabular::docbook(XMLStream & xs, OutputParams const & runparams) const
{
- docstring ret;
-
// 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.
// "Formal" tables have a title and use the tag <table>; the distinction with <informaltable> is done outside.
// HTML has the caption first with titles forbidden, and CALS has a title first.
if (haveLTCaption()) {
- std::string tag = ((buffer().params().docbook_table_output) == BufferParams::HTMLTable) ? "caption" : "title";
+ std::string caption_tag = ((buffer().params().docbook_table_output) == BufferParams::HTMLTable) ? "caption" : "title";
- xs << xml::StartTag(tag);
+ xs << xml::StartTag(caption_tag);
for (row_type r = 0; r < nrows(); ++r)
if (row_info[r].caption)
- docbookRow(xs, r, runparams);
- xs << xml::EndTag(tag);
+ xmlRow(xs, r, runparams, false, XmlOutputFormat::DOCBOOK, buffer().params().docbook_table_output);
+ xs << xml::EndTag(caption_tag);
xs << xml::CR();
}
}
}
- // Output the header of the table. For both HTML and CALS, this is surrounded by a thead.
- 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 DocBook. 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) {
- docbookRow(xs, r, runparams, true);
- }
- }
- xs << xml::EndTag("thead");
- xs << xml::CR();
- }
-
- // 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 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) {
- docbookRow(xs, r, runparams); // TODO: HTML vs CALS
- }
- }
- xs << xml::EndTag("tfoot");
- xs << xml::CR();
- }
-
- // 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))
- docbookRow(xs, r, runparams);
- xs << xml::EndTag("tbody");
- xs << xml::CR();
+ xmlHeader(xs, runparams, XmlOutputFormat::DOCBOOK);
+ xmlFooter(xs, runparams, XmlOutputFormat::DOCBOOK);
+ xmlBody(xs, runparams, XmlOutputFormat::DOCBOOK);
// If this method started the table tag, also make it close it.
if (!runparams.docbook_in_table) {
}
-docstring Tabular::xhtmlRow(XMLStream & xs, row_type row,
- OutputParams const & runparams, bool header) const
-{
- docstring ret;
- string const celltag = header ? "th" : "td";
- idx_type cell = getFirstCellInRow(row);
-
- xs << xml::StartTag("tr");
- for (col_type c = 0; c < ncols(); ++c) {
- if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c))
- continue;
-
- stringstream attr;
-
- Length const cwidth = column_info[c].p_width;
- if (!cwidth.zero()) {
- string const hwidth = cwidth.asHTMLString();
- attr << "style=\"width: " << hwidth << ";\" ";
- }
-
- attr << "align='";
- switch (getAlignment(cell)) {
- case LYX_ALIGN_LEFT:
- attr << "left";
- break;
- case LYX_ALIGN_RIGHT:
- attr << "right";
- break;
- default:
- attr << "center";
- break;
- }
- 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) << "'";
-
- xs << xml::StartTag(celltag, attr.str(), true) << xml::CR();
- ret += cellInset(cell)->xhtml(xs, runparams);
- xs << xml::EndTag(celltag) << xml::CR();
- ++cell;
- }
- xs << xml::EndTag("tr");
- return ret;
-}
-
-
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:
}
xs << xml::StartTag("div", "class='longtable' style='text-align: " + align + ";'");
xs << xml::CR();
- // The caption flag wins over head/foot
+
+ // The caption flag is output before header/footer.
if (haveLTCaption()) {
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);
+ ret += xmlRow(xs, r, runparams, false, XmlOutputFormat::XHTML);
xs << xml::EndTag("div");
xs << 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");
- xs << 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");
- xs << 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");
- xs << 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("tbody");
- xs << xml::CR();
xs << xml::EndTag("table");
xs << xml::CR();
if (is_long_tabular) {
}
+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
// We tell metrics here not to expand on multiple pars
// This is the difference to InsetText::Metrics
- if (hasFixedWidth() || isVarwidth)
- tm.metrics(mi, dim, mi.base.textwidth, false);
+ Changer changetight = changeVar(mi.tight_insets, true);
+ if (hasFixedWidth())
+ 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);
// 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)
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)
if (bvcur.selIsMultiCell()) {
bvcur.pit() = bvcur.lastpit();
bvcur.pos() = bvcur.lastpos();
- }
+ } else
+ // Let InsetTableCell do it
+ cell(cur.idx())->dispatch(cur, cmd);
}
break;
case LFUN_CHANGE_ACCEPT:
case LFUN_CHANGE_REJECT:
+ case LFUN_FONT_DEFAULT:
case LFUN_FONT_EMPH:
case LFUN_FONT_BOLD:
case LFUN_FONT_BOLDSYMBOL:
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:
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);
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;
}
// 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) {
}
-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;
}
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;
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:
}
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)
}
-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);
}