]> git.lyx.org Git - features.git/commitdiff
Implement change tracking of column/row addition/deletion
authorJuergen Spitzmueller <spitz@lyx.org>
Sat, 11 Jan 2020 15:17:04 +0000 (16:17 +0100)
committerJean-Marc Lasgouttes <lasgouttes@lyx.org>
Thu, 18 Jun 2020 13:48:50 +0000 (15:48 +0200)
Fixes #8469

File format change

development/FORMAT
lib/lyx2lyx/lyx_2_4.py
src/BufferView.cpp
src/insets/InsetTabular.cpp
src/insets/InsetTabular.h
src/version.h

index 2a171c034ec916129e99f7db4036a81087f21d77..ee34ef4284ea411b550a5888f206d4ecc3913e9e 100644 (file)
@@ -7,6 +7,10 @@ changes happened in particular if possible. A good example would be
 
 -----------------------
 
+2020-01-11 Jürgen Spitzmüller <spitz@lyx.org>
+       * 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 <spitz@lyx.org>
        * Format incremented to 591: Add buffer param \postpone_fragile_content
          (option to toggle the movement of labels and stuff of moving arguments).
index daae36bf0605f0773e1ae0a0e8fc34622e4b9b75..75e758841661f7546eb05033943bafd4e6aac5d5 100644 (file)
@@ -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('^<column.*change="([^"]+)".*>$', document.body[k])
+            if m:
+                document.body[k] = document.body[k].replace(' change="' + m.group(1) + '"', '')
+            m = re.search('^<row.*change="([^"]+)".*>$', 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]],
index eda08ed6f97aa8b8b1feca0d529a8fe1b4af8a78..af0ad368b32d29188f56f3c5a03138b82d5b7e62 100644 (file)
@@ -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;
index d225854289502187e32ab437bfa73fd8e57f557f..1bd757b024a14e76f5c942587152ec47e4289842 100644 (file)
@@ -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 << "<column"
                   << write_attribute("alignment", column_info[c].alignment);
                if (column_info[c].alignment == LYX_ALIGN_DECIMAL)
-                  os << write_attribute("decimal_point", column_info[c].decimal_point);
-               os << write_attribute("valignment", column_info[c].valignment)
+                       os << write_attribute("decimal_point", column_info[c].decimal_point);
+               os << write_attribute("change", column_info[c].change)
+                  << write_attribute("valignment", column_info[c].valignment)
                   << write_attribute("width", column_info[c].p_width.asString())
                   << write_attribute("varwidth", column_info[c].varwidth)
                   << write_attribute("special", column_info[c].align_special)
@@ -1684,7 +1728,8 @@ void Tabular::write(ostream & os) const
                        os << write_attribute("interlinespace", def);
                else
                        os << write_attribute("interlinespace", row_info[r].interline_space);
-               os << write_attribute("endhead", row_info[r].endhead)
+               os << write_attribute("change", row_info[r].change)
+                  << write_attribute("endhead", row_info[r].endhead)
                   << write_attribute("endfirsthead", row_info[r].endfirsthead)
                   << write_attribute("endfoot", row_info[r].endfoot)
                   << write_attribute("endlastfoot", row_info[r].endlastfoot)
@@ -1783,6 +1828,7 @@ void Tabular::read(Lexer & lex)
                getTokenValue(line, "width", column_info[c].p_width);
                getTokenValue(line, "special", column_info[c].align_special);
                getTokenValue(line, "varwidth", column_info[c].varwidth);
+               getTokenValue(line, "change", column_info[c].change);
        }
 
        for (row_type i = 0; i < nrows(); ++i) {
@@ -1804,6 +1850,7 @@ void Tabular::read(Lexer & lex)
                getTokenValue(line, "endlastfoot", row_info[i].endlastfoot);
                getTokenValue(line, "newpage", row_info[i].newpage);
                getTokenValue(line, "caption", row_info[i].caption);
+               getTokenValue(line, "change", row_info[i].change);
                for (col_type j = 0; j < ncols(); ++j) {
                        l_getline(is, line);
                        if (!prefixIs(line, "<cell")) {
@@ -5072,14 +5119,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 == Change::INSERTED)
+                                                       tabular.row_info[r].change = Change::UNCHANGED;
+                                               else if (tabular.row_info[r].change == Change::DELETED) {
+                                                       tabular.deleteRow(r, true);
+                                                       --re;
+                                                       continue;
+                                               }
+                                       } else {
+                                               if (tabular.row_info[r].change == Change::DELETED)
+                                                       tabular.row_info[r].change = Change::UNCHANGED;
+                                               else if (tabular.row_info[r].change == 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 == Change::INSERTED)
+                                                               tabular.column_info[c].change = Change::UNCHANGED;
+                                                       else if (tabular.column_info[c].change == Change::DELETED) {
+                                                               tabular.deleteColumn(c, true);
+                                                               --ce;
+                                                               continue;
+                                                       }
+                                               } else {
+                                                       if (tabular.column_info[c].change == Change::DELETED)
+                                                               tabular.column_info[c].change = Change::UNCHANGED;
+                                                       else if (tabular.column_info[c].change == Change::INSERTED) {
+                                                               tabular.deleteColumn(c, true);
+                                                               --ce;
+                                                               continue;
+                                                       }
+                                               }
+                                       }
                                        // cursor follows cell:
                                        tmpcur.idx() = tabular.cellIndex(r, c);
                                        // select this cell only:
@@ -5093,7 +5181,7 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd)
                                        cell(tmpcur.idx())->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);
+       }
 }
 
 
index 65cc2ae2e1d118999afa4ae5d07cd0af27b0dbdf..edd7242d2003904b93fae15426f2621b66eea6bd 100644 (file)
@@ -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<RowData> row_vector;
@@ -808,6 +810,8 @@ public:
                docstring decimal_point;
                ///
                bool varwidth;
+               ///
+               Change::Type change;
        };
        ///
        typedef std::vector<ColumnData> column_vector;
index 8d5c2129d52afea36832e69acfe23088f5313701..df249ab5262f924083846a5f9baa6ce3db24ce32 100644 (file)
@@ -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