commit bbc6ea4a5fb42516f91108d18a40520a2aba232a
Author: Juergen Spitzmueller <[email protected]>
Date: Sat Jan 11 16:17:04 2020 +0100
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 2a171c0..ee34ef4 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 <[email protected]>
+ * 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 <[email protected]>
* 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 daae36b..75e7588 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('^<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]],
diff --git a/src/BufferView.cpp b/src/BufferView.cpp
index eda08ed..af0ad36 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 d225854..1bd757b 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 << "<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);
+ }
}
diff --git a/src/insets/InsetTabular.h b/src/insets/InsetTabular.h
index 65cc2ae..edd7242 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<RowData> row_vector;
@@ -808,6 +810,8 @@ public:
docstring decimal_point;
///
bool varwidth;
+ ///
+ Change::Type change;
};
///
typedef std::vector<ColumnData> column_vector;
diff --git a/src/version.h b/src/version.h
index 8d5c212..df249ab 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
--
lyx-cvs mailing list
[email protected]
http://lists.lyx.org/mailman/listinfo/lyx-cvs