}
-void Tabular::docbookRow(XMLStream & xs, row_type row,
- OutputParams const & runparams, bool header) const
-{
- switch (buffer().params().docbook_table_output) {
- case BufferParams::HTMLTable:
- docbookRowAsHTML(xs, row, runparams, header);
- break;
- case BufferParams::CALSTable:
- docbookRowAsCALS(xs, row, runparams);
- break;
- }
-}
-
-
std::string Tabular::getVAlignAsXmlAttribute(idx_type cell) const
{
switch (getVAlignment(cell)) {
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 "";
}
}
-std::string Tabular::getHAlignAsXmlAttribute(idx_type cell, bool is_xhtml) const
+std::string Tabular::getHAlignAsXmlAttribute(idx_type cell, const XmlOutputFormat output_format) const
{
- // TODO: the Boolean flag isn't really clean; switch to an enum at some point.
switch (getAlignment(cell)) {
case LYX_ALIGN_LEFT:
return "align='left'";
default:
// HTML only supports left, right, and center.
- if (is_xhtml)
+ if (output_format == XmlOutputFormat::XHTML)
return "align='center'";
// DocBook also has justify and decimal.
}
}
-
-void Tabular::docbookRowAsHTML(XMLStream & xs, row_type row,
- OutputParams const & runparams, bool header) const
+Tabular::XmlRowWiseBorders Tabular::computeXmlBorders(row_type row) const
{
- string const celltag = header ? "th" : "td";
- idx_type cell = getFirstCellInRow(row);
+ Tabular::XmlRowWiseBorders borders;
- xs << xml::StartTag("tr");
- xs << xml::CR();
+ // Determine whether borders are required.
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 << ";\" ";
+ 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 << getHAlignAsXmlAttribute(cell, false) << " " << getVAlignAsXmlAttribute(cell);
-
- if (isMultiColumn(cell))
- attr << " colspan='" << columnSpan(cell) << "'";
- else if (isMultiRow(cell))
- attr << " rowspan='" << rowSpan(cell) << "'";
+ if ((row > 0 && !bottomLine(cellIndex(row - 1, c)) && !topLine(cellIndex(row, c))) ||
+ (row == 0 && !topLine(cellIndex(row, c)))) {
+ borders.completeBorderAbove = false;
+ }
+ }
- 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;
+ // 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;
+ }
}
- xs << xml::EndTag("tr");
- xs << xml::CR();
+
+ return borders;
}
-void Tabular::docbookRowAsCALS(XMLStream & xs, row_type row,
- OutputParams const & runparams) const
+std::vector<std::string> Tabular::computeCssStylePerCell(row_type row, col_type col, idx_type cell) const
{
- idx_type cell = getFirstCellInRow(row);
-
- xs << xml::StartTag("row");
- xs << xml::CR();
- for (col_type c = 0; c < ncols(); ++c) {
- if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c))
- continue;
+ std::vector<std::string> styles;
- stringstream attr;
+ // Fixed width.
+ Length const col_width = column_info[col].p_width;
+ if (!col_width.zero())
+ styles.emplace_back("width: " + col_width.asHTMLString());
- attr << getHAlignAsXmlAttribute(cell, false) << " " << getVAlignAsXmlAttribute(cell);
+ // Borders and booktabs.
+ const Tabular::XmlRowWiseBorders borders = computeXmlBorders(row);
- if (isMultiColumn(cell))
- attr << " colspan='" << columnSpan(cell) << "'";
- else if (isMultiRow(cell))
- attr << " rowspan='" << rowSpan(cell) << "'";
+ if (bottomLine(cell)) {
+ if (row < nrows() - 1 && borders.completeBorder)
+ styles.emplace_back("border-bottom: " + to_string(borders.borderBottomWidthComplete) + "px double");
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'";
-
- xs << xml::StartTag("entry", attr.str(), true);
- cellInset(cell)->docbook(xs, runparams);
- xs << xml::EndTag("entry");
- xs << xml::CR();
- ++cell;
+ styles.emplace_back("border-bottom: " + to_string(borders.borderBottomWidth) + "px solid");
}
- xs << xml::EndTag("row");
- 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::docbook(XMLStream & xs, 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);
- // 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();
+ 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;
+ }
}
- // "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";
+ 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;
- xs << xml::StartTag(tag);
- for (row_type r = 0; r < nrows(); ++r)
- if (row_info[r].caption)
- docbookRow(xs, r, runparams);
- xs << xml::EndTag(tag);
- xs << xml::CR();
- }
+ stringstream attr;
- // 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.
+ 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.
+ }
- xs << xml::CompTag("colspec", attr.str());
- xs << xml::CR();
+ // 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();
}
+ 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 havefirsthead = haveLTFirstHead(false);
+ 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 DocBook. this test accomplishes that.
- bool const havehead = !havefirsthead && haveLTHead(false);
- if (havehead || havefirsthead) {
+ // 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 (((havefirsthead && row_info[r].endfirsthead) ||
- (havehead && row_info[r].endhead)) &&
+ if (((have_first_head && row_info[r].endfirsthead) ||
+ (have_head && row_info[r].endhead)) &&
!row_info[r].caption) {
- docbookRow(xs, r, runparams, true);
+ xmlRow(xs, r, runparams, true, output_format, buffer().params().docbook_table_output);
}
}
xs << xml::EndTag("thead");
xs << xml::CR();
}
+}
+
+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 havelastfoot = haveLTLastFoot(false);
- // as before.
- bool const havefoot = !havelastfoot && haveLTFoot(false);
- if (havefoot || havelastfoot) {
+ 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 (((havelastfoot && row_info[r].endlastfoot) ||
- (havefoot && row_info[r].endfoot)) &&
+ if (((have_last_foot && row_info[r].endlastfoot) ||
+ (have_foot && row_info[r].endfoot)) &&
!row_info[r].caption) {
- docbookRow(xs, r, runparams); // TODO: HTML vs CALS
+ xmlRow(xs, r, runparams, false, output_format, buffer().params().docbook_table_output);
}
}
xs << xml::EndTag("tfoot");
xs << xml::CR();
}
+}
+
+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))
- docbookRow(xs, r, runparams);
+ xmlRow(xs, r, runparams, false, output_format, buffer().params().docbook_table_output);
xs << xml::EndTag("tbody");
xs << xml::CR();
-
- // If this method started the table tag, also make it close it.
- if (!runparams.docbook_in_table) {
- xs << xml::EndTag("informaltable");
- 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 <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 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 << getHAlignAsXmlAttribute(cell, true) << " " << getVAlignAsXmlAttribute(cell);
+ xs << xml::CompTag("colspec", attr.str());
+ xs << xml::CR();
+ }
+ }
- 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;
}
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) {
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:
}
-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);
}