X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Finsets%2FInsetTabular.cpp;h=7b4db191732032526bf4d72872f749744a16025b;hb=62af7ee772f16f154225d2d0b65d77f4376b6001;hp=d52ba528a37c94fdb56ef922c4caaefd23719b21;hpb=b37b39365fad227f2c12d325d01b9b39abf49342;p=lyx.git diff --git a/src/insets/InsetTabular.cpp b/src/insets/InsetTabular.cpp index d52ba528a3..7b4db19173 100644 --- a/src/insets/InsetTabular.cpp +++ b/src/insets/InsetTabular.cpp @@ -451,6 +451,49 @@ bool getTokenValue(string const & str, char const * token, Length & len) } +bool getTokenValue(string const & str, char const * token, Change & change, BufferParams bp) +{ + // set the change to be Change() as default as this it should be if not + // in the file format. + change = Change(); + string tmp; + if (getTokenValue(str, token, tmp)) { + vector const changedata = getVectorFromString(tmp, " "); + if (changedata.size() != 3) { + Alert::warning(_("Change tracking data incomplete"), + _("Change tracking information for tabular row/column " + "is incomplete. I will ignore this.")); + return false; + } + BufferParams::AuthorMap const & am = bp.author_map_; + int aid = convert(changedata[1]); + if (am.find(aid) == am.end()) { + // FIXME Use ErrorList + Alert::warning(_("Change tracking author index missing"), + bformat(_("A change tracking author information for index " + "%1$d is missing. This can happen after a wrong " + "merge by a version control system. In this case, " + "either fix the merge, or have this information " + "missing until the corresponding tracked changes " + "are merged or this user edits the file again.\n"), + aid)); + bp.addAuthor(Author(aid)); + } + istringstream is(changedata[2]); + time_t ct; + is >> ct; + if (changedata[0] == "inserted") { + change = Change(Change::INSERTED, am.find(aid)->second, ct); + return true; + } else if (changedata[0] == "deleted") { + change = Change(Change::DELETED, am.find(aid)->second, ct); + return true; + } + } + return false; +} + + bool getTokenValue(string const & str, char const * token, Length & len, bool & flag) { len = Length(); @@ -531,6 +574,21 @@ string const write_attribute(string const & name, Length const & value) return value.zero() ? string() : write_attribute(name, value.asString()); } +string const write_attribute(string const & name, Change const & change, BufferParams const bp) +{ + odocstringstream ods; + if (change.inserted()) + ods << from_ascii("inserted"); + else if (change.deleted()) + ods << from_ascii("deleted"); + if (change.changed()) { + ods << " " << bp.authors().get(change.author).bufferId() + << " " << change.changetime; + return write_attribute(name, ods.str()); + } + return string(); +} + } // namespace @@ -673,7 +731,8 @@ Tabular::RowData::RowData() endfoot(false), endlastfoot(false), newpage(false), - caption(false) + caption(false), + change(Change()) {} @@ -681,7 +740,8 @@ Tabular::ColumnData::ColumnData() : alignment(LYX_ALIGN_CENTER), valignment(LYX_VALIGN_TOP), width(0), - varwidth(false) + varwidth(false), + change(Change()) { } @@ -738,12 +798,14 @@ void Tabular::init(Buffer * buf, row_type rows_arg, } -void Tabular::deleteRow(row_type const row) +void Tabular::deleteRow(row_type const row, bool const force) { // Not allowed to delete last row if (nrows() == 1) return; + bool const ct = force ? false : buffer().params().track_changes; + for (col_type c = 0; c < ncols(); ++c) { // Care about multirow cells if (row + 1 < nrows() && @@ -752,8 +814,12 @@ void Tabular::deleteRow(row_type const row) cell_info[row + 1][c].multirow = CELL_BEGIN_OF_MULTIROW; } } - row_info.erase(row_info.begin() + row); - cell_info.erase(cell_info.begin() + row); + if (ct) + row_info[row].change.setDeleted(); + else { + row_info.erase(row_info.begin() + row); + cell_info.erase(cell_info.begin() + row); + } updateIndexes(); } @@ -779,8 +845,6 @@ void Tabular::insertRow(row_type const row, bool copy) 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_)); - if (buffer().params().track_changes) - cell_info[row + 1][c].inset->setChange(Change(Change::INSERTED)); if (cell_info[row][c].multirow == CELL_BEGIN_OF_MULTIROW) cell_info[row + 1][c].multirow = CELL_PART_OF_MULTIROW; } @@ -799,9 +863,10 @@ void Tabular::insertRow(row_type const row, bool copy) setBottomLine(i, true); setBottomLine(j, false); } - // mark track changes - if (buffer().params().track_changes) - cellInfo(i).inset->setChange(Change(Change::INSERTED)); + } + if (buffer().params().track_changes) { + row_info[row + 1].change.setInserted(); + updateIndexes(); } } @@ -852,12 +917,14 @@ void Tabular::moveRow(row_type row, RowDirection direction) } -void Tabular::deleteColumn(col_type const col) +void Tabular::deleteColumn(col_type const col, bool const force) { // Not allowed to delete last column if (ncols() == 1) return; + bool const ct = force ? false : buffer().params().track_changes; + for (row_type r = 0; r < nrows(); ++r) { // Care about multicolumn cells if (col + 1 < ncols() && @@ -865,9 +932,13 @@ void Tabular::deleteColumn(col_type const col) cell_info[r][col + 1].multicolumn == CELL_PART_OF_MULTICOLUMN) { cell_info[r][col + 1].multicolumn = CELL_BEGIN_OF_MULTICOLUMN; } - cell_info[r].erase(cell_info[r].begin() + col); + if (!ct) + cell_info[r].erase(cell_info[r].begin() + col); } - column_info.erase(column_info.begin() + col); + if (ct) + column_info[col].change.setDeleted(); + else + column_info.erase(column_info.begin() + col); updateIndexes(); } @@ -886,14 +957,12 @@ void Tabular::appendColumn(col_type col) void Tabular::insertColumn(col_type const col, bool copy) { - BufferParams const & bp = buffer().params(); + bool const ct = buffer().params().track_changes; column_info.insert(column_info.begin() + col + 1, ColumnData(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_)); - if (bp.track_changes) - cell_info[r][col + 1].inset->setChange(Change(Change::INSERTED)); if (cell_info[r][col].multicolumn == CELL_BEGIN_OF_MULTICOLUMN) cell_info[r][col + 1].multicolumn = CELL_PART_OF_MULTICOLUMN; } @@ -909,8 +978,10 @@ void Tabular::insertColumn(col_type const col, bool copy) if (rightLine(i) && rightLine(j)) { setRightLine(j, false); } - if (buffer().params().track_changes) - cellInfo(i).inset->setChange(Change(Change::INSERTED)); + } + if (ct) { + column_info[col + 1].change.setInserted(); + updateIndexes(); } } @@ -934,8 +1005,8 @@ void Tabular::updateIndexes() rowofcell.resize(numberofcells); columnofcell.resize(numberofcells); idx_type i = 0; - // reset column and row of cells and update their width and alignment - for (row_type row = 0; row < nrows(); ++row) + // reset column and row of cells and update their width, alignment and ct status + for (row_type row = 0; row < nrows(); ++row) { for (col_type column = 0; column < ncols(); ++column) { if (isPartOfMultiColumn(row, column)) { cell_info[row][column].inset->toggleMultiCol(true); @@ -956,8 +1027,15 @@ void Tabular::updateIndexes() cell_info[row][column].inset->toggleMultiRow(false); cell_info[row][column].inset->setContentAlignment( getAlignment(cellIndex(row, column))); + if (buffer().params().track_changes) { + if (row_info[row].change.changed()) + cell_info[row][column].inset->setChange(row_info[row].change); + if (column_info[column].change.changed()) + cell_info[row][column].inset->setChange(column_info[column].change); + } ++i; } + } } @@ -1569,7 +1647,7 @@ int Tabular::textVOffset(idx_type cell) const } -Tabular::idx_type Tabular::getFirstCellInRow(row_type row) const +Tabular::idx_type Tabular::getFirstCellInRow(row_type row, bool const ct) const { col_type c = 0; idx_type const numcells = numberOfCellsInRow(row); @@ -1578,26 +1656,52 @@ Tabular::idx_type Tabular::getFirstCellInRow(row_type row) const // is really invalid, i.e., it is NOT the first cell in the row. but // i do not know what to do here. (rgh) while (c < numcells - 1 - && cell_info[row][c].multirow == CELL_PART_OF_MULTIROW) + && (cell_info[row][c].multirow == CELL_PART_OF_MULTIROW + || (ct && column_info[c].change.deleted()))) ++c; return cell_info[row][c].cellno; } -Tabular::idx_type Tabular::getLastCellInRow(row_type row) const +Tabular::idx_type Tabular::getLastCellInRow(row_type row, bool const ct) const { col_type c = ncols() - 1; // of course we check against 0 so we don't crash. but we have the same // problem as in the previous routine: if all the cells are part of a // multirow or part of a multi column, then our return value is invalid. while (c > 0 - && (cell_info[row][c].multirow == CELL_PART_OF_MULTIROW - || cell_info[row][c].multicolumn == CELL_PART_OF_MULTICOLUMN)) + && ((cell_info[row][c].multirow == CELL_PART_OF_MULTIROW + || cell_info[row][c].multicolumn == CELL_PART_OF_MULTICOLUMN) + || (ct && column_info[c].change.deleted()))) --c; return cell_info[row][c].cellno; } +Tabular::row_type Tabular::getFirstRow(bool const ct) const +{ + row_type r = 0; + if (!ct) + return r; + // exclude deleted rows if ct == true + while (r < nrows() && row_info[r].change.deleted()) + ++r; + return r; +} + + +Tabular::row_type Tabular::getLastRow(bool const ct) const +{ + row_type r = nrows() - 1; + if (!ct) + return r; + // exclude deleted rows if ct == true + while (r > 0 && row_info[r].change.deleted()) + --r; + return r; +} + + Tabular::row_type Tabular::cellRow(idx_type cell) const { if (cell >= numberofcells) @@ -1652,8 +1756,9 @@ void Tabular::write(ostream & os) const os << " columns) const +void Tabular::TeXTopHLine(otexstream & os, row_type row, list columns, + list logical_columns) const { // we only output complete row lines and the 1st row here, the rest // is done in Tabular::TeXBottomHLine(...) @@ -2415,15 +2524,16 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list columns) } // do nothing if empty first row, or incomplete row line after - if ((row == 0 && nset == 0) || (row > 0 && nset != ncols())) + row_type first = getFirstRow(!buffer().params().output_changes); + if ((row == first && nset == 0) || (row > first && nset != columns.size())) return; // Is this the actual first row (excluding longtable caption row)? - bool const realfirstrow = (row == 0 - || (is_long_tabular && row == 1 && ltCaption(0))); + bool const realfirstrow = (row == first + || (is_long_tabular && row == first + 1 && ltCaption(first))); // only output complete row lines and the 1st row's clines - if (nset == ncols() && !have_trims) { + if (nset == columns.size() && !have_trims) { if (use_booktabs) { os << (realfirstrow ? "\\toprule " : "\\midrule "); } else { @@ -2432,7 +2542,12 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list columns) } else if (realfirstrow || have_trims) { string const cline = use_booktabs ? "\\cmidrule" : "\\cline"; col_type c = 0; - for (auto & cl : columns) { + std::list::const_iterator it1 = logical_columns.begin(); + std::list::const_iterator it2 = columns.begin(); + // We need to iterate over the logical columns here, but take care for + // bidi swapping + for (; it1 != logical_columns.end() && it2 != columns.end(); ++it1, ++it2) { + col_type cl = *it1; if (cl < c) continue; c = cl; @@ -2441,7 +2556,8 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list columns) for (col_type j = 0 ; j < c; ++j) if (column_info[j].alignment == LYX_ALIGN_DECIMAL) ++offset; - string const firstcol = convert(c + 1 + offset); + // If the two iterators differ, we are in bidi with swapped columns + col_type firstcol = (*it1 == *it2) ? c + 1 + offset : columns.size() - c + offset; while (isPartOfMultiColumn(row, c)) ++c; string trim; @@ -2466,7 +2582,7 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list columns) for (col_type j = cstart ; j < c ; ++j) if (column_info[j].alignment == LYX_ALIGN_DECIMAL) ++offset; - col_type const lastcol = c + 1 + offset; + col_type lastcol = (*it1 == *it2) ? c + 1 + offset : columns.size() - c + offset; if (toprtrims.find(c) != toprtrims.end() && toprtrims.find(c)->second) trim += "r"; @@ -2474,8 +2590,12 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list columns) os << cline; if (!trim.empty()) os << "(" << trim << ")"; - os << "{" << firstcol << '-' << lastcol << "}"; - if (c == ncols() - 1) + if (firstcol > lastcol) + // This can happen with bidi (swapped columns) + os << "{" << lastcol << '-' << firstcol << "}"; + else + os << "{" << firstcol << '-' << lastcol << "}"; + if (c == columns.size() - 1) break; ++c; } @@ -2485,13 +2605,14 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list columns) } -void Tabular::TeXBottomHLine(otexstream & os, row_type row, list columns) const +void Tabular::TeXBottomHLine(otexstream & os, row_type row, list columns, + list logical_columns) const { // we output bottomlines of row r and the toplines of row r+1 // if the latter do not span the whole tabular // get the bottomlines of row r, and toplines in next row - bool lastrow = row == nrows() - 1; + bool lastrow = row == getLastRow(!buffer().params().output_changes); map bottomline, topline, topltrims, toprtrims, bottomltrims, bottomrtrims; bool nextrowset = true; for (auto const & c : columns) { @@ -2534,7 +2655,7 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list colum bottomline[c] = bottomline.find(c)->second || topline.find(c)->second; bottomltrims[c] = (bottomltrims.find(c) != bottomltrims.end() && bottomltrims.find(c)->second) || (topltrims.find(c) != topltrims.end() && topltrims.find(c)->second); - bottomrtrims[c] =(bottomrtrims.find(c) != bottomrtrims.end() && bottomrtrims.find(c)->second) + bottomrtrims[c] = (bottomrtrims.find(c) != bottomrtrims.end() && bottomrtrims.find(c)->second) || (toprtrims.find(c) != toprtrims.end() && toprtrims.find(c)->second); if (bottomline.find(c)->second) ++nset; @@ -2544,10 +2665,10 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list colum } // do nothing if empty, OR incomplete row line with a topline in next row - if (nset == 0 || (nextrowset && nset != ncols())) + if (nset == 0 || (nextrowset && nset != columns.size())) return; - if (nset == ncols() && !have_trims) { + if (nset == columns.size() && !have_trims) { if (use_booktabs) os << (lastrow ? "\\bottomrule" : "\\midrule"); else @@ -2555,7 +2676,12 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list colum } else { string const cline = use_booktabs ? "\\cmidrule" : "\\cline"; col_type c = 0; - for (auto & cl : columns) { + std::list::const_iterator it1 = logical_columns.begin(); + std::list::const_iterator it2 = columns.begin(); + // We need to iterate over the logical columns here, but take care for + // bidi swapping + for (; it1 != logical_columns.end() && it2 != columns.end(); ++it1, ++it2) { + col_type cl = *it1; if (cl < c) continue; c = cl; @@ -2564,7 +2690,8 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list colum for (col_type j = 0 ; j < c; ++j) if (column_info[j].alignment == LYX_ALIGN_DECIMAL) ++offset; - string const firstcol = convert(c + 1 + offset); + // If the two iterators differ, we are in bidi with swapped columns + col_type firstcol = (*it1 == *it2) ? c + 1 + offset : columns.size() - c + offset; while (isPartOfMultiColumn(row, c)) ++c; string trim; @@ -2591,7 +2718,7 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list colum for (col_type j = cstart ; j < c ; ++j) if (column_info[j].alignment == LYX_ALIGN_DECIMAL) ++offset; - col_type const lastcol = c + 1 + offset; + col_type lastcol = (*it1 == *it2) ? c + 1 + offset : columns.size() - c + offset; if (bottomrtrims.find(c) != bottomrtrims.end() && bottomrtrims.find(c)->second) trim += "r"; @@ -2599,8 +2726,12 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list colum os << cline; if (!trim.empty()) os << "(" << trim << ")"; - os << "{" << firstcol << '-' << lastcol << "}"; - if (c == ncols() - 1) + if (firstcol > lastcol) + // This can happen with bidi (swapped columns) + os << "{" << lastcol << '-' << firstcol << "}"; + else + os << "{" << firstcol << '-' << lastcol << "}"; + if (c == columns.size() - 1) break; ++c; } @@ -2808,7 +2939,8 @@ void Tabular::TeXCellPostamble(otexstream & os, idx_type cell, void Tabular::TeXLongtableHeaderFooter(otexstream & os, OutputParams const & runparams, - list columns) const + list columns, + list logical_columns) const { if (!is_long_tabular) return; @@ -2820,7 +2952,7 @@ void Tabular::TeXLongtableHeaderFooter(otexstream & os, if (row_info[r].caption && !row_info[r].endfirsthead && !row_info[r].endhead && !row_info[r].endfoot && !row_info[r].endlastfoot) - TeXRow(os, r, runparams, columns); + TeXRow(os, r, runparams, columns, logical_columns); } } // output first header info @@ -2829,7 +2961,7 @@ void Tabular::TeXLongtableHeaderFooter(otexstream & os, os << "\\hline\n"; for (row_type r = 0; r < nrows(); ++r) { if (row_info[r].endfirsthead) - TeXRow(os, r, runparams, columns); + TeXRow(os, r, runparams, columns, logical_columns); } if (endfirsthead.bottomDL) os << "\\hline\n"; @@ -2843,7 +2975,7 @@ void Tabular::TeXLongtableHeaderFooter(otexstream & os, os << "\\hline\n"; for (row_type r = 0; r < nrows(); ++r) { if (row_info[r].endhead) - TeXRow(os, r, runparams, columns); + TeXRow(os, r, runparams, columns, logical_columns); } if (endhead.bottomDL) os << "\\hline\n"; @@ -2855,7 +2987,7 @@ void Tabular::TeXLongtableHeaderFooter(otexstream & os, os << "\\hline\n"; for (row_type r = 0; r < nrows(); ++r) { if (row_info[r].endfoot) - TeXRow(os, r, runparams, columns); + TeXRow(os, r, runparams, columns, logical_columns); } if (endfoot.bottomDL) os << "\\hline\n"; @@ -2869,7 +3001,7 @@ void Tabular::TeXLongtableHeaderFooter(otexstream & os, os << "\\hline\n"; for (row_type r = 0; r < nrows(); ++r) { if (row_info[r].endlastfoot) - TeXRow(os, r, runparams, columns); + TeXRow(os, r, runparams, columns, logical_columns); } if (endlastfoot.bottomDL) os << "\\hline\n"; @@ -2890,12 +3022,10 @@ bool Tabular::isValidRow(row_type row) const void Tabular::TeXRow(otexstream & os, row_type row, OutputParams const & runparams, - list columns) const + list columns, list logical_columns) const { - idx_type cell = cellIndex(row, 0); - //output the top line - TeXTopHLine(os, row, columns); + TeXTopHLine(os, row, columns, logical_columns); if (row_info[row].top_space_default) { if (use_booktabs) @@ -2921,14 +3051,15 @@ void Tabular::TeXRow(otexstream & os, row_type row, bool const bidi_rtl = runparams.local_font->isRightToLeft() && runparams.useBidiPackage(); + bool const ct = !buffer().params().output_changes; idx_type lastcell = - bidi_rtl ? getFirstCellInRow(row) : getLastCellInRow(row); + bidi_rtl ? getFirstCellInRow(row, ct) : getLastCellInRow(row, ct); for (auto const & c : columns) { if (isPartOfMultiColumn(row, c)) continue; - cell = cellIndex(row, c); + idx_type cell = cellIndex(row, c); if (isPartOfMultiRow(row, c) && column_info[c].alignment != LYX_ALIGN_DECIMAL) { @@ -3043,7 +3174,7 @@ void Tabular::TeXRow(otexstream & os, row_type row, os << '\n'; //output the bottom line - TeXBottomHLine(os, row, columns); + TeXBottomHLine(os, row, columns, logical_columns); if (row_info[row].interline_space_default) { if (use_booktabs) @@ -3092,11 +3223,17 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const runparams.local_font->isRightToLeft() && runparams.useBidiPackage(); list columns; + list logical_columns; for (col_type cl = 0; cl < ncols(); ++cl) { + if (!buffer().params().output_changes && column_info[cl].change.deleted()) + continue; if (bidi_rtl) columns.push_front(cl); else columns.push_back(cl); + // for some calculations, we need the logical (non-swapped) + // columns also in bidi. + logical_columns.push_back(cl); } // If we use \cline or \cmidrule, we need to locally de-activate @@ -3311,15 +3448,17 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const } os << "}\n"; - TeXLongtableHeaderFooter(os, runparams, columns); + TeXLongtableHeaderFooter(os, runparams, columns, logical_columns); //+--------------------------------------------------------------------- //+ the single row and columns (cells) + //+--------------------------------------------------------------------- for (row_type r = 0; r < nrows(); ++r) { + if (!buffer().params().output_changes && row_info[r].change.deleted()) + continue; if (isValidRow(r)) { - TeXRow(os, r, runparams, columns); + TeXRow(os, r, runparams, columns, logical_columns); if (is_long_tabular && row_info[r].newpage) os << "\\newpage\n"; } @@ -4039,10 +4178,11 @@ docstring InsetTableCell::xhtml(XHTMLStream & xs, OutputParams const & rp) const void InsetTableCell::metrics(MetricsInfo & mi, Dimension & dim) const { TextMetrics & tm = mi.base.bv->textMetrics(&text()); + int const horiz_offset = leftOffset(mi.base.bv) + rightOffset(mi.base.bv); // Hand font through to contained lyxtext: tm.font_.fontInfo() = mi.base.font; - mi.base.textwidth -= 2 * TEXT_TO_INSET_OFFSET; + mi.base.textwidth -= horiz_offset; // This can happen when a layout has a left and right margin, // and the view is made very narrow. We can't do better than @@ -4056,10 +4196,10 @@ void InsetTableCell::metrics(MetricsInfo & mi, Dimension & dim) const tm.metrics(mi, dim, mi.base.textwidth, false); else tm.metrics(mi, dim, 0, false); - mi.base.textwidth += 2 * TEXT_TO_INSET_OFFSET; - dim.asc += TEXT_TO_INSET_OFFSET; - dim.des += TEXT_TO_INSET_OFFSET; - dim.wid += 2 * TEXT_TO_INSET_OFFSET; + mi.base.textwidth += horiz_offset; + dim.asc += topOffset(mi.base.bv); + dim.des += bottomOffset(mi.base.bv); + dim.wid += horiz_offset; } @@ -4250,9 +4390,9 @@ void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const tabular.cell_info[r][c].decimal_width = decimal_width; // with LYX_VALIGN_BOTTOM the descent is relative to the last par - // = descent of text in last par + TEXT_TO_INSET_OFFSET: + // = descent of text in last par + bottomOffset: int const lastpardes = tm.last().second->descent() - + TEXT_TO_INSET_OFFSET; + + bottomOffset(mi.base.bv); int offset = 0; switch (tabular.getVAlignment(cell)) { case Tabular::LYX_VALIGN_TOP: @@ -4332,6 +4472,9 @@ void InsetTabular::draw(PainterInfo & pi, int x, int y) const bool const original_selection_state = pi.selected; idx_type idx = 0; + + // Save tabular change status + Change tab_change = pi.change; int yy = y + tabular.offsetVAlignment(); for (row_type r = 0; r < tabular.nrows(); ++r) { @@ -4348,6 +4491,15 @@ void InsetTabular::draw(PainterInfo & pi, int x, int y) const } pi.selected |= isCellSelected(cur, r, c); + + // Mark deleted rows/columns + if (tabular.column_info[c].change.changed()) + pi.change = tabular.column_info[c].change; + else if (tabular.row_info[r].change.changed()) + pi.change = tabular.row_info[r].change; + else + pi.change = tab_change; + int const cx = nx + tabular.textHOffset(idx); int const cy = yy + tabular.textVOffset(idx); // Cache the Inset position. @@ -4429,9 +4581,9 @@ void InsetTabular::drawSelection(PainterInfo & pi, int x, int y) const namespace { void tabline(PainterInfo const & pi, int x1, int y1, int x2, int y2, int lt, int rt, - bool drawline, bool heavy = false) + Color const incol, bool drawline, bool heavy = false) { - ColorCode const col = drawline ? Color_tabularline : Color_tabularonoffline; + Color const col = drawline ? incol : Color_tabularonoffline; if (drawline && lt > 0) pi.pain.line(x1, y1, x1 + lt, y2, pi.textColor(Color_tabularonoffline), Painter::line_onoffdash, @@ -4459,6 +4611,12 @@ void InsetTabular::drawCellLines(PainterInfo & pi, int x, int y, col_type const col = tabular.cellColumn(cell); + // Colour the frame if rows/columns are added or deleted + 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(); + // Top bool drawline = tabular.topLine(cell) || (row > 0 && tabular.bottomLine(tabular.cellAbove(cell))); @@ -4471,7 +4629,7 @@ void InsetTabular::drawCellLines(PainterInfo & pi, int x, int y, if (tabular.topLineTrim(cell).second || (row > 0 && tabular.bottomLineTrim(tabular.cellIndex(row - 1, col)).second)) rt = 10; - tabline(pi, x, y, x + w, y, lt, rt, drawline, heavy); + tabline(pi, x, y, x + w, y, lt, rt, colour, drawline, heavy); // Bottom lt = rt = 0; @@ -4489,12 +4647,12 @@ void InsetTabular::drawCellLines(PainterInfo & pi, int x, int y, lt = 10; if (tabular.bottomLineTrim(cell).second) rt = 10; - tabline(pi, x, y + h, x + w, y + h, lt, rt, drawline, heavy); + tabline(pi, x, y + h, x + w, y + h, lt, rt, colour, drawline, heavy); // Left drawline = tabular.leftLine(cell) || (col > 0 && tabular.rightLine(tabular.cellIndex(row, col - 1))); - tabline(pi, x, y, x, y + h, 0, 0, drawline); + tabline(pi, x, y, x, y + h, 0, 0, colour, drawline); // Right x -= tabular.interColumnSpace(cell); @@ -4505,7 +4663,7 @@ void InsetTabular::drawCellLines(PainterInfo & pi, int x, int y, drawline = tabular.rightLine(cell) || (next_cell_col < tabular.ncols() && tabular.leftLine(tabular.cellIndex(row, next_cell_col))); - tabline(pi, x + w, y, x + w, y + h, 0, 0, drawline); + tabline(pi, x + w, y, x + w, y + h, 0, 0, colour, drawline); } @@ -5062,14 +5220,55 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd) case LFUN_WORD_CAPITALIZE: case LFUN_WORD_UPCASE: case LFUN_WORD_LOWCASE: - case LFUN_CHARS_TRANSPOSE: + case LFUN_CHARS_TRANSPOSE: { + bool const ct = (act == LFUN_CHANGE_ACCEPT || act == LFUN_CHANGE_REJECT); if (cur.selIsMultiCell()) { row_type rs, re; col_type cs, ce; getSelection(cur, rs, re, cs, ce); Cursor tmpcur = cur; for (row_type r = rs; r <= re; ++r) { + if (ct && cs == 0 && ce == tabular.ncols() - 1) { + // whole row selected + if (act == LFUN_CHANGE_ACCEPT) { + if (tabular.row_info[r].change.inserted()) + tabular.row_info[r].change.setUnchanged(); + else if (tabular.row_info[r].change.deleted()) { + tabular.deleteRow(r, true); + --re; + continue; + } + } else { + if (tabular.row_info[r].change.deleted()) + tabular.row_info[r].change.setUnchanged(); + else if (tabular.row_info[r].change.inserted()) { + tabular.deleteRow(r, true); + --re; + continue; + } + } + } for (col_type c = cs; c <= ce; ++c) { + if (ct && rs == 0 && re == tabular.nrows() - 1) { + // whole col selected + if (act == LFUN_CHANGE_ACCEPT) { + if (tabular.column_info[c].change.inserted()) + tabular.column_info[c].change.setUnchanged(); + else if (tabular.column_info[c].change.deleted()) { + tabular.deleteColumn(c, true); + --ce; + continue; + } + } else { + if (tabular.column_info[c].change.deleted()) + tabular.column_info[c].change.setUnchanged(); + else if (tabular.column_info[c].change.inserted()) { + tabular.deleteColumn(c, true); + --ce; + continue; + } + } + } // cursor follows cell: tmpcur.idx() = tabular.cellIndex(r, c); // select this cell only: @@ -5083,15 +5282,53 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd) cell(tmpcur.idx())->dispatch(tmpcur, cmd); } } - if (act == LFUN_CHANGE_ACCEPT || act == LFUN_CHANGE_REJECT) { + if (ct) { + tabular.updateIndexes(); // cursor might be invalid cur.fixIfBroken(); + // change bar might need to be redrawn + cur.screenUpdateFlags(Update::Force); + cur.forceBufferUpdate(); } break; } else { cell(cur.idx())->dispatch(cur, cmd); break; } + } + + case LFUN_CHANGE_NEXT: + case LFUN_CHANGE_PREVIOUS: { + // BufferView::dispatch has already moved the cursor, we just + // need to select here if we have a changed row or column + if (tabular.row_info[tabular.cellRow(cur.idx())].change.changed()) { + // select row + cur.idx() = tabular.getFirstCellInRow(tabular.cellRow(cur.idx())); + cur.pit() = 0; + cur.pos() = 0; + cur.resetAnchor(); + cur.idx() = tabular.getLastCellInRow(tabular.cellRow(cur.idx())); + cur.pit() = cur.lastpit(); + cur.pos() = cur.lastpos(); + cur.selection(true); + bvcur = cur; + rowselect_ = true; + } + else if (tabular.column_info[tabular.cellColumn(cur.idx())].change.changed()) { + // select column + cur.idx() = tabular.cellIndex(0, tabular.cellColumn(cur.idx())); + cur.pit() = 0; + cur.pos() = 0; + cur.resetAnchor(); + cur.idx() = tabular.cellIndex(tabular.nrows() - 1, tabular.cellColumn(cur.idx())); + cur.pit() = cur.lastpit(); + cur.pos() = cur.lastpos(); + cur.selection(true); + bvcur = cur; + colselect_ = true; + } + break; + } case LFUN_INSET_SETTINGS: // relay this lfun to Inset, not to the cell. @@ -5648,6 +5885,37 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd, return cell(cur.idx())->getStatus(cur, cmd, status); } + case LFUN_CHANGE_ACCEPT: + case LFUN_CHANGE_REJECT: { + if (cur.selIsMultiCell()) { + row_type rs, re; + col_type cs, ce; + getSelection(cur, rs, re, cs, ce); + for (row_type r = rs; r <= re; ++r) { + if (tabular.row_info[r].change.changed()) { + status.setEnabled(true); + return true; + } + for (col_type c = cs; c <= ce; ++c) { + if (tabular.column_info[c].change.changed()) { + status.setEnabled(true); + return true; + } + } + } + } else { + if (tabular.row_info[tabular.cellRow(cur.idx())].change.changed()) { + status.setEnabled(true); + return true; + } + else if (tabular.column_info[tabular.cellColumn(cur.idx())].change.changed()) { + status.setEnabled(true); + return true; + } + } + return cell(cur.idx())->getStatus(cur, cmd, status); + } + // disable in non-fixed-width cells case LFUN_PARAGRAPH_BREAK: // multirow does not allow paragraph breaks @@ -6959,6 +7227,20 @@ Text * InsetTabular::getText(int idx) const } +bool InsetTabular::isChanged() const +{ + for (idx_type idx = 0; idx < nargs(); ++idx) { + if (cell(idx)->isChanged()) + return true; + if (tabular.row_info[tabular.cellRow(idx)].change.changed()) + return true; + if (tabular.column_info[tabular.cellColumn(idx)].change.changed()) + return true; + } + return false; +} + + void InsetTabular::setChange(Change const & change) { for (idx_type idx = 0; idx < nargs(); ++idx) @@ -6970,6 +7252,19 @@ void InsetTabular::acceptChanges() { for (idx_type idx = 0; idx < nargs(); ++idx) cell(idx)->acceptChanges(); + for (row_type row = 0; row < tabular.nrows(); ++row) { + if (tabular.row_info[row].change.inserted()) + tabular.row_info[row].change.setUnchanged(); + else if (tabular.row_info[row].change.deleted()) + tabular.deleteRow(row, true); + } + for (col_type col = 0; col < tabular.ncols(); ++col) { + if (tabular.column_info[col].change.inserted()) + tabular.column_info[col].change.setUnchanged(); + else if (tabular.column_info[col].change.deleted()) + tabular.deleteColumn(col, true); + } + tabular.updateIndexes(); } @@ -6977,6 +7272,19 @@ void InsetTabular::rejectChanges() { for (idx_type idx = 0; idx < nargs(); ++idx) cell(idx)->rejectChanges(); + for (row_type row = 0; row < tabular.nrows(); ++row) { + if (tabular.row_info[row].change.deleted()) + tabular.row_info[row].change.setUnchanged(); + else if (tabular.row_info[row].change.inserted()) + tabular.deleteRow(row, true); + } + for (col_type col = 0; col < tabular.ncols(); ++col) { + if (tabular.column_info[col].change.deleted()) + tabular.column_info[col].change.setUnchanged(); + else if (tabular.column_info[col].change.inserted()) + tabular.deleteColumn(col, true); + } + tabular.updateIndexes(); }