Uwe Stöhr wrote:
You have done some additional simplifications. With your version, setting a multicolumn is no longer possible. Can you please have a look?
works fine here
However there are many other minor issues to solve: - The content of the multirow is not horizontally centered
you mean in the latex output?
- Adding/deleting columns while the cursor is in a table row containing a multirow cell is not properly working.
should be fixed in attached
- plain text export needs to be done
also the multirow package need to be loaded when necessary
I propose that I or you put the patch into branch in a few days and we continue to fine-tune the remaining issues. The patch is already very big and I fear we might loose the track if we work only with patches. What do you think?
i won't have time to work on this before the end of next week note that atm copy/pasting is bonked... latest iteration attached, maybe create a branch for the time being? edwin
Index: src/insets/InsetTabular.cpp =================================================================== --- src/insets/InsetTabular.cpp (revision 33411) +++ src/insets/InsetTabular.cpp (working copy) @@ -70,7 +70,6 @@ using namespace lyx::support; using boost::shared_ptr; -using boost::dynamic_pointer_cast; namespace lyx { @@ -130,6 +129,7 @@ { Tabular::M_VALIGN_BOTTOM, "m-valign-bottom" }, { Tabular::M_VALIGN_MIDDLE, "m-valign-middle" }, { Tabular::MULTICOLUMN, "multicolumn" }, + { Tabular::MULTIROW, "multirow" }, { Tabular::SET_ALL_LINES, "set-all-lines" }, { Tabular::UNSET_ALL_LINES, "unset-all-lines" }, { Tabular::SET_LONGTABULAR, "set-longtabular" }, @@ -154,7 +154,8 @@ { Tabular::SET_LTNEWPAGE, "set-ltnewpage" }, { Tabular::TOGGLE_LTCAPTION, "toggle-ltcaption" }, { Tabular::SET_SPECIAL_COLUMN, "set-special-column" }, - { Tabular::SET_SPECIAL_MULTI, "set-special-multi" }, + { Tabular::SET_SPECIAL_MULTICOLUMN, "set-special-multicolumn" }, + { Tabular::SET_SPECIAL_MULTIROW, "set-special-multirow" }, { Tabular::SET_BOOKTABS, "set-booktabs" }, { Tabular::UNSET_BOOKTABS, "unset-booktabs" }, { Tabular::SET_TOP_SPACE, "set-top-space" }, @@ -519,6 +520,7 @@ : cellno(0), width(0), multicolumn(Tabular::CELL_NORMAL), + multirow(Tabular::CELL_NORMAL), alignment(LYX_ALIGN_CENTER), valignment(LYX_VALIGN_TOP), top_line(false), @@ -537,6 +539,7 @@ : cellno(cs.cellno), width(cs.width), multicolumn(cs.multicolumn), + multirow(cs.multirow), alignment(cs.alignment), valignment(cs.valignment), top_line(cs.top_line), @@ -562,6 +565,7 @@ std::swap(cellno, rhs.cellno); std::swap(width, rhs.width); std::swap(multicolumn, rhs.multicolumn); + std::swap(multirow, rhs.multirow); std::swap(alignment, rhs.alignment); std::swap(valignment, rhs.valignment); std::swap(top_line, rhs.top_line); @@ -657,30 +661,25 @@ void Tabular::appendRow(idx_type const cell) { - BufferParams const & bp = buffer_->params(); row_type const row = cellRow(cell); - row_vector::iterator rit = row_info.begin() + row; - row_info.insert(rit, RowData()); - // now set the values of the row before - row_info[row] = row_info[row + 1]; + row_info.insert(row_info.begin() + row + 1, RowData()); + row_info[row + 1] = row_info[row]; - row_type const nrows = row_info.size(); col_type const ncols = column_info.size(); + cell_info.insert(cell_info.begin() + row + 1, + cell_vector(ncols, CellData(buffer_))); + for (col_type c = 0; c < ncols; ++c) { + if (cell_info[row][c].multirow == CELL_BEGIN_OF_MULTIROW) + cell_info[row + 1][c].multirow = CELL_PART_OF_MULTIROW; + else + cell_info[row + 1][c].multirow = cell_info[row][c].multirow; + } + updateIndexes(); - cell_vvector old(nrows - 1); - for (row_type i = 0; i < nrows - 1; ++i) - swap(cell_info[i], old[i]); - - cell_info = cell_vvector(nrows, cell_vector(ncols, CellData(buffer_))); - - for (row_type i = 0; i <= row; ++i) - swap(cell_info[i], old[i]); - for (row_type i = row + 2; i < nrows; ++i) - swap(cell_info[i], old[i - 1]); - - updateIndexes(); for (col_type c = 0; c < ncols; ++c) { + if (isPartOfMultiRow(row, c)) + continue; // inherit line settings idx_type const i = cellIndex(row + 1, c); idx_type const j = cellIndex(row, c); @@ -692,7 +691,7 @@ setBottomLine(j, false); } // mark track changes - if (bp.trackChanges) + if (buffer().params().trackChanges) cellInfo(i).inset->setChange(Change(Change::INSERTED)); } } @@ -704,6 +703,15 @@ if (row_info.size() == 1) return; + size_t const column_count = column_info.size(); + for (col_type i = 0; i < column_count; ++i) { + // Care about multirow cells + if (row + 1 < row_info.size() && + cell_info[row][i].multirow == CELL_BEGIN_OF_MULTIROW && + cell_info[row][i + 1].multirow == CELL_PART_OF_MULTIROW) { + cell_info[row][i + 1].multirow = CELL_BEGIN_OF_MULTIROW; + } + } row_info.erase(row_info.begin() + row); cell_info.erase(cell_info.begin() + row); updateIndexes(); @@ -726,23 +734,18 @@ void Tabular::appendColumn(idx_type const cell) { col_type const c = cellColumn(cell); - column_vector::iterator cit = column_info.begin() + c + 1; - column_info.insert(cit, ColumnData()); - row_type const nrows = row_info.size(); - // set the column values of the column before + + column_info.insert(column_info.begin() + c + 1, ColumnData()); column_info[c + 1] = column_info[c]; + row_type const nrows = row_info.size(); for (row_type r = 0; r < nrows; ++r) { cell_info[r].insert(cell_info[r].begin() + c + 1, CellData(buffer_)); -#if 0 -// FIXME: This code does not work. It deletes the cell's content and -// it triggers an assertion if the cursor is at pos > 0. if (cell_info[r][c].multicolumn == CELL_BEGIN_OF_MULTICOLUMN) cell_info[r][c + 1].multicolumn = CELL_PART_OF_MULTICOLUMN; else cell_info[r][c + 1].multicolumn = cell_info[r][c].multicolumn; -#endif } updateIndexes(); for (row_type r = 0; r < nrows; ++r) { @@ -756,47 +759,44 @@ setRightLine(i, true); setRightLine(j, false); } - // - cellInfo(i).inset->clear(); if (buffer().params().trackChanges) cellInfo(i).inset->setChange(Change(Change::INSERTED)); } } -void Tabular::deleteColumn(col_type const column) +void Tabular::deleteColumn(col_type const col) { // Not allowed to delete last column if (column_info.size() == 1) return; - size_t const row_count = row_info.size(); - for (row_type i = 0; i < row_count; ++i) { + row_type const nrows = row_info.size(); + for (row_type r = 0; r < nrows; ++r) { // Care about multicolumn cells - if (column + 1 < column_info.size() && - cell_info[i][column].multicolumn == CELL_BEGIN_OF_MULTICOLUMN && - cell_info[i][column + 1].multicolumn == CELL_PART_OF_MULTICOLUMN) { - cell_info[i][column + 1].multicolumn = CELL_BEGIN_OF_MULTICOLUMN; + if (col + 1 < column_info.size() && + 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[i].erase(cell_info[i].begin() + column); + cell_info[r].erase(cell_info[r].begin() + col); } - column_info.erase(column_info.begin() + column); + column_info.erase(column_info.begin() + col); updateIndexes(); } -void Tabular::copyColumn(col_type const column) +void Tabular::copyColumn(col_type const col) { BufferParams const & bp = buffer().params(); - column_info.insert(column_info.begin() + column, column_info[column]); + column_info.insert(column_info.begin() + col, column_info[col]); - size_t row_count = row_info.size(); - for (row_type i = 0; i < row_count; ++i) - cell_info[i].insert(cell_info[i].begin() + column, cell_info[i][column]); - - if (bp.trackChanges) - for (row_type i = 0; i < row_count; ++i) - cell_info[i][column + 1].inset->setChange(Change(Change::INSERTED)); + row_type nrows = row_info.size(); + for (row_type r = 0; r < nrows; ++r) { + cell_info[r].insert(cell_info[r].begin() + col, cell_info[r][col]); + if (bp.trackChanges) + cell_info[r][col + 1].inset->setChange(Change(Change::INSERTED)); + } updateIndexes(); } @@ -809,9 +809,13 @@ numberofcells = 0; for (row_type row = 0; row < nrows; ++row) for (col_type column = 0; column < ncols; ++column) { - if (!isPartOfMultiColumn(row, column)) + if (!isPartOfMultiColumn(row, column) + && !isPartOfMultiRow(row, column)) ++numberofcells; - cell_info[row][column].cellno = numberofcells - 1; + if (isPartOfMultiRow(row, column)) + cell_info[row][column].cellno = cell_info[row - 1][column].cellno; + else + cell_info[row][column].cellno = numberofcells - 1; } rowofcell.resize(numberofcells); @@ -819,7 +823,8 @@ idx_type i = 0; for (row_type row = 0; row < nrows; ++row) for (col_type column = 0; column < ncols; ++column) { - if (isPartOfMultiColumn(row, column)) + if (isPartOfMultiColumn(row, column) + || isPartOfMultiRow(row, column)) continue; rowofcell[i] = row; columnofcell[i] = column; @@ -895,7 +900,7 @@ } -int Tabular::getAdditionalHeight(row_type row) const +int Tabular::interRowSpace(row_type row) const { if (!row || row >= row_info.size()) return 0; @@ -909,11 +914,11 @@ } -int Tabular::getAdditionalWidth(idx_type cell) const +int Tabular::interColumnSpace(idx_type cell) const { col_type const nextcol = cellColumn(cell) + columnSpan(cell); - if (rightLine(cell) - && nextcol < column_info.size() && leftLine(cellIndex(cellRow(cell), nextcol))) + if (rightLine(cell) && nextcol < column_info.size() + && leftLine(cellIndex(cellRow(cell), nextcol))) return WIDTH_OF_LINE; return 0; } @@ -930,6 +935,24 @@ } +int Tabular::rowHeight(idx_type cell) const +{ + row_type const span = rowSpan(cell); + row_type const row = cellRow(cell); + int h = rowAscent(row) + rowDescent(row); + + for(row_type r = row; r < row + span ; ++r) { + if (r > row) { + h += rowAscent(r); + h += interRowSpace(r); + } + if (r < row + span - 1) + h += rowDescent(r); + } + return h; +} + + bool Tabular::updateColumnWidths() { col_type const ncols = column_info.size(); @@ -984,7 +1007,7 @@ void Tabular::setCellWidth(idx_type cell, int new_width) { cellInfo(cell).width = new_width + 2 * WIDTH_OF_LINE - + getAdditionalWidth(cell); + + interColumnSpace(cell); } @@ -1098,7 +1121,7 @@ void Tabular::setAlignSpecial(idx_type cell, docstring const & special, Tabular::Feature what) { - if (what == SET_SPECIAL_MULTI) + if (what == SET_SPECIAL_MULTICOLUMN) cellInfo(cell).align_special = special; else column_info[cellColumn(cell)].align_special = special; @@ -1137,24 +1160,27 @@ cellInfo(cell).right_line = line; } + bool Tabular::rowTopLine(row_type r) const { - idx_type i0 = getFirstCellInRow(r); - idx_type i1 = getLastCellInRow(r); + col_type const ncols = column_info.size(); bool all_rows_set = true; - for (idx_type j = i0; all_rows_set && j <= i1; ++j) - all_rows_set = cellInfo(j).top_line; + for (col_type c = 0; all_rows_set && c < ncols; ++c) { + idx_type const i = cellIndex(r, c); + all_rows_set = cellInfo(i).top_line; + } return all_rows_set; } bool Tabular::rowBottomLine(row_type r) const { - idx_type i0 = getFirstCellInRow(r); - idx_type i1 = getLastCellInRow(r); + col_type const ncols = column_info.size(); bool all_rows_set = true; - for (idx_type j = i0; all_rows_set && j <= i1; ++j) - all_rows_set = cellInfo(j).bottom_line; + for (col_type c = 0; all_rows_set && c < ncols; ++c) { + idx_type const i = cellIndex(r, c); + all_rows_set = cellInfo(i).bottom_line; + } return all_rows_set; } @@ -1234,9 +1260,10 @@ } -int Tabular::getBeginningOfTextInCell(idx_type cell) const +int Tabular::textHOffset(idx_type cell) const { - int x = 0; + // the LaTeX Way :-( + int x = WIDTH_OF_LINE; switch (getAlignment(cell)) { case LYX_ALIGN_CENTER: @@ -1244,22 +1271,35 @@ break; case LYX_ALIGN_RIGHT: x += columnWidth(cell) - cellWidth(cell); - // + getAdditionalWidth(cell); + // + interColumnSpace(cell); break; default: // LYX_ALIGN_LEFT: nothing :-) break; } - // the LaTeX Way :-( - x += WIDTH_OF_LINE; return x; } -bool Tabular::isFirstCellInRow(idx_type cell) const +int Tabular::textVOffset(idx_type cell) const { - return cellColumn(cell) == 0; + row_type const row = cellRow(cell); + int h = rowHeight(cell) - rowAscent(row) - rowDescent(row); + + int y = 0; + switch (getVAlignment(cell)) { + case LYX_VALIGN_TOP: + break; + case LYX_VALIGN_MIDDLE: + y += h/2; + break; + case LYX_VALIGN_BOTTOM: + y += h; + break; + } + + return y; } @@ -1267,7 +1307,12 @@ { if (row > row_info.size() - 1) row = row_info.size() - 1; - return cell_info[row][0].cellno; + + col_type c = 0; + while (cell_info[row][c].multirow == CELL_PART_OF_MULTIROW) + ++c; + + return cell_info[row][c].cellno; } @@ -1379,6 +1424,7 @@ for (col_type j = 0; j < column_info.size(); ++j) { os << "<cell" << write_attribute("multicolumn", cell_info[i][j].multicolumn) + << write_attribute("multirow", cell_info[i][j].multirow) << write_attribute("alignment", cell_info[i][j].alignment) << write_attribute("valignment", cell_info[i][j].valignment) << write_attribute("topline", cell_info[i][j].top_line) @@ -1486,6 +1532,7 @@ return; } getTokenValue(line, "multicolumn", cell_info[i][j].multicolumn); + getTokenValue(line, "multirow", cell_info[i][j].multirow); getTokenValue(line, "alignment", cell_info[i][j].alignment); getTokenValue(line, "valignment", cell_info[i][j].valignment); getTokenValue(line, "topline", cell_info[i][j].top_line); @@ -1525,14 +1572,19 @@ bool Tabular::isMultiColumn(idx_type cell) const { - return cellInfo(cell).multicolumn != CELL_NORMAL; + if (cellInfo(cell).multicolumn == CELL_BEGIN_OF_MULTICOLUMN + || cellInfo(cell).multicolumn == CELL_PART_OF_MULTICOLUMN) + return true; + else + return false; } bool Tabular::isMultiColumnReal(idx_type cell) const { return cellColumn(cell) != cellRightColumn(cell) && - cellInfo(cell).multicolumn != CELL_NORMAL; + (cellInfo(cell).multicolumn == CELL_BEGIN_OF_MULTICOLUMN + || cellInfo(cell).multicolumn == CELL_PART_OF_MULTICOLUMN); } @@ -1558,40 +1610,79 @@ } +bool Tabular::isMultiRow(idx_type cell) const +{ + return (cellInfo(cell).multirow == CELL_BEGIN_OF_MULTIROW + || cellInfo(cell).multirow == CELL_PART_OF_MULTIROW); +} + + +void Tabular::setMultiRow(idx_type cell, idx_type number) +{ + idx_type const column = cellColumn(cell); + idx_type const row = cellRow(cell); + idx_type const ncolumns = column_info.size(); + CellData & cs = cellInfo(cell); + cs.multirow = CELL_BEGIN_OF_MULTIROW; + // FIXME: the horizontal alignment can only be changed for + // the whole table, support for this needs to be implemented + // assigning this to uwestoehr + cs.valignment = LYX_VALIGN_MIDDLE; + // set the bottom row of the last selected cell + setBottomLine(cell, bottomLine(cell + (number - 1)*ncolumns)); + for (idx_type i = 1; i < number; ++i) { + CellData & cs1 = cell_info[row + i][column]; + cs1.multirow = CELL_PART_OF_MULTIROW; + cs.inset->appendParagraphs(cs1.inset->paragraphs()); + cs1.inset->clear(); + } + updateIndexes(); +} + + Tabular::idx_type Tabular::columnSpan(idx_type cell) const { row_type const row = cellRow(cell); col_type const ncols = column_info.size(); - idx_type result = 1; col_type column = cellColumn(cell) + 1; - while (column < ncols && isPartOfMultiColumn(row, column)) { - ++result; + while (column < ncols && isPartOfMultiColumn(row, column)) ++column; - } - return result; + + return column - cellColumn(cell); } -Tabular::idx_type Tabular::unsetMultiColumn(idx_type cell) +Tabular::idx_type Tabular::rowSpan(idx_type cell) const { + col_type const column = cellColumn(cell); + row_type const nrows = row_info.size(); + col_type row = cellRow(cell) + 1; + while (row < nrows && isPartOfMultiRow(row, column)) + ++row; + + return row - cellRow(cell); +} + + +void Tabular::unsetMultiColumn(idx_type cell) +{ row_type const row = cellRow(cell); - col_type column = cellColumn(cell); + col_type const column = cellColumn(cell); + row_type const span = columnSpan(cell); + for (col_type c = 0; c < span; ++c) + cell_info[row][column + c].multicolumn = CELL_NORMAL; + updateIndexes(); +} - idx_type result = 0; - if (cell_info[row][column].multicolumn == CELL_BEGIN_OF_MULTICOLUMN) { - cell_info[row][column].multicolumn = CELL_NORMAL; - ++column; - while (column < column_info.size() && - cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN) - { - cell_info[row][column].multicolumn = CELL_NORMAL; - ++column; - ++result; - } - } +void Tabular::unsetMultiRow(idx_type cell) +{ + row_type const row = cellRow(cell); + col_type const column = cellColumn(cell); + row_type const span = rowSpan(cell); + for (row_type r = 0; r < span; ++r) + cell_info[row + r][column].multirow = CELL_NORMAL; updateIndexes(); - return result; } @@ -1637,17 +1728,17 @@ Tabular::idx_type Tabular::cellBelow(idx_type cell) const { - if (cellRow(cell) + 1 < row_info.size()) - return cell_info[cellRow(cell)+1][cellColumn(cell)].cellno; + row_type const nextrow = cellRow(cell) + rowSpan(cell); + if (nextrow < row_info.size()) + return cell_info[nextrow][cellColumn(cell)].cellno; return cell; } -Tabular::idx_type Tabular::cellIndex(row_type row, - col_type column) const +Tabular::idx_type Tabular::cellIndex(row_type row, col_type column) const { - BOOST_ASSERT(column != npos && column < column_info.size() - && row != npos && row < row_info.size()); + LASSERT(column != npos && column < column_info.size() + && row != npos && row < row_info.size(), /**/); return cell_info[row][column].cellno; } @@ -1842,8 +1933,7 @@ int Tabular::rowAscent(row_type row) const { - if (row >= row_info.size()) - return 0; + LASSERT(row < row_info.size(), /**/); return row_info[row].ascent; } @@ -1860,7 +1950,7 @@ int height = 0; for (row_type row = 0; row < row_info.size(); ++row) height += rowAscent(row) + rowDescent(row) + - getAdditionalHeight(row); + interRowSpace(row); return height; } @@ -1873,6 +1963,14 @@ } +bool Tabular::isPartOfMultiRow(row_type row, col_type column) const +{ + LASSERT(row < row_info.size(), /**/); + LASSERT(column < column_info.size(), /**/); + return cell_info[row][column].multirow == CELL_PART_OF_MULTIROW; +} + + int Tabular::TeXTopHLine(odocstream & os, row_type row, string const lang) const { // we only output complete row lines and the 1st row here, the rest @@ -1884,6 +1982,13 @@ col_type nset = 0; for (col_type c = 0; c < ncols; ++c) { topline.push_back(topLine(cellIndex(row, c))); + // If cell is part of a multirow and not the first cell of the + // multirow, no line must be drawn. + if (row != 0) + if (isMultiRow(cellIndex(row, c))) { + if (isMultiRow(cellIndex(row - 1, c))) + topline[c] = false; + } if (topline[c]) ++nset; } @@ -1935,6 +2040,15 @@ for (col_type c = 0; c < ncols; ++c) { bottomline.push_back(bottomLine(cellIndex(row, c))); topline.push_back(!lastrow && topLine(cellIndex(row + 1, c))); + // If cell is part of a multirow and not the last or first cell of the + // multirow, no line must be drawn. + if (!lastrow) + if (isMultiRow(cellIndex(row, c))) { + if (isMultiRow(cellIndex(row + 1, c))) { + bottomline[c] = false; + topline[c] = false; + } + } nextrowset &= topline[c]; } @@ -1955,7 +2069,7 @@ if (use_booktabs) os << (lastrow ? "\\bottomrule" : "\\midrule"); else - os << "\\hline"; + os << "\\hline "; } else { for (col_type c = 0; c < ncols; ++c) { if (bottomline[c]) { @@ -1979,7 +2093,8 @@ } -int Tabular::TeXCellPreamble(odocstream & os, idx_type cell, bool & ismulticol) const +int Tabular::TeXCellPreamble(odocstream & os, idx_type cell, + bool & ismulticol, bool & ismultirow) const { int ret = 0; row_type const r = cellRow(cell); @@ -1999,7 +2114,8 @@ && leftLine(cellIndex(r, nextcol)); bool coldouble = colright && nextcolleft; bool celldouble = rightLine(cell) && nextcellleft; - ismulticol = isMultiColumn(cell) + + ismulticol = isMultiColumn(cell) || (c == 0 && colleft != leftLine(cell)) || ((colright || nextcolleft) && !rightLine(cell) && !nextcellleft) || (!colright && !nextcolleft && (rightLine(cell) || nextcellleft)) @@ -2059,7 +2175,21 @@ // add extra vertical line if we want a double one os << '|'; os << "}{"; - } + } // end if ismulticol + + // we only need code for the first multirow cell + ismultirow = isMultiRow(cell); + if (ismultirow) { + os << "\\multirow{" << rowSpan(cell) << "}{"; + if (!getPWidth(cell).zero()) + os << from_ascii(getPWidth(cell).asLatexString()); + else + // we need to set a default value + // needs to be discussed + os << "2.0cm"; + os << "}{"; + } // end if ismultirow + if (getRotateCell(cell)) { os << "\\begin{sideways}\n"; ++ret; @@ -2100,7 +2230,8 @@ } -int Tabular::TeXCellPostamble(odocstream & os, idx_type cell, bool ismulticol) const +int Tabular::TeXCellPostamble(odocstream & os, idx_type cell, + bool ismulticol, bool ismultirow) const { int ret = 0; row_type const r = cellRow(cell); @@ -2118,7 +2249,7 @@ os << "%\n\\end{sideways}"; ++ret; } - if (ismulticol) { + if (ismulticol || ismultirow) { os << '}'; } return ret; @@ -2267,10 +2398,14 @@ ++ret; } bool ismulticol = false; + bool ismultirow = false; for (col_type j = 0; j < column_info.size(); ++j) { - if (isPartOfMultiColumn(i, j)) + if (isPartOfMultiRow(i, j)) + os << " & "; + if (isPartOfMultiColumn(i, j) || isPartOfMultiRow(i, j)) continue; - ret += TeXCellPreamble(os, cell, ismulticol); + cell = cellIndex(i, j); + ret += TeXCellPreamble(os, cell, ismulticol, ismultirow); shared_ptr<InsetTableCell> inset = cellInset(cell); Paragraph const & par = inset->paragraphs().front(); @@ -2302,11 +2437,10 @@ if (rtl) os << '}'; - ret += TeXCellPostamble(os, cell, ismulticol); + ret += TeXCellPostamble(os, cell, ismulticol, ismultirow); if (!isLastCellInRow(cell)) { // not last cell in row os << " & "; } - ++cell; } if (row_info[i].caption && !endfirsthead.empty && !haveLTFirstHead()) // if no first header and no empty first header is used, @@ -3117,11 +3251,9 @@ int h = yo(cur.bv()) - tabular.rowAscent(0); size_t nrows = tabular.row_info.size(); row_type r = 0; - for (; r < nrows && y > h; ++r) { - h += tabular.rowAscent(r); - h += tabular.rowDescent(r); - h += tabular.getAdditionalHeight(r); - } + for (; r < nrows && y > h; ++r) + h += tabular.rowAscent(r) + tabular.rowDescent(r) + + tabular.interRowSpace(r); return r - 1; } @@ -3147,14 +3279,15 @@ LASSERT(false, /**/); } - row_type i = 0; - for (idx_type cell = 0; i < tabular.row_info.size(); ++i) { + for (row_type i = 0; i < tabular.row_info.size(); ++i) { int maxAsc = 0; int maxDesc = 0; for (col_type j = 0; j < tabular.column_info.size(); ++j) { - if (tabular.isPartOfMultiColumn(i, j)) - // Multicolumn cell, but not first one + if (tabular.isPartOfMultiColumn(i, j) + || tabular.isPartOfMultiRow(i, j)) + // multicolumn or multirow cell, but not first one continue; + idx_type const cell = tabular.cellIndex(i, j); Dimension dim; MetricsInfo m = mi; Length p_width; @@ -3171,7 +3304,6 @@ tabular.setCellWidth(cell, dim.wid); maxAsc = max(maxAsc, dim.asc); maxDesc = max(maxDesc, dim.des); - ++cell; } int const top_space = tabular.row_info[i].top_space_default ? default_line_space : @@ -3217,59 +3349,49 @@ void InsetTabular::draw(PainterInfo & pi, int x, int y) const { - //lyxerr << "InsetTabular::draw: " << x << " " << y << endl; + x += scx_ + ADD_TO_TABULAR_WIDTH; + BufferView * bv = pi.base.bv; Cursor & cur = pi.base.bv->cursor(); + resetPos(cur); // FIXME: As the full background is painted in drawSelection(), // we have no choice but to do a full repaint for the Text cells. pi.full_repaint = true; - - resetPos(bv->cursor()); - - x += scx_; - x += ADD_TO_TABULAR_WIDTH; - - bool const original_drawing_state = pi.pain.isDrawingEnabled(); bool const original_selection_state = pi.selected; idx_type idx = 0; first_visible_cell = Tabular::npos; for (row_type i = 0; i < tabular.row_info.size(); ++i) { int nx = x; - int const a = tabular.rowAscent(i); - int const d = tabular.rowDescent(i); - idx = tabular.cellIndex(i, 0); for (col_type j = 0; j < tabular.column_info.size(); ++j) { if (tabular.isPartOfMultiColumn(i, j)) continue; + + idx = tabular.cellIndex(i, j); + + if (tabular.isPartOfMultiRow(i, j)) { + nx += tabular.columnWidth(idx); + continue; + } + if (first_visible_cell == Tabular::npos) first_visible_cell = idx; pi.selected |= isCellSelected(cur, i, j); - int const cx = nx + tabular.getBeginningOfTextInCell(idx); + int const cx = nx + tabular.textHOffset(idx); + int const cy = y + tabular.textVOffset(idx); // Cache the Inset position. bv->coordCache().insets().add(cell(idx).get(), cx, y); - if (nx + tabular.columnWidth(idx) < 0 - || nx > bv->workWidth() - || y + d < 0 - || y - a > bv->workHeight()) { - pi.pain.setDrawingEnabled(false); - cell(idx)->draw(pi, cx, y); - drawCellLines(pi.pain, nx, y, i, idx, pi.change_); - pi.pain.setDrawingEnabled(original_drawing_state); - } else { - cell(idx)->draw(pi, cx, y); - drawCellLines(pi.pain, nx, y, i, idx, pi.change_); - } + cell(idx)->draw(pi, cx, cy); + drawCellLines(pi.pain, nx, y, i, idx, pi.change_); nx += tabular.columnWidth(idx); - ++idx; pi.selected = original_selection_state; } if (i + 1 < tabular.row_info.size()) - y += d + tabular.rowAscent(i + 1) + - tabular.getAdditionalHeight(i + 1); + y += tabular.rowDescent(i) + tabular.rowAscent(i + 1) + + tabular.interRowSpace(i + 1); } } @@ -3302,27 +3424,32 @@ if (cur.selIsMultiCell() || full_cell_selected) { y -= tabular.rowAscent(0); - for (row_type j = 0; j < tabular.row_info.size(); ++j) { - int const a = tabular.rowAscent(j); - int const h = a + tabular.rowDescent(j); + for (row_type r = 0; r < tabular.row_info.size(); ++r) { int xx = x; - y += tabular.getAdditionalHeight(j); - for (col_type i = 0; i < tabular.column_info.size(); ++i) { - if (tabular.isPartOfMultiColumn(j, i)) + for (col_type c = 0; c < tabular.column_info.size(); ++c) { + if (tabular.isPartOfMultiColumn(r, c)) continue; - idx_type const cell = - tabular.cellIndex(j, i); + + idx_type const cell = tabular.cellIndex(r, c); + + if (tabular.isPartOfMultiRow(r, c)) { + xx += tabular.columnWidth(cell); + continue; + } int const w = tabular.columnWidth(cell); - if (isCellSelected(cur, j, i)) + int const h = tabular.rowHeight(cell); + if (isCellSelected(cur, r, c)) pi.pain.fillRectangle(xx, y, w, h, Color_selection); xx += w; } - y += h; + if (r + 1 < tabular.row_info.size()) + y += tabular.rowDescent(r) + tabular.rowAscent(r + 1) + + tabular.interRowSpace(r + 1); } } else { x += cellXPos(cur.idx()); - x += tabular.getBeginningOfTextInCell(cur.idx()); + x += tabular.textHOffset(cur.idx()); cell(cur.idx())->drawSelection(pi, x, 0 /* ignored */); } } @@ -3331,7 +3458,9 @@ void InsetTabular::drawCellLines(Painter & pain, int x, int y, row_type row, idx_type cell, Change const & change) const { - int x2 = x + tabular.columnWidth(cell); + y = y - tabular.rowAscent(row); + int const w = tabular.columnWidth(cell); + int const h = tabular.rowHeight(cell); bool on_off = false; Color col = Color_tabularline; Color onoffcol = Color_tabularonoffline; @@ -3343,28 +3472,23 @@ if (!tabular.topAlreadyDrawn(cell)) { on_off = !tabular.topLine(cell); - pain.line(x, y - tabular.rowAscent(row), - x2, y - tabular.rowAscent(row), + pain.line(x, y, x + w, y, on_off ? onoffcol : col, on_off ? Painter::line_onoffdash : Painter::line_solid); } on_off = !tabular.bottomLine(cell); - pain.line(x, y + tabular.rowDescent(row), - x2, y + tabular.rowDescent(row), + pain.line(x, y + h, x + w, y + h, on_off ? onoffcol : col, on_off ? Painter::line_onoffdash : Painter::line_solid); if (!tabular.leftAlreadyDrawn(cell)) { on_off = !tabular.leftLine(cell); - pain.line(x, y - tabular.rowAscent(row), - x, y + tabular.rowDescent(row), + pain.line(x, y, x, y + h, on_off ? onoffcol : col, on_off ? Painter::line_onoffdash : Painter::line_solid); } on_off = !tabular.rightLine(cell); - pain.line(x2 - tabular.getAdditionalWidth(cell), - y - tabular.rowAscent(row), - x2 - tabular.getAdditionalWidth(cell), - y + tabular.rowDescent(row), + pain.line(x + w - tabular.interColumnSpace(cell), y, + x + w - tabular.interColumnSpace(cell), y + h, on_off ? onoffcol : col, on_off ? Painter::line_onoffdash : Painter::line_solid); } @@ -3944,7 +4068,8 @@ case Tabular::SET_PWIDTH: case Tabular::SET_MPWIDTH: case Tabular::SET_SPECIAL_COLUMN: - case Tabular::SET_SPECIAL_MULTI: + case Tabular::SET_SPECIAL_MULTICOLUMN: + case Tabular::SET_SPECIAL_MULTIROW: case Tabular::APPEND_ROW: case Tabular::APPEND_COLUMN: case Tabular::DELETE_ROW: @@ -3965,6 +4090,14 @@ status.setOnOff(tabular.isMultiColumn(cur.idx())); break; + case Tabular::MULTIROW: + // If a row is set as longtable caption, it must not be allowed + // to unset that this row is a multirow. + status.setEnabled(sel_col_start == sel_col_end + && !tabular.ltCaption(tabular.cellRow(cur.idx()))); + status.setOnOff(tabular.isMultiRow(cur.idx())); + break; + case Tabular::SET_ALL_LINES: case Tabular::UNSET_ALL_LINES: case Tabular::SET_BORDER_LINES: @@ -4353,27 +4486,25 @@ { cell(sl.idx())->cursorPos(bv, sl, boundary, x, y); + int const row = tabular.cellRow(sl.idx()); + int const col = tabular.cellColumn(sl.idx()); + // y offset correction - int const row = tabular.cellRow(sl.idx()); for (int i = 0; i <= row; ++i) { if (i != 0) { y += tabular.rowAscent(i); - y += tabular.getAdditionalHeight(i); + y += tabular.interRowSpace(i); } if (i != row) y += tabular.rowDescent(i); } + y += tabular.textVOffset(sl.idx()); // x offset correction - int const col = tabular.cellColumn(sl.idx()); - int idx = tabular.cellIndex(row, 0); - for (int j = 0; j < col; ++j) { - if (tabular.isPartOfMultiColumn(row, j)) - continue; - x += tabular.columnWidth(idx); - ++idx; - } - x += tabular.getBeginningOfTextInCell(idx); + for (int c = 0; c < col; ++c) + x += tabular.column_info[c].width; + + x += tabular.textHOffset(sl.idx()); x += ADD_TO_TABULAR_WIDTH; x += scx_; } @@ -4385,11 +4516,11 @@ int yy = 0; Inset const & inset = *tabular.cellInset(cell); Point o = bv.coordCache().getInsets().xy(&inset); - int const xbeg = o.x_ - tabular.getBeginningOfTextInCell(cell); + int const xbeg = o.x_ - tabular.textHOffset(cell); int const xend = xbeg + tabular.columnWidth(cell); row_type const row = tabular.cellRow(cell); int const ybeg = o.y_ - tabular.rowAscent(row) - - tabular.getAdditionalHeight(row); + tabular.interRowSpace(row); int const yend = o.y_ + tabular.rowDescent(row); if (x < xbeg) @@ -4447,14 +4578,10 @@ int InsetTabular::cellXPos(idx_type const cell) const { - idx_type c = cell; - - for (; !tabular.isFirstCellInRow(c); --c) - ; + col_type col = tabular.cellColumn(cell); int lx = 0; - for (; c < cell; ++c) + for (col_type c = 0; c < col; ++c) lx += tabular.columnWidth(c); - return lx; } @@ -4491,21 +4618,29 @@ void InsetTabular::moveNextCell(Cursor & cur, EntryDirection entry_from) { + row_type const row = tabular.cellRow(cur.idx()); + col_type const col = tabular.cellColumn(cur.idx()); + if (isRightToLeft(cur)) { - if (tabular.isFirstCellInRow(cur.idx())) { - row_type const row = tabular.cellRow(cur.idx()); + if (tabular.cellColumn(cur.idx()) == 0) { if (row == tabular.row_info.size() - 1) return; cur.idx() = tabular.cellBelow(tabular.getLastCellInRow(row)); } else { if (cur.idx() == 0) return; - --cur.idx(); + if (col == 0) + cur.idx() = tabular.getLastCellInRow(row - 1); + else + cur.idx() = tabular.cellIndex(row, col - 1); } } else { if (tabular.isLastCell(cur.idx())) return; - ++cur.idx(); + if (tabular.isLastCellInRow(cur.idx())) + cur.idx() = tabular.cellIndex(row + 1, 0); + else + cur.idx() = tabular.cellIndex(row, col + 1); } cur.boundary(false); @@ -4516,7 +4651,6 @@ resetPos(cur); return; } - cur.pit() = 0; cur.pos() = 0; @@ -4542,9 +4676,11 @@ void InsetTabular::movePrevCell(Cursor & cur, EntryDirection entry_from) { + row_type const row = tabular.cellRow(cur.idx()); + col_type const col = tabular.cellColumn(cur.idx()); + if (isRightToLeft(cur)) { if (tabular.isLastCellInRow(cur.idx())) { - row_type const row = tabular.cellRow(cur.idx()); if (row == 0) return; cur.idx() = tabular.getFirstCellInRow(row); @@ -4552,12 +4688,18 @@ } else { if (tabular.isLastCell(cur.idx())) return; - ++cur.idx(); + if (tabular.isLastCellInRow(cur.idx())) + cur.idx() = tabular.cellIndex(row + 1, 0); + else + cur.idx() = tabular.cellIndex(row, col + 1); } } else { if (cur.idx() == 0) // first cell return; - --cur.idx(); + if (col == 0) + cur.idx() = tabular.getLastCellInRow(row - 1); + else + cur.idx() = tabular.cellIndex(row, col - 1); } if (cur.selIsMultiCell()) { @@ -4723,7 +4865,7 @@ break; case Tabular::SET_SPECIAL_COLUMN: - case Tabular::SET_SPECIAL_MULTI: + case Tabular::SET_SPECIAL_MULTICOLUMN: tabular.setAlignSpecial(cur.idx(), from_utf8(value), feature); break; @@ -4847,6 +4989,29 @@ cur.setSelection(false); break; } + + case Tabular::MULTIROW: { + if (!cur.selection()) { + // just multirow for one single cell + // check whether we are completely in a multirow + if (tabular.isMultiRow(cur.idx())) + tabular.unsetMultiRow(cur.idx()); + else + tabular.setMultiRow(cur.idx(), 1); + break; + } + // we have a selection so this means we just add all this + // cells to form a multirow cell + idx_type const s_start = cur.selBegin().idx(); + row_type const row_start = tabular.cellRow(s_start); + row_type const row_end = tabular.cellRow(cur.selEnd().idx()); + tabular.setMultiRow(s_start, row_end - row_start + 1); + cur.idx() = s_start; + cur.pit() = 0; + cur.pos() = 0; + cur.setSelection(false); + break; + } case Tabular::SET_ALL_LINES: setLines = true; Index: src/insets/InsetTabular.h =================================================================== --- src/insets/InsetTabular.h (revision 33411) +++ src/insets/InsetTabular.h (working copy) @@ -12,7 +12,6 @@ * Full author contact details are available in file CREDITS. */ - // This is Juergen's rewrite of the tabular (table) support. // Things to think of when designing the new tabular support: @@ -24,14 +23,6 @@ // - multirow // - column styles -// This is what I have written about tabular support in the LyX3-Tasks file: -// -// o rewrite of table code. Should probably be written as some -// kind of an inset. [Done] -// o enhance longtable support - -// Lgb - #ifndef INSET_TABULAR_H #define INSET_TABULAR_H @@ -120,6 +111,8 @@ /// MULTICOLUMN, /// + MULTIROW, + /// SET_ALL_LINES, /// UNSET_ALL_LINES, @@ -164,8 +157,10 @@ /// SET_SPECIAL_COLUMN, /// - SET_SPECIAL_MULTI, + SET_SPECIAL_MULTICOLUMN, /// + SET_SPECIAL_MULTIROW, + /// SET_BOOKTABS, /// UNSET_BOOKTABS, @@ -199,7 +194,11 @@ /// CELL_BEGIN_OF_MULTICOLUMN, /// - CELL_PART_OF_MULTICOLUMN + CELL_PART_OF_MULTICOLUMN, + /// + CELL_BEGIN_OF_MULTIROW, + /// + CELL_PART_OF_MULTIROW }; /// @@ -277,14 +276,16 @@ /// return space occupied by the second horizontal line and /// interline space above row \p row in pixels - int getAdditionalHeight(row_type row) const; + int interRowSpace(row_type row) const; /// - int getAdditionalWidth(idx_type cell) const; + int interColumnSpace(idx_type cell) const; /* returns the maximum over all rows */ /// int columnWidth(idx_type cell) const; /// + int rowHeight(idx_type cell) const; + /// int width() const; /// int height() const; @@ -340,8 +341,10 @@ /// int cellWidth(idx_type cell) const; /// - int getBeginningOfTextInCell(idx_type cell) const; + int textHOffset(idx_type cell) const; /// + int textVOffset(idx_type cell) const; + /// void appendRow(idx_type cell); /// void deleteRow(row_type row); @@ -354,8 +357,6 @@ /// void copyColumn(col_type); /// - bool isFirstCellInRow(idx_type cell) const; - /// idx_type getFirstCellInRow(row_type row) const; /// bool isLastCellInRow(idx_type cell) const; @@ -384,10 +385,18 @@ /// void setMultiColumn(idx_type cell, idx_type number); /// - idx_type unsetMultiColumn(idx_type cell); // returns number of new cells + void unsetMultiColumn(idx_type cell); /// bool isPartOfMultiColumn(row_type row, col_type column) const; /// + bool isPartOfMultiRow(row_type row, col_type column) const; + /// + bool isMultiRow(idx_type cell) const; + /// + void setMultiRow(idx_type cell, idx_type number); + /// + void unsetMultiRow(idx_type cell); + /// row_type cellRow(idx_type cell) const; /// col_type cellColumn(idx_type cell) const; @@ -482,6 +491,8 @@ /// int multicolumn; /// + int multirow; + /// LyXAlignment alignment; /// VAlignment valignment; @@ -614,6 +625,8 @@ /// idx_type columnSpan(idx_type cell) const; /// + idx_type rowSpan(idx_type cell) const; + /// BoxType useParbox(idx_type cell) const; /// // helper function for Latex returns number of newlines @@ -622,9 +635,9 @@ /// int TeXBottomHLine(odocstream &, row_type row, std::string const lang) const; /// - int TeXCellPreamble(odocstream &, idx_type cell, bool & ismulticol) const; + int TeXCellPreamble(odocstream &, idx_type cell, bool & ismulticol, bool & ismultirow) const; /// - int TeXCellPostamble(odocstream &, idx_type cell, bool ismulticol) const; + int TeXCellPostamble(odocstream &, idx_type cell, bool ismulticol, bool ismultirow) const; /// int TeXLongtableHeaderFooter(odocstream &, OutputParams const &) const; ///