#include "buffer.h"
#include "bufferparams.h"
+#include "BufferView.h"
+#include "cursor.h"
#include "debug.h"
#include "LaTeXFeatures.h"
#include "lyxlex.h"
#include "outputparams.h"
#include "paragraph.h"
+#include "paragraph_funcs.h"
#include "insets/insettabular.h"
#include "support/lstrings.h"
-#include "support/tostr.h"
+#include "support/convert.h"
#include <sstream>
using lyx::support::ltrim;
using lyx::support::prefixIs;
using lyx::support::rtrim;
-using lyx::support::strToInt;
using lyx::support::suffixIs;
using boost::shared_ptr;
namespace {
int const WIDTH_OF_LINE = 5;
+int const default_line_space = 10;
+
template <class T>
string const write_attribute(string const & name, T const & t)
num = 0;
if (!getTokenValue(str, token, tmp))
return false;
- num = strToInt(tmp);
+ num = convert<int>(tmp);
return true;
}
}
+bool getTokenValue(string const & str, char const * token, LyXLength & len, bool & flag)
+{
+ len = LyXLength();
+ flag = false;
+ string tmp;
+ if (!getTokenValue(str, token, tmp))
+ return false;
+ if (tmp == "default") {
+ flag = true;
+ return true;
+ }
+ return isValidLength(tmp, &len);
+}
+
+
void l_getline(istream & is, string & str)
{
str.erase();
descent_of_row(0),
top_line(true),
bottom_line(false),
+ top_space_default(false),
+ bottom_space_default(false),
+ interline_space_default(false),
endhead(false),
endfirsthead(false),
endfoot(false),
LyXTabular::LyXTabular(BufferParams const & bp, row_type rows_arg,
- col_type columns_arg)
+ col_type columns_arg)
{
init(bp, rows_arg, columns_arg);
}
// activates all lines and sets all widths to 0
void LyXTabular::init(BufferParams const & bp, row_type rows_arg,
- col_type columns_arg)
+ col_type columns_arg)
{
rows_ = rows_arg;
columns_ = columns_arg;
column_info.back().right_line = true;
is_long_tabular = false;
rotate = false;
+ use_booktabs = false;
}
}
//++column;
for (row_type i = 0; i < rows_; ++i) {
- cell_info[i][column + 1].inset->clear(false);
+ cell_info[i][column + 1].inset->clear();
if (bp.tracking_changes)
cell_info[i][column + 1].inset->markNew(true);
}
}
-// returns 1 if there is a topline, returns 0 if not
bool LyXTabular::topLine(idx_type const cell, bool const onlycolumn) const
{
- if (!onlycolumn && isMultiColumn(cell))
+ if (!onlycolumn && isMultiColumn(cell) &&
+ !(use_booktabs && row_of_cell(cell) == 0))
return cellinfo_of_cell(cell).top_line;
return row_info[row_of_cell(cell)].top_line;
}
bool LyXTabular::bottomLine(idx_type const cell, bool onlycolumn) const
{
- if (!onlycolumn && isMultiColumn(cell))
+ if (!onlycolumn && isMultiColumn(cell) &&
+ !(use_booktabs && isLastRow(cell)))
return cellinfo_of_cell(cell).bottom_line;
return row_info[row_of_cell(cell)].bottom_line;
}
bool LyXTabular::leftLine(idx_type cell, bool onlycolumn) const
{
+ if (use_booktabs)
+ return false;
if (!onlycolumn && isMultiColumn(cell) &&
(isFirstCellInRow(cell) || isMultiColumn(cell-1)))
{
bool LyXTabular::rightLine(idx_type cell, bool onlycolumn) const
{
+ if (use_booktabs)
+ return false;
if (!onlycolumn && isMultiColumn(cell) &&
(isLastCellInRow(cell) || isMultiColumn(cell + 1)))
{
top = row_info[row].top_line;
}
}
+ int const interline_space = row_info[row - 1].interline_space_default ?
+ default_line_space :
+ row_info[row - 1].interline_space.inPixels(width_of_tabular);
if (top && bottom)
- return WIDTH_OF_LINE;
- return 0;
+ return interline_space + WIDTH_OF_LINE;
+ return interline_space;
}
// the last column does not have to be recalculated because all
// multicolumns will have here there last multicolumn cell which
// always will have the whole rest of the width of the cell.
- if (column > (columns_ - 2))
+ if (columns_ < 2 || column > (columns_ - 2))
return;
- for(row_type row = 0; row < rows_; ++row) {
+ for (row_type row = 0; row < rows_; ++row) {
int mc = cell_info[row][column].multicolumn;
int nmc = cell_info[row][column+1].multicolumn;
// we only have to update multicolumns which do not have this
}
-// returns 1 if a complete update is necessary, otherwise 0
void LyXTabular::setWidthOfCell(idx_type cell, int new_width)
{
row_type const row = row_of_cell(cell);
void LyXTabular::setAlignment(idx_type cell, LyXAlignment align,
- bool onlycolumn)
+ bool onlycolumn)
{
if (!isMultiColumn(cell) || onlycolumn)
column_info[column_of_cell(cell)].alignment = align;
void LyXTabular::setVAlignment(idx_type cell, VAlignment align,
- bool onlycolumn)
+ bool onlycolumn)
{
if (!isMultiColumn(cell) || onlycolumn)
column_info[column_of_cell(cell)].valignment = align;
}
-void LyXTabular::setColumnPWidth(idx_type cell, LyXLength const & width)
+namespace {
+
+/**
+ * Allow line and paragraph breaks for fixed width cells or disallow them,
+ * merge cell paragraphs and reset layout to standard for variable width
+ * cells.
+ */
+void toggleFixedWidth(LCursor & cur, InsetText * inset, bool fixedWidth)
+{
+ inset->setAutoBreakRows(fixedWidth);
+ if (fixedWidth)
+ return;
+
+ // merge all paragraphs to one
+ BufferParams const & bp =
+ inset->getText(0)->bv_owner->buffer()->params();
+ while (inset->paragraphs().size() > 1)
+ mergeParagraph(bp, inset->paragraphs(), 0);
+
+ // reset layout
+ cur.push(*inset);
+ // undo information has already been recorded
+ inset->getText(0)->setLayout(0, cur.lastpit() + 1,
+ bp.getLyXTextClass().defaultLayoutName());
+ cur.pop();
+}
+
+}
+
+
+void LyXTabular::setColumnPWidth(LCursor & cur, idx_type cell,
+ LyXLength const & width)
{
col_type const j = column_of_cell(cell);
for (row_type i = 0; i < rows_; ++i) {
idx_type const cell = getCellNumber(i, j);
// because of multicolumns
- getCellInset(cell)->setAutoBreakRows(!getPWidth(cell).zero());
+ toggleFixedWidth(cur, getCellInset(cell).get(),
+ !getPWidth(cell).zero());
}
+ // cur paragraph can become invalid after paragraphs were merged
+ if (cur.pit() > cur.lastpit())
+ cur.pit() = cur.lastpit();
+ // cur position can become invalid after newlines were removed
+ if (cur.pos() > cur.lastpos())
+ cur.pos() = cur.lastpos();
}
-bool LyXTabular::setMColumnPWidth(idx_type cell, LyXLength const & width)
+bool LyXTabular::setMColumnPWidth(LCursor & cur, idx_type cell,
+ LyXLength const & width)
{
if (!isMultiColumn(cell))
return false;
cellinfo_of_cell(cell).p_width = width;
- getCellInset(cell)->setAutoBreakRows(!width.zero());
+ toggleFixedWidth(cur, getCellInset(cell).get(), !width.zero());
+ // cur paragraph can become invalid after paragraphs were merged
+ if (cur.pit() > cur.lastpit())
+ cur.pit() = cur.lastpit();
+ // cur position can become invalid after newlines were removed
+ if (cur.pos() > cur.lastpos())
+ cur.pos() = cur.lastpos();
return true;
}
// global longtable options
os << "<features"
<< write_attribute("rotate", rotate)
+ << write_attribute("booktabs", use_booktabs)
<< write_attribute("islongtable", is_long_tabular)
<< write_attribute("firstHeadTopDL", endfirsthead.topDL)
<< write_attribute("firstHeadBottomDL", endfirsthead.bottomDL)
for (row_type i = 0; i < rows_; ++i) {
os << "<row"
<< write_attribute("topline", row_info[i].top_line)
- << write_attribute("bottomline", row_info[i].bottom_line)
- << write_attribute("endhead", row_info[i].endhead)
+ << write_attribute("bottomline", row_info[i].bottom_line);
+ static const string def("default");
+ if (row_info[i].top_space_default)
+ os << write_attribute("topspace", def);
+ else
+ os << write_attribute("topspace", row_info[i].top_space);
+ if (row_info[i].bottom_space_default)
+ os << write_attribute("bottomspace", def);
+ else
+ os << write_attribute("bottomspace", row_info[i].bottom_space);
+ if (row_info[i].interline_space_default)
+ os << write_attribute("interlinespace", def);
+ else
+ os << write_attribute("interlinespace", row_info[i].interline_space);
+ os << write_attribute("endhead", row_info[i].endhead)
<< write_attribute("endfirsthead", row_info[i].endfirsthead)
<< write_attribute("endfoot", row_info[i].endfoot)
<< write_attribute("endlastfoot", row_info[i].endlastfoot)
return;
}
getTokenValue(line, "rotate", rotate);
+ getTokenValue(line, "booktabs", use_booktabs);
getTokenValue(line, "islongtable", is_long_tabular);
getTokenValue(line, "firstHeadTopDL", endfirsthead.topDL);
getTokenValue(line, "firstHeadBottomDL", endfirsthead.bottomDL);
}
getTokenValue(line, "topline", row_info[i].top_line);
getTokenValue(line, "bottomline", row_info[i].bottom_line);
+ getTokenValue(line, "topspace", row_info[i].top_space,
+ row_info[i].top_space_default);
+ getTokenValue(line, "bottomspace", row_info[i].bottom_space,
+ row_info[i].bottom_space_default);
+ getTokenValue(line, "interlinespace", row_info[i].interline_space,
+ row_info[i].interline_space_default);
getTokenValue(line, "endfirsthead", row_info[i].endfirsthead);
getTokenValue(line, "endhead", row_info[i].endhead);
getTokenValue(line, "endfoot", row_info[i].endfoot);
void LyXTabular::setMultiColumn(Buffer * buffer, idx_type cell,
- idx_type number)
+ idx_type number)
{
cellstruct & cs = cellinfo_of_cell(cell);
cs.multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
cs.alignment = column_info[column_of_cell(cell)].alignment;
cs.top_line = row_info[row_of_cell(cell)].top_line;
cs.bottom_line = row_info[row_of_cell(cell)].bottom_line;
+ cs.left_line = column_info[column_of_cell(cell)].left_line;
cs.right_line = column_info[column_of_cell(cell+number-1)].right_line;
for (idx_type i = 1; i < number; ++i) {
cellstruct & cs1 = cellinfo_of_cell(cell + i);
cs1.multicolumn = CELL_PART_OF_MULTICOLUMN;
cs.inset->appendParagraphs(buffer, cs1.inset->paragraphs());
- cs1.inset->clear(false);
+ cs1.inset->clear();
}
set_row_column_number_info();
}
}
+void LyXTabular::setBookTabs(bool what)
+{
+ use_booktabs = what;
+}
+
+
+bool LyXTabular::useBookTabs() const
+{
+ return use_booktabs;
+}
+
+
void LyXTabular::setLongTabular(bool what)
{
is_long_tabular = what;
LyXTabular::idx_type LyXTabular::getCellNumber(row_type row,
- col_type column) const
+ col_type column) const
{
BOOST_ASSERT(column != npos && column < columns_ &&
- row != npos && row < rows_);
+ row != npos && row < rows_);
return cell_info[row][column].cellno;
}
// This are functions used for the longtable support
///
void LyXTabular::setLTHead(row_type row, bool flag, ltType const & hd,
- bool first)
+ bool first)
{
if (first) {
endfirsthead = hd;
void LyXTabular::setLTFoot(row_type row, bool flag, ltType const & fd,
- bool last)
+ bool last)
{
if (last) {
endlastfoot = fd;
if (topLine(i))
++tmp;
}
- if (tmp == n - fcell) {
- os << "\\hline ";
+ if (use_booktabs && row == 0) {
+ os << "\\toprule ";
+ } else if (tmp == n - fcell) {
+ os << (use_booktabs ? "\\midrule " : "\\hline ");
} else if (tmp) {
for (idx_type i = fcell; i < n; ++i) {
if (topLine(i)) {
- os << "\\cline{"
+ os << (use_booktabs ? "\\cmidrule{" : "\\cline{")
<< column_of_cell(i) + 1
<< '-'
<< right_column_of_cell(i) + 1
if (bottomLine(i))
++tmp;
}
- if (tmp == n - fcell) {
- os << "\\hline";
+ if (use_booktabs && row == rows_ - 1) {
+ os << "\\bottomrule";
+ } else if (tmp == n - fcell) {
+ os << (use_booktabs ? "\\midrule" : "\\hline");
} else if (tmp) {
for (idx_type i = fcell; i < n; ++i) {
if (bottomLine(i)) {
- os << "\\cline{"
+ os << (use_booktabs ? "\\cmidrule{" : "\\cline{")
<< column_of_cell(i) + 1
<< '-'
<< right_column_of_cell(i) + 1
OutputParams const & runparams) const
{
idx_type cell = getCellNumber(i, 0);
-
int ret = TeXTopHLine(os, i);
+ if (row_info[i].top_space_default) {
+ if (use_booktabs)
+ os << "\\addlinespace\n";
+ else
+ os << "\\noalign{\\vskip\\doublerulesep}\n";
+ } else if(!row_info[i].top_space.zero()) {
+ if (use_booktabs)
+ os << "\\addlinespace["
+ << row_info[i].top_space.asLatexString() << "]\n";
+ else {
+ os << "\\noalign{\\vskip"
+ << row_info[i].top_space.asLatexString() << "}\n";
+ }
+ ++ret;
+ }
+
for (col_type j = 0; j < columns_; ++j) {
if (isPartOfMultiColumn(i, j))
continue;
}
++cell;
}
- os << "\\tabularnewline\n";
+ os << "\\tabularnewline";
+ if (row_info[i].bottom_space_default) {
+ if (use_booktabs)
+ os << "\\addlinespace";
+ else
+ os << "[\\doublerulesep]";
+ } else if (!row_info[i].bottom_space.zero()) {
+ if (use_booktabs)
+ os << "\\addlinespace";
+ os << '[' << row_info[i].bottom_space.asLatexString() << ']';
+ }
+ os << '\n';
++ret;
ret += TeXBottomHLine(os, i);
+ if (row_info[i].interline_space_default) {
+ if (use_booktabs)
+ os << "\\addlinespace\n";
+ else
+ os << "\\noalign{\\vskip\\doublerulesep}\n";
+ } else if (!row_info[i].interline_space.zero()) {
+ if (use_booktabs)
+ os << "\\addlinespace["
+ << row_info[i].interline_space.asLatexString()
+ << "]\n";
+ else
+ os << "\\noalign{\\vskip"
+ << row_info[i].interline_space.asLatexString()
+ << "}\n";
+ ++ret;
+ }
return ret;
}
if (!column_info[i].align_special.empty()) {
os << column_info[i].align_special;
} else {
- if (column_info[i].left_line)
+ if (!use_booktabs && column_info[i].left_line)
os << '|';
if (!column_info[i].p_width.zero()) {
switch (column_info[i].alignment) {
break;
}
}
- if (column_info[i].right_line)
+ if (!use_booktabs && column_info[i].right_line)
os << '|';
}
}
shared_ptr<InsetText> LyXTabular::getCellInset(row_type row,
- col_type column) const
+ col_type column) const
{
return cell_info[row][column].inset;
}
+void LyXTabular::setCellInset(row_type row, col_type column,
+ shared_ptr<InsetText> ins) const
+{
+ cell_info[row][column].inset = ins;
+}
+
+
LyXTabular::idx_type
LyXTabular::getCellFromInset(InsetBase const * inset) const
{
void LyXTabular::validate(LaTeXFeatures & features) const
{
features.require("NeedTabularnewline");
+ if (useBookTabs())
+ features.require("booktabs");
if (isLongTabular())
features.require("longtable");
if (needRotating())
}
-void LyXTabular::getLabelList(Buffer const & buffer,
- std::vector<string> & list) const
-{
- for (row_type i = 0; i < rows_; ++i)
- for (col_type j = 0; j < columns_; ++j)
- getCellInset(i, j)->getLabelList(buffer, list);
-}
-
-
LyXTabular::BoxType LyXTabular::useParbox(idx_type cell) const
{
ParagraphList const & parlist = getCellInset(cell)->paragraphs();