From db98cb233e57c4502bdaf2519801f38e20f050cf Mon Sep 17 00:00:00 2001 From: Juergen Spitzmueller Date: Sat, 11 Jan 2020 16:17:04 +0100 Subject: [PATCH] Implement change tracking of column/row addition/deletion Fixes #8469 File format change --- development/FORMAT | 4 + lib/lyx2lyx/lyx_2_4.py | 24 ++++- src/BufferView.cpp | 6 ++ src/insets/InsetTabular.cpp | 205 +++++++++++++++++++++++++++++++++--- src/insets/InsetTabular.h | 8 +- src/version.h | 4 +- 6 files changed, 231 insertions(+), 20 deletions(-) diff --git a/development/FORMAT b/development/FORMAT index 2a171c034e..ee34ef4284 100644 --- a/development/FORMAT +++ b/development/FORMAT @@ -7,6 +7,10 @@ changes happened in particular if possible. A good example would be ----------------------- +2020-01-11 Jürgen Spitzmüller + * Format incremented to 592: Add tabular column/row tag changed=[added|deleted] + which tracks whether a whole row/column has been inserted/deleted in ct. + 2020-01-10 Jürgen Spitzmüller * Format incremented to 591: Add buffer param \postpone_fragile_content (option to toggle the movement of labels and stuff of moving arguments). diff --git a/lib/lyx2lyx/lyx_2_4.py b/lib/lyx2lyx/lyx_2_4.py index daae36bf06..75e7588416 100644 --- a/lib/lyx2lyx/lyx_2_4.py +++ b/lib/lyx2lyx/lyx_2_4.py @@ -3689,6 +3689,24 @@ def revert_postpone_fragile(document): del document.header[i] +def revert_colrow_tracking(document): + " Remove change tag from tabular columns/rows " + i = 0 + while True: + i = find_token(document.body, "\\begin_inset Tabular", i+1) + if i == -1: + return + j = find_end_of_inset(document.body, i+1) + if j == -1: + document.warning("Malformed LyX document: Could not find end of tabular.") + continue + for k in range(i, j): + m = re.search('^$', document.body[k]) + if m: + document.body[k] = document.body[k].replace(' change="' + m.group(1) + '"', '') + m = re.search('^$', document.body[k]) + if m: + document.body[k] = document.body[k].replace(' change="' + m.group(1) + '"', '') ## # Conversion hub @@ -3742,10 +3760,12 @@ convert = [ [588, []], [589, [convert_totalheight]], [590, [convert_changebars]], - [591, [convert_postpone_fragile]] + [591, [convert_postpone_fragile]], + [592, []] ] -revert = [[590, [revert_postpone_fragile]], +revert = [[591, [revert_colrow_tracking]], + [590, [revert_postpone_fragile]], [589, [revert_changebars]], [588, [revert_totalheight]], [587, [revert_memoir_endnotes,revert_enotez,revert_theendnotes]], diff --git a/src/BufferView.cpp b/src/BufferView.cpp index eda08ed6f9..af0ad368b3 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -1539,12 +1539,18 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) case LFUN_CHANGE_NEXT: findNextChange(this); + if (cur.inset().isTable()) + // In tables, there might be whole changed rows or columns + cur.dispatch(cmd); // FIXME: Move this LFUN to Buffer so that we don't have to do this: dr.screenUpdate(Update::Force | Update::FitCursor); break; case LFUN_CHANGE_PREVIOUS: findPreviousChange(this); + if (cur.inset().isTable()) + // In tables, there might be whole changed rows or columns + cur.dispatch(cmd); // FIXME: Move this LFUN to Buffer so that we don't have to do this: dr.screenUpdate(Update::Force | Update::FitCursor); break; diff --git a/src/insets/InsetTabular.cpp b/src/insets/InsetTabular.cpp index d225854289..1bd757b024 100644 --- a/src/insets/InsetTabular.cpp +++ b/src/insets/InsetTabular.cpp @@ -451,6 +451,25 @@ bool getTokenValue(string const & str, char const * token, Length & len) } +bool getTokenValue(string const & str, char const * token, Change::Type & change) +{ + // set the length to be zero() as default as this it should be if not + // in the file format. + change = Change::UNCHANGED; + string tmp; + if (getTokenValue(str, token, tmp)) { + if (tmp == "inserted") { + change = Change::INSERTED; + return true; + } else if (tmp == "deleted") { + change = Change::DELETED; + return true; + } + } + return false; +} + + bool getTokenValue(string const & str, char const * token, Length & len, bool & flag) { len = Length(); @@ -531,6 +550,16 @@ string const write_attribute(string const & name, Length const & value) return value.zero() ? string() : write_attribute(name, value.asString()); } +template <> +string const write_attribute(string const & name, Change::Type const & type) +{ + if (type == Change::INSERTED) + return write_attribute(name, from_ascii("inserted")); + else if (type == Change::DELETED) + return write_attribute(name, from_ascii("deleted")); + return string(); +} + } // namespace @@ -673,7 +702,8 @@ Tabular::RowData::RowData() endfoot(false), endlastfoot(false), newpage(false), - caption(false) + caption(false), + change(Change::UNCHANGED) {} @@ -681,7 +711,8 @@ Tabular::ColumnData::ColumnData() : alignment(LYX_ALIGN_CENTER), valignment(LYX_VALIGN_TOP), width(0), - varwidth(false) + varwidth(false), + change(Change::UNCHANGED) { } @@ -738,15 +769,17 @@ 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) { // mark track changes - if (buffer().params().track_changes) + if (ct) cell_info[row][c].inset->setChange(Change(Change::DELETED)); // Care about multirow cells if (row + 1 < nrows() && @@ -755,7 +788,9 @@ void Tabular::deleteRow(row_type const row) cell_info[row + 1][c].multirow = CELL_BEGIN_OF_MULTIROW; } } - if (!buffer().params().track_changes) { + if (ct) + row_info[row].change = Change::DELETED; + else { row_info.erase(row_info.begin() + row); cell_info.erase(cell_info.begin() + row); } @@ -808,6 +843,8 @@ void Tabular::insertRow(row_type const row, bool copy) if (buffer().params().track_changes) cellInfo(i).inset->setChange(Change(Change::INSERTED)); } + if (buffer().params().track_changes) + row_info[row + 1].change = Change::INSERTED; } @@ -857,15 +894,17 @@ 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) { // mark track changes - if (buffer().params().track_changes) + if (ct) cell_info[r][col].inset->setChange(Change(Change::DELETED)); // Care about multicolumn cells if (col + 1 < ncols() && @@ -873,10 +912,12 @@ 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; } - if (!buffer().params().track_changes) + if (!ct) cell_info[r].erase(cell_info[r].begin() + col); } - if (!buffer().params().track_changes) + if (ct) + column_info[col].change = Change::DELETED; + else column_info.erase(column_info.begin() + col); updateIndexes(); } @@ -922,6 +963,8 @@ void Tabular::insertColumn(col_type const col, bool copy) if (buffer().params().track_changes) cellInfo(i).inset->setChange(Change(Change::INSERTED)); } + if (buffer().params().track_changes) + column_info[col + 1].change = Change::INSERTED; } @@ -1662,8 +1705,9 @@ void Tabular::write(ostream & os) const os << "dispatch(tmpcur, cmd); } } - if (act == LFUN_CHANGE_ACCEPT || act == LFUN_CHANGE_REJECT) { + if (ct) { // cursor might be invalid cur.fixIfBroken(); } @@ -5102,6 +5190,40 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd) 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 != Change::UNCHANGED) { + // 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 != Change::UNCHANGED) { + // 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. @@ -5658,6 +5780,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 != Change::UNCHANGED) { + status.setEnabled(true); + return true; + } + for (col_type c = cs; c <= ce; ++c) { + if (tabular.column_info[c].change != Change::UNCHANGED) { + status.setEnabled(true); + return true; + } + } + } + } else { + if (tabular.row_info[tabular.cellRow(cur.idx())].change != Change::UNCHANGED) { + status.setEnabled(true); + return true; + } + else if (tabular.column_info[tabular.cellColumn(cur.idx())].change != Change::UNCHANGED) { + 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 @@ -6980,6 +7133,18 @@ 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 == Change::INSERTED) + tabular.row_info[row].change = Change::UNCHANGED; + else if (tabular.row_info[row].change == Change::DELETED) + tabular.deleteRow(row, true); + } + for (col_type col = 0; col < tabular.ncols(); ++col) { + if (tabular.column_info[col].change == Change::INSERTED) + tabular.column_info[col].change = Change::UNCHANGED; + else if (tabular.column_info[col].change == Change::DELETED) + tabular.deleteColumn(col, true); + } } @@ -6987,6 +7152,18 @@ 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 == Change::DELETED) + tabular.row_info[row].change = Change::UNCHANGED; + else if (tabular.row_info[row].change == Change::INSERTED) + tabular.deleteRow(row, true); + } + for (col_type col = 0; col < tabular.ncols(); ++col) { + if (tabular.column_info[col].change == Change::DELETED) + tabular.column_info[col].change = Change::UNCHANGED; + else if (tabular.column_info[col].change == Change::INSERTED) + tabular.deleteColumn(col, true); + } } diff --git a/src/insets/InsetTabular.h b/src/insets/InsetTabular.h index 65cc2ae2e1..edd7242d20 100644 --- a/src/insets/InsetTabular.h +++ b/src/insets/InsetTabular.h @@ -537,7 +537,7 @@ public: /// void appendRow(row_type row); /// - void deleteRow(row_type row); + void deleteRow(row_type row, bool const force = false); /// void copyRow(row_type row); /// @@ -549,7 +549,7 @@ public: /// void appendColumn(col_type column); /// - void deleteColumn(col_type column); + void deleteColumn(col_type column, bool const force = false); /// void copyColumn(col_type column); /// @@ -785,6 +785,8 @@ public: bool newpage; /// caption bool caption; + /// + Change::Type change; }; /// typedef std::vector row_vector; @@ -808,6 +810,8 @@ public: docstring decimal_point; /// bool varwidth; + /// + Change::Type change; }; /// typedef std::vector column_vector; diff --git a/src/version.h b/src/version.h index 8d5c2129d5..df249ab526 100644 --- a/src/version.h +++ b/src/version.h @@ -32,8 +32,8 @@ extern char const * const lyx_version_info; // Do not remove the comment below, so we get merge conflict in // independent branches. Instead add your own. -#define LYX_FORMAT_LYX 591 // spitz: postpone_fragile_content -#define LYX_FORMAT_TEX2LYX 591 +#define LYX_FORMAT_LYX 592 // spitz: row/column change tracking +#define LYX_FORMAT_TEX2LYX 592 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX #ifndef _MSC_VER -- 2.39.5