]> git.lyx.org Git - features.git/blobdiff - src/insets/InsetTabular.cpp
Make InsetIterator compatible with range-based loops
[features.git] / src / insets / InsetTabular.cpp
index f305dbc5e84cfd5cc8ed433678020ef1cfddc046..21947f46a5a7e9c45d208581b2fe1118632af804 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "InsetTabular.h"
 
+#include "Author.h"
 #include "buffer_funcs.h"
 #include "Buffer.h"
 #include "BufferParams.h"
@@ -42,6 +43,7 @@
 #include "LyXRC.h"
 #include "MetricsInfo.h"
 #include "OutputParams.h"
+#include "xml.h"
 #include "output_xhtml.h"
 #include "Paragraph.h"
 #include "ParagraphParameters.h"
@@ -451,7 +453,7 @@ bool getTokenValue(string const & str, char const * token, Length & len)
 }
 
 
-bool getTokenValue(string const & str, char const * token, Change & change, BufferParams bp)
+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.
@@ -462,7 +464,7 @@ bool getTokenValue(string const & str, char const * token, Change & change, Buff
                if (changedata.size() != 3) {
                        Alert::warning(_("Change tracking data incomplete"),
                                       _("Change tracking information for tabular row/column "
-                                        "is incomplete. I will ignore this.\n"));
+                                        "is incomplete. I will ignore this."));
                        return false;
                }
                BufferParams::AuthorMap const & am = bp.author_map_;
@@ -559,7 +561,7 @@ string const write_attribute(string const & name, int const & i)
 
 
 template <>
-string const write_attribute(string const & name, Tabular::idx_type const & i)
+string const write_attribute(string const & name, idx_type const & i)
 {
        // we write only true attribute values so we remove a bit of the
        // file format bloat for tabulars.
@@ -570,11 +572,11 @@ string const write_attribute(string const & name, Tabular::idx_type const & i)
 template <>
 string const write_attribute(string const & name, Length const & value)
 {
-       // we write only the value if we really have one same reson as above.
+       // we write only the value if we really have one same reason as above.
        return value.zero() ? string() : write_attribute(name, value.asString());
 }
 
-string const write_attribute(string const & name, Change const & change, BufferParams const bp)
+string const write_attribute(string const & name, Change const & change, BufferParams const bp)
 {
        odocstringstream ods;
        if (change.inserted())
@@ -618,9 +620,9 @@ InsetTableCell splitCell(InsetTableCell & head, docstring const & align_d, bool
 {
        InsetTableCell tail = InsetTableCell(head);
        DocIterator const dit = separatorPos(&head, align_d);
-       hassep = (bool)dit;
+       hassep = static_cast<bool>(dit);
        if (hassep) {
-               pit_type const psize = head.paragraphs().front().size();
+               pos_type const psize = head.paragraphs().front().size();
                head.paragraphs().front().eraseChars(dit.pos(), psize, false);
                tail.paragraphs().front().eraseChars(0,
                        dit.pos() < psize ? dit.pos() + 1 : psize, false);
@@ -807,9 +809,6 @@ void Tabular::deleteRow(row_type const row, bool const force)
        bool const ct = force ? false : buffer().params().track_changes;
 
        for (col_type c = 0; c < ncols(); ++c) {
-               // mark track changes
-               if (ct)
-                       cell_info[row][c].inset->setChange(Change(Change::DELETED));
                // Care about multirow cells
                if (row + 1 < nrows() &&
                    cell_info[row][c].multirow == CELL_BEGIN_OF_MULTIROW &&
@@ -848,8 +847,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;
        }
@@ -868,12 +865,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();
        }
 }
 
@@ -933,9 +928,6 @@ void Tabular::deleteColumn(col_type const col, bool const force)
        bool const ct = force ? false : buffer().params().track_changes;
 
        for (row_type r = 0; r < nrows(); ++r) {
-               // mark track changes
-               if (ct)
-                       cell_info[r][col].inset->setChange(Change(Change::DELETED));
                // Care about multicolumn cells
                if (col + 1 < ncols() &&
                    cell_info[r][col].multicolumn == CELL_BEGIN_OF_MULTICOLUMN &&
@@ -967,14 +959,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;
        }
@@ -990,11 +980,11 @@ 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 (buffer().params().track_changes)
+       if (ct) {
                column_info[col + 1].change.setInserted();
+               updateIndexes();
+       }
 }
 
 
@@ -1017,15 +1007,15 @@ 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);
                                continue;
                        }
                        cell_info[row][column].inset->toggleMultiCol(false);
-                       // columnofcell needs to be called before setting width and aligment
+                       // columnofcell needs to be called before setting width and alignment
                        // multirow cells inherit the width from the column width
                        if (!isPartOfMultiRow(row, column)) {
                                columnofcell[i] = column;
@@ -1039,12 +1029,19 @@ 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;
                }
+       }
 }
 
 
-Tabular::idx_type Tabular::numberOfCellsInRow(row_type const row) const
+idx_type Tabular::numberOfCellsInRow(row_type const row) const
 {
        idx_type result = 0;
        for (col_type c = 0; c < ncols(); ++c)
@@ -1179,8 +1176,8 @@ bool Tabular::updateColumnWidths(MetricsInfo & mi)
        int restwidth = -1;
        if (!tab_width.zero()) {
                restwidth = mi.base.inPixels(tab_width);
-               // Substract the fixed widths from the table width
-               for (auto const w : max_pwidth)
+               // Subtract the fixed widths from the table width
+               for (auto const w : max_pwidth)
                        restwidth -= w.second;
        }
 
@@ -1194,7 +1191,7 @@ bool Tabular::updateColumnWidths(MetricsInfo & mi)
        // Now consider that some variable width columns exceed the vcolwidth
        if (vcolwidth > 0) {
                bool changed = false;
-               for (auto const w : max_width) {
+               for (auto const w : max_width) {
                        if (tabularx || w.second > vcolwidth) {
                                --restcols;
                                restwidth -= w.second;
@@ -1652,7 +1649,7 @@ int Tabular::textVOffset(idx_type cell) const
 }
 
 
-Tabular::idx_type Tabular::getFirstCellInRow(row_type row) const
+idx_type Tabular::getFirstCellInRow(row_type row, bool const ct) const
 {
        col_type c = 0;
        idx_type const numcells = numberOfCellsInRow(row);
@@ -1661,27 +1658,53 @@ 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
+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::cellRow(idx_type cell) const
+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;
+}
+
+
+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;
+}
+
+
+row_type Tabular::cellRow(idx_type cell) const
 {
        if (cell >= numberofcells)
                return nrows() - 1;
@@ -1691,7 +1714,7 @@ Tabular::row_type Tabular::cellRow(idx_type cell) const
 }
 
 
-Tabular::col_type Tabular::cellColumn(idx_type cell) const
+col_type Tabular::cellColumn(idx_type cell) const
 {
        if (cell >= numberofcells)
                return ncols() - 1;
@@ -1982,7 +2005,7 @@ Tabular::CellData & Tabular::cellInfo(idx_type cell)
 }
 
 
-Tabular::idx_type Tabular::setMultiColumn(Cursor & cur, idx_type cell, idx_type number,
+idx_type Tabular::setMultiColumn(Cursor & cur, idx_type cell, idx_type number,
                                          bool const right_border)
 {
        idx_type const col = cellColumn(cell);
@@ -2033,7 +2056,7 @@ bool Tabular::hasMultiRow(row_type r) const
        return false;
 }
 
-Tabular::idx_type Tabular::setMultiRow(Cursor & cur, idx_type cell, idx_type number,
+idx_type Tabular::setMultiRow(Cursor & cur, idx_type cell, idx_type number,
                                       bool const bottom_border,
                                       LyXAlignment const halign)
 {
@@ -2079,7 +2102,7 @@ Tabular::idx_type Tabular::setMultiRow(Cursor & cur, idx_type cell, idx_type num
 }
 
 
-Tabular::idx_type Tabular::columnSpan(idx_type cell) const
+idx_type Tabular::columnSpan(idx_type cell) const
 {
        row_type const row = cellRow(cell);
        col_type const col = cellColumn(cell);
@@ -2091,7 +2114,7 @@ Tabular::idx_type Tabular::columnSpan(idx_type cell) const
 }
 
 
-Tabular::idx_type Tabular::rowSpan(idx_type cell) const
+idx_type Tabular::rowSpan(idx_type cell) const
 {
        col_type const column = cellColumn(cell);
        col_type row = cellRow(cell) + 1;
@@ -2167,13 +2190,11 @@ bool Tabular::needRotating() const
 
 bool Tabular::isLastCell(idx_type cell) const
 {
-       if (cell + 1 < numberofcells)
-               return false;
-       return true;
+       return cell + 1 >= numberofcells;
 }
 
 
-Tabular::idx_type Tabular::cellAbove(idx_type cell) const
+idx_type Tabular::cellAbove(idx_type cell) const
 {
        if (cellRow(cell) == 0)
                return cell;
@@ -2187,7 +2208,7 @@ Tabular::idx_type Tabular::cellAbove(idx_type cell) const
 }
 
 
-Tabular::idx_type Tabular::cellBelow(idx_type cell) const
+idx_type Tabular::cellBelow(idx_type cell) const
 {
        row_type const nextrow = cellRow(cell) + rowSpan(cell);
        if (nextrow < nrows())
@@ -2196,7 +2217,7 @@ Tabular::idx_type Tabular::cellBelow(idx_type cell) const
 }
 
 
-Tabular::idx_type Tabular::cellIndex(row_type row, col_type column) const
+idx_type Tabular::cellIndex(row_type row, col_type column) const
 {
        LASSERT(column != npos && column < ncols(), column = 0);
        LASSERT(row != npos && row < nrows(), row = 0);
@@ -2347,7 +2368,7 @@ bool Tabular::haveLTLastFoot(bool withcaptions) const
 }
 
 
-Tabular::idx_type Tabular::setLTCaption(Cursor & cur, row_type row, bool what)
+idx_type Tabular::setLTCaption(Cursor & cur, row_type row, bool what)
 {
        idx_type i = getFirstCellInRow(row);
        if (what) {
@@ -2467,7 +2488,8 @@ bool Tabular::isPartOfMultiRow(row_type row, col_type column) const
 }
 
 
-void Tabular::TeXTopHLine(otexstream & os, row_type row, list<col_type> columns) const
+void Tabular::TeXTopHLine(otexstream & os, row_type row, list<col_type> columns,
+                         list<col_type> logical_columns) const
 {
        // we only output complete row lines and the 1st row here, the rest
        // is done in Tabular::TeXBottomHLine(...)
@@ -2502,15 +2524,16 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list<col_type> 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 {
@@ -2519,7 +2542,12 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list<col_type> columns)
        } else if (realfirstrow || have_trims) {
                string const cline = use_booktabs ? "\\cmidrule" : "\\cline";
                col_type c = 0;
-               for (auto & cl : columns) {
+               std::list<col_type>::const_iterator it1 = logical_columns.begin();
+               std::list<col_type>::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;
@@ -2528,7 +2556,8 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list<col_type> columns)
                                for (col_type j = 0 ; j < c; ++j)
                                        if (column_info[j].alignment == LYX_ALIGN_DECIMAL)
                                                ++offset;
-                               string const firstcol = convert<string>(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;
@@ -2553,7 +2582,7 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list<col_type> 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";
@@ -2561,8 +2590,12 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list<col_type> 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;
                        }
@@ -2572,13 +2605,14 @@ void Tabular::TeXTopHLine(otexstream & os, row_type row, list<col_type> columns)
 }
 
 
-void Tabular::TeXBottomHLine(otexstream & os, row_type row, list<col_type> columns) const
+void Tabular::TeXBottomHLine(otexstream & os, row_type row, list<col_type> columns,
+                            list<col_type> 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<col_type, bool> bottomline, topline, topltrims, toprtrims, bottomltrims, bottomrtrims;
        bool nextrowset = true;
        for (auto const & c : columns) {
@@ -2621,7 +2655,7 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list<col_type> 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;
@@ -2631,10 +2665,10 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list<col_type> 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
@@ -2642,7 +2676,12 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list<col_type> colum
        } else {
                string const cline = use_booktabs ? "\\cmidrule" : "\\cline";
                col_type c = 0;
-               for (auto & cl : columns) {
+               std::list<col_type>::const_iterator it1 = logical_columns.begin();
+               std::list<col_type>::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;
@@ -2651,7 +2690,8 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list<col_type> colum
                                for (col_type j = 0 ; j < c; ++j)
                                        if (column_info[j].alignment == LYX_ALIGN_DECIMAL)
                                                ++offset;
-                               string const firstcol = convert<string>(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;
@@ -2678,7 +2718,7 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list<col_type> 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";
@@ -2686,8 +2726,12 @@ void Tabular::TeXBottomHLine(otexstream & os, row_type row, list<col_type> 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;
                        }
@@ -2895,7 +2939,8 @@ void Tabular::TeXCellPostamble(otexstream & os, idx_type cell,
 
 void Tabular::TeXLongtableHeaderFooter(otexstream & os,
                                       OutputParams const & runparams,
-                                      list<col_type> columns) const
+                                      list<col_type> columns,
+                                      list<col_type> logical_columns) const
 {
        if (!is_long_tabular)
                return;
@@ -2907,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
@@ -2916,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";
@@ -2930,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";
@@ -2942,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";
@@ -2956,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";
@@ -2977,12 +3022,10 @@ bool Tabular::isValidRow(row_type row) const
 
 void Tabular::TeXRow(otexstream & os, row_type row,
                     OutputParams const & runparams,
-                    list<col_type> columns) const
+                    list<col_type> columns, list<col_type> 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)
@@ -3008,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) {
@@ -3088,12 +3132,11 @@ void Tabular::TeXRow(otexstream & os, row_type row,
                } else if (ltCaption(row)) {
                        // Inside longtable caption rows, we must only output the caption inset
                        // with its content and omit anything outside of that (see #10791)
-                       InsetIterator it = inset_iterator_begin(*const_cast<InsetTableCell *>(inset));
-                       InsetIterator i_end = inset_iterator_end(*const_cast<InsetTableCell *>(inset));
-                       for (; it != i_end; ++it) {
-                               if (it->lyxCode() != CAPTION_CODE)
+                       InsetTableCell & tc_inset = *const_cast<InsetTableCell *>(inset);
+                       for (Inset const & it : tc_inset) {
+                               if (it.lyxCode() != CAPTION_CODE)
                                        continue;
-                               it->latex(os, runparams);
+                               it.latex(os, runparams);
                                break;
                        }
                } else if (!isPartOfMultiRow(row, c)) {
@@ -3130,7 +3173,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)
@@ -3179,11 +3222,17 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const
                runparams.local_font->isRightToLeft()
                && runparams.useBidiPackage();
        list<col_type> columns;
+       list<col_type> 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
@@ -3398,15 +3447,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";
                }
@@ -3446,165 +3497,264 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const
 }
 
 
-int Tabular::docbookRow(odocstream & os, row_type row,
-                          OutputParams const & runparams) const
+void Tabular::docbookRow(XMLStream & xs, row_type row,
+                  OutputParams const & runparams, bool header) const
 {
-       int ret = 0;
+       switch (buffer().params().docbook_table_output) {
+       case BufferParams::HTMLTable:
+               docbookRowAsHTML(xs, row, runparams, header);
+               break;
+       case BufferParams::CALSTable:
+               docbookRowAsCALS(xs, row, runparams);
+               break;
+       }
+}
+
+
+void Tabular::docbookRowAsHTML(XMLStream & xs, row_type row,
+                  OutputParams const & runparams, bool header) const
+{
+       string const celltag = header ? "th" : "td";
        idx_type cell = getFirstCellInRow(row);
 
-       os << "<row>\n";
+       xs << xml::StartTag("tr");
+       xs << xml::CR();
        for (col_type c = 0; c < ncols(); ++c) {
-               if (isPartOfMultiColumn(row, c))
+               if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c))
                        continue;
 
-               os << "<entry align=\"";
+               stringstream attr;
+
+               Length const cwidth = column_info[c].p_width;
+               if (!cwidth.zero()) {
+                       string const hwidth = cwidth.asHTMLString();
+                       attr << "style =\"width: " << hwidth << ";\" ";
+               }
+
+               attr << "align='";
                switch (getAlignment(cell)) {
+               case LYX_ALIGN_BLOCK:
+                       attr << "justify";
+                       break;
+               case LYX_ALIGN_DECIMAL: {
+                       Language const *tlang = buffer().paragraphs().front().getParLanguage(buffer().params());
+                       attr << "char' char='" << to_utf8(tlang->decimalSeparator());
+               }
+                       break;
                case LYX_ALIGN_LEFT:
-                       os << "left";
+                       attr << "left";
                        break;
                case LYX_ALIGN_RIGHT:
-                       os << "right";
+                       attr << "right";
                        break;
                default:
-                       os << "center";
+                       attr << "center";
                        break;
                }
-
-               os << "\" valign=\"";
+               attr << "'";
+               attr << " valign='";
                switch (getVAlignment(cell)) {
                case LYX_VALIGN_TOP:
-                       os << "top";
+                       attr << "top";
                        break;
                case LYX_VALIGN_BOTTOM:
-                       os << "bottom";
+                       attr << "bottom";
                        break;
                case LYX_VALIGN_MIDDLE:
-                       os << "middle";
+                       attr << "middle";
                }
-               os << '"';
+               attr << "'";
 
-               if (isMultiColumn(cell)) {
-                       os << " namest=\"col" << c << "\" ";
-                       os << "nameend=\"col" << c + columnSpan(cell) - 1 << '"';
-               }
+               if (isMultiColumn(cell))
+                       attr << " colspan='" << columnSpan(cell) << "'";
+               else if (isMultiRow(cell))
+                       attr << " rowspan='" << rowSpan(cell) << "'";
 
-               os << '>';
-               ret += cellInset(cell)->docbook(os, runparams);
-               os << "</entry>\n";
+               xs << xml::StartTag(celltag, attr.str(), true);
+               cellInset(cell)->docbook(xs, runparams);
+               xs << xml::EndTag(celltag);
+               xs << xml::CR();
                ++cell;
        }
-       os << "</row>\n";
-       return ret;
+       xs << xml::EndTag("tr");
+       xs << xml::CR();
 }
 
 
-int Tabular::docbook(odocstream & os, OutputParams const & runparams) const
+void Tabular::docbookRowAsCALS(XMLStream & xs, row_type row,
+                                OutputParams const & runparams) const
 {
-       int ret = 0;
+       idx_type cell = getFirstCellInRow(row);
 
-       //+---------------------------------------------------------------------
-       //+                      first the opening preamble                    +
-       //+---------------------------------------------------------------------
+       xs << xml::StartTag("row");
+       xs << xml::CR();
+       for (col_type c = 0; c < ncols(); ++c) {
+               if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c))
+                       continue;
 
-       os << "<tgroup cols=\"" << ncols()
-          << "\" colsep=\"1\" rowsep=\"1\">\n";
+               stringstream attr;
 
-       for (col_type c = 0; c < ncols(); ++c) {
-               os << "<colspec colname=\"col" << c << "\" align=\"";
-               switch (column_info[c].alignment) {
+               attr << "align='";
+               switch (getAlignment(cell)) {
+               case LYX_ALIGN_BLOCK:
+                       attr << "justify";
+                       break;
+               case LYX_ALIGN_DECIMAL: {
+                       Language const *tlang = buffer().paragraphs().front().getParLanguage(buffer().params());
+                       attr << "char' char='" << to_utf8(tlang->decimalSeparator());
+               }
+                       break;
                case LYX_ALIGN_LEFT:
-                       os << "left";
+                       attr << "left";
                        break;
                case LYX_ALIGN_RIGHT:
-                       os << "right";
+                       attr << "right";
                        break;
+
                default:
-                       os << "center";
+                       attr << "center";
                        break;
                }
-               os << '"';
-               if (runparams.flavor == OutputParams::XML)
-                       os << '/';
-               os << ">\n";
-               ++ret;
+               attr << "'";
+               attr << " valign='";
+               switch (getVAlignment(cell)) {
+               case LYX_VALIGN_TOP:
+                       attr << "top";
+                       break;
+               case LYX_VALIGN_BOTTOM:
+                       attr << "bottom";
+                       break;
+               case LYX_VALIGN_MIDDLE:
+                       attr << "middle";
+               }
+               attr << "'";
+
+               if (isMultiColumn(cell))
+                       attr << " colspan='" << columnSpan(cell) << "'";
+               else if (isMultiRow(cell))
+                       attr << " rowspan='" << rowSpan(cell) << "'";
+               else
+                       attr << " colname='c" << (c + 1) << "'"; // Column numbering starts at 1.
+
+               // All cases where there should be a line *below* this row.
+               if (row_info[row].bottom_space_default)
+                       attr << " rowsep='1'";
+
+               xs << xml::StartTag("entry", attr.str(), true);
+               cellInset(cell)->docbook(xs, runparams);
+               xs << xml::EndTag("entry");
+               xs << xml::CR();
+               ++cell;
        }
+       xs << xml::EndTag("row");
+       xs << xml::CR();
+}
 
-       //+---------------------------------------------------------------------
-       //+                      Long Tabular case                             +
-       //+---------------------------------------------------------------------
 
-       // output caption info
-       // The caption flag wins over head/foot
+void Tabular::docbook(XMLStream & xs, OutputParams const & runparams) const
+{
+       docstring ret;
+
+       // Some tables are inline. Likely limitation: cannot output a table within a table; is that really a limitation?
+       if (!runparams.docbook_in_table) { // Check on the *outer* set of parameters, so that the table can be closed
+               // properly at the end of this function.
+               xs << xml::StartTag("informaltable");
+               xs << xml::CR();
+       }
+
+       // "Formal" tables have a title and use the tag <table>; the distinction with <informaltable> is done outside.
+       // HTML has the caption first with titles forbidden, and CALS has a title first.
        if (haveLTCaption()) {
-               os << "<caption>\n";
-               ++ret;
-               for (row_type r = 0; r < nrows(); ++r) {
-                       if (row_info[r].caption) {
-                               ret += docbookRow(os, r, runparams);
-                       }
+               std::string tag = ((buffer().params().docbook_table_output) == BufferParams::HTMLTable) ? "caption" : "title";
+
+               xs << xml::StartTag(tag);
+               for (row_type r = 0; r < nrows(); ++r)
+                       if (row_info[r].caption)
+                               docbookRow(xs, r, runparams);
+               xs << xml::EndTag(tag);
+               xs << xml::CR();
+       }
+
+       // CALS header: describe all columns in this table. For names, take 'c' then the ID of the column.
+       // Start at one, as is customary with CALS!
+       if (buffer().params().docbook_table_output == BufferParams::CALSTable) {
+               for (col_type c = 0; c < ncols(); ++c) {
+                       std::stringstream attr;
+                       attr << "colnum='" << (c + 1) << "' ";
+                       attr << "colname='c" << (c + 1) << "' ";
+                       Length const cwidth = column_info[c].p_width;
+                       if (!cwidth.zero())
+                               attr << "colwidth='" << cwidth.asHTMLString() << "' ";
+                       attr << "rowheader='norowheader'"; // Last attribute, hence no space at the end.
+
+                       xs << xml::CompTag("colspec", attr.str());
+                       xs << xml::CR();
                }
-               os << "</caption>\n";
-               ++ret;
        }
-       // output header info
-       if (haveLTHead(false) || haveLTFirstHead(false)) {
-               os << "<thead>\n";
-               ++ret;
+
+       // Output the header of the table. For both HTML and CALS, this is surrounded by a thead.
+       bool const havefirsthead = haveLTFirstHead(false);
+       // if we have a first head, then we are going to ignore the
+       // headers for the additional pages, since there aren't any
+       // in DocBook. this test accomplishes that.
+       bool const havehead = !havefirsthead && haveLTHead(false);
+       if (havehead || havefirsthead) {
+               xs << xml::StartTag("thead") << xml::CR();
                for (row_type r = 0; r < nrows(); ++r) {
-                       if ((row_info[r].endhead || row_info[r].endfirsthead) &&
+                       if (((havefirsthead && row_info[r].endfirsthead) ||
+                            (havehead && row_info[r].endhead)) &&
                            !row_info[r].caption) {
-                               ret += docbookRow(os, r, runparams);
+                               docbookRow(xs, r, runparams, true); // TODO: HTML vs CALS
                        }
                }
-               os << "</thead>\n";
-               ++ret;
+               xs << xml::EndTag("thead");
+               xs << xml::CR();
        }
-       // output footer info
-       if (haveLTFoot(false) || haveLTLastFoot(false)) {
-               os << "<tfoot>\n";
-               ++ret;
+
+       // Output the footer of the table. For both HTML and CALS, this is surrounded by a tfoot and output just after
+       // the header (and before the body).
+       bool const havelastfoot = haveLTLastFoot(false);
+       // as before.
+       bool const havefoot = !havelastfoot && haveLTFoot(false);
+       if (havefoot || havelastfoot) {
+               xs << xml::StartTag("tfoot") << xml::CR();
                for (row_type r = 0; r < nrows(); ++r) {
-                       if ((row_info[r].endfoot || row_info[r].endlastfoot) &&
+                       if (((havelastfoot && row_info[r].endlastfoot) ||
+                            (havefoot && row_info[r].endfoot)) &&
                            !row_info[r].caption) {
-                               ret += docbookRow(os, r, runparams);
+                               docbookRow(xs, r, runparams); // TODO: HTML vs CALS
                        }
                }
-               os << "</tfoot>\n";
-               ++ret;
+               xs << xml::EndTag("tfoot");
+               xs << xml::CR();
        }
 
-       //+---------------------------------------------------------------------
-       //+                      the single row and columns (cells)            +
-       //+---------------------------------------------------------------------
+       // Output the main part of the table. The tbody container is mandatory for CALS, but optional for HTML (only if
+       // there is no header and no footer). It never hurts to have it, though.
+       xs << xml::StartTag("tbody");
+       xs << xml::CR();
+       for (row_type r = 0; r < nrows(); ++r)
+               if (isValidRow(r))
+                       docbookRow(xs, r, runparams);
+       xs << xml::EndTag("tbody");
+       xs << xml::CR();
 
-       os << "<tbody>\n";
-       ++ret;
-       for (row_type r = 0; r < nrows(); ++r) {
-               if (isValidRow(r)) {
-                       ret += docbookRow(os, r, runparams);
-               }
+       // If this method started the table tag, also make it close it.
+       if (!runparams.docbook_in_table) {
+               xs << xml::EndTag("informaltable");
+               xs << xml::CR();
        }
-       os << "</tbody>\n";
-       ++ret;
-       //+---------------------------------------------------------------------
-       //+                      the closing of the tabular                    +
-       //+---------------------------------------------------------------------
-
-       os << "</tgroup>";
-       ++ret;
-
-       return ret;
 }
 
 
-docstring Tabular::xhtmlRow(XHTMLStream & xs, row_type row,
+docstring Tabular::xhtmlRow(XMLStream & xs, row_type row,
                           OutputParams const & runparams, bool header) const
 {
        docstring ret;
        string const celltag = header ? "th" : "td";
        idx_type cell = getFirstCellInRow(row);
 
-       xs << html::StartTag("tr");
+       xs << xml::StartTag("tr");
        for (col_type c = 0; c < ncols(); ++c) {
                if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c))
                        continue;
@@ -3648,17 +3798,17 @@ docstring Tabular::xhtmlRow(XHTMLStream & xs, row_type row,
                else if (isMultiRow(cell))
                        attr << " rowspan='" << rowSpan(cell) << "'";
 
-               xs << html::StartTag(celltag, attr.str(), true) << html::CR();
+               xs << xml::StartTag(celltag, attr.str(), true) << xml::CR();
                ret += cellInset(cell)->xhtml(xs, runparams);
-               xs << html::EndTag(celltag) << html::CR();
+               xs << xml::EndTag(celltag) << xml::CR();
                ++cell;
        }
-       xs << html::EndTag("tr");
+       xs << xml::EndTag("tr");
        return ret;
 }
 
 
-docstring Tabular::xhtml(XHTMLStream & xs, OutputParams const & runparams) const
+docstring Tabular::xhtml(XMLStream & xs, OutputParams const & runparams) const
 {
        docstring ret;
 
@@ -3676,20 +3826,22 @@ docstring Tabular::xhtml(XHTMLStream & xs, OutputParams const & runparams) const
                        align = "right";
                        break;
                }
-               xs << html::StartTag("div", "class='longtable' style='text-align: " + align + ";'")
-                  << html::CR();
+               xs << xml::StartTag("div", "class='longtable' style='text-align: " + align + ";'");
+               xs << xml::CR();
                // The caption flag wins over head/foot
                if (haveLTCaption()) {
-                       xs << html::StartTag("div", "class='longtable-caption' style='text-align: " + align + ";'")
-                          << html::CR();
+                       xs << xml::StartTag("div", "class='longtable-caption' style='text-align: " + align + ";'");
+                       xs << xml::CR();
                        for (row_type r = 0; r < nrows(); ++r)
                                if (row_info[r].caption)
                                        ret += xhtmlRow(xs, r, runparams);
-                       xs << html::EndTag("div") << html::CR();
+                       xs << xml::EndTag("div");
+                       xs << xml::CR();
                }
        }
 
-       xs << html::StartTag("table") << html::CR();
+       xs << xml::StartTag("table");
+       xs << xml::CR();
 
        // output header info
        bool const havefirsthead = haveLTFirstHead(false);
@@ -3698,7 +3850,8 @@ docstring Tabular::xhtml(XHTMLStream & xs, OutputParams const & runparams) const
        // in XHTML. this test accomplishes that.
        bool const havehead = !havefirsthead && haveLTHead(false);
        if (havehead || havefirsthead) {
-               xs << html::StartTag("thead") << html::CR();
+               xs << xml::StartTag("thead");
+               xs << xml::CR();
                for (row_type r = 0; r < nrows(); ++r) {
                        if (((havefirsthead && row_info[r].endfirsthead) ||
                             (havehead && row_info[r].endhead)) &&
@@ -3706,14 +3859,15 @@ docstring Tabular::xhtml(XHTMLStream & xs, OutputParams const & runparams) const
                                ret += xhtmlRow(xs, r, runparams, true);
                        }
                }
-               xs << html::EndTag("thead") << html::CR();
+               xs << xml::EndTag("thead");
+               xs << xml::CR();
        }
        // output footer info
        bool const havelastfoot = haveLTLastFoot(false);
        // as before.
        bool const havefoot = !havelastfoot && haveLTFoot(false);
        if (havefoot || havelastfoot) {
-               xs << html::StartTag("tfoot") << html::CR();
+               xs << xml::StartTag("tfoot") << xml::CR();
                for (row_type r = 0; r < nrows(); ++r) {
                        if (((havelastfoot && row_info[r].endlastfoot) ||
                             (havefoot && row_info[r].endfoot)) &&
@@ -3721,21 +3875,22 @@ docstring Tabular::xhtml(XHTMLStream & xs, OutputParams const & runparams) const
                                ret += xhtmlRow(xs, r, runparams);
                        }
                }
-               xs << html::EndTag("tfoot") << html::CR();
+               xs << xml::EndTag("tfoot");
+               xs << xml::CR();
        }
 
-       xs << html::StartTag("tbody") << html::CR();
-       for (row_type r = 0; r < nrows(); ++r) {
-               if (isValidRow(r)) {
+       xs << xml::StartTag("tbody") << xml::CR();
+       for (row_type r = 0; r < nrows(); ++r)
+               if (isValidRow(r))
                        ret += xhtmlRow(xs, r, runparams);
-               }
+       xs << xml::EndTag("tbody");
+       xs << xml::CR();
+       xs << xml::EndTag("table");
+       xs << xml::CR();
+       if (is_long_tabular) {
+               xs << xml::EndTag("div");
+               xs << xml::CR();
        }
-       xs << html::EndTag("tbody")
-          << html::CR()
-          << html::EndTag("table")
-          << html::CR();
-       if (is_long_tabular)
-               xs << html::EndTag("div") << html::CR();
        return ret;
 }
 
@@ -4050,7 +4205,6 @@ InsetTableCell::InsetTableCell(Buffer * buf)
          isMultiColumn(false), isMultiRow(false), contentAlign(LYX_ALIGN_CENTER)
 {}
 
-
 bool InsetTableCell::forcePlainLayout(idx_type) const
 {
        return isMultiRow || (isMultiColumn && !isFixedWidth);
@@ -4115,7 +4269,7 @@ void InsetTableCell::addToToc(DocIterator const & di, bool output_active,
 }
 
 
-docstring InsetTableCell::xhtml(XHTMLStream & xs, OutputParams const & rp) const
+docstring InsetTableCell::xhtml(XMLStream & xs, OutputParams const & rp) const
 {
        if (!isFixedWidth)
                return InsetText::insetAsXHTML(xs, rp, InsetText::JustText);
@@ -4123,13 +4277,20 @@ docstring InsetTableCell::xhtml(XHTMLStream & xs, OutputParams const & rp) const
 }
 
 
+void InsetTableCell::docbook(XMLStream & xs, OutputParams const & runparams) const
+{
+       InsetText::docbook(xs, runparams);
+}
+
+
 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
@@ -4143,14 +4304,13 @@ 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;
 }
 
 
-
 /////////////////////////////////////////////////////////////////////
 //
 // InsetTabular
@@ -4203,6 +4363,18 @@ bool InsetTabular::insetAllowed(InsetCode code) const
 }
 
 
+bool InsetTabular::allowMultiPar() const
+{
+       for (col_type c = 0; c < tabular.ncols(); ++c) {
+               for (row_type r = 0; r < tabular.nrows(); ++r) {
+                       if (tabular.cellInset(r,c)->allowMultiPar())
+                               return true;
+               }
+       }
+       return false;
+}
+
+
 bool InsetTabular::allowsCaptionVariation(std::string const & newtype) const
 {
        return tabular.is_long_tabular &&
@@ -4324,7 +4496,7 @@ void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const
                                docstring const align_d = tabular.column_info[c].decimal_point;
                                dit = separatorPos(&tail, align_d);
 
-                               pit_type const psize = tail.paragraphs().front().size();
+                               pos_type const psize = tail.paragraphs().front().size();
                                if (dit) {
                                        tail.paragraphs().front().eraseChars(0,
                                                dit.pos() < psize ? dit.pos() + 1 : psize, false);
@@ -4337,9 +4509,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:
@@ -4419,6 +4591,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) {
@@ -4435,6 +4610,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.
@@ -4628,7 +4812,7 @@ void InsetTabular::edit(Cursor & cur, bool front, EntryDirection)
 }
 
 
-void InsetTabular::updateBuffer(ParIterator const & it, UpdateType utype)
+void InsetTabular::updateBuffer(ParIterator const & it, UpdateType utype, bool const /*deleted*/)
 {
        // In a longtable, tell captions what the current float is
        Counters & cnts = buffer().masterBuffer()->params().documentClass().counters();
@@ -5120,6 +5304,16 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd)
                                        break;
                                }
                        }
+                       else if (theClipboard().hasTextContents(Clipboard::LyXTextType)) {
+                               // This might be tabular data from another LyX instance. Check!
+                               docstring const clip =
+                                       theClipboard().getAsText(Clipboard::PlainTextType);
+                               if (clip.find_first_of(from_ascii("\t\n")) != docstring::npos) {
+                                       FuncRequest ncmd = FuncRequest(LFUN_CLIPBOARD_PASTE, cmd.argument());
+                                       doDispatch(cur, ncmd);
+                                       break;
+                               }
+                       }
                        if (!cur.selIsMultiCell())
                                cell(cur.idx())->dispatch(cur, cmd);
                        break;
@@ -5218,8 +5412,12 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd)
                                }
                        }
                        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 {
@@ -5554,8 +5752,8 @@ bool InsetTabular::getFeatureStatus(Cursor & cur, string const & s,
                case Tabular::SET_LONGTABULAR:
                case Tabular::TOGGLE_LONGTABULAR:
                        // setting as longtable is not allowed when table is inside a float
-                       if (cur.innerInsetOfType(FLOAT_CODE) != 0
-                               || cur.innerInsetOfType(WRAP_CODE) != 0)
+                       if (cur.innerInsetOfType(FLOAT_CODE) != nullptr
+                               || cur.innerInsetOfType(WRAP_CODE) != nullptr)
                                status.setEnabled(false);
                        else
                                status.setEnabled(true);
@@ -5898,18 +6096,18 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd,
 }
 
 
-Inset::DisplayType InsetTabular::display() const
+Inset::RowFlags InsetTabular::rowFlags() const
 {
                if (tabular.is_long_tabular) {
                        switch (tabular.longtabular_alignment) {
                        case Tabular::LYX_LONGTABULAR_ALIGN_LEFT:
-                               return AlignLeft;
+                               return Display | AlignLeft;
                        case Tabular::LYX_LONGTABULAR_ALIGN_CENTER:
-                               return AlignCenter;
+                               return Display;
                        case Tabular::LYX_LONGTABULAR_ALIGN_RIGHT:
-                               return AlignRight;
+                               return Display | AlignRight;
                        default:
-                               return AlignCenter;
+                               return Display;
                        }
                } else
                        return Inline;
@@ -5932,34 +6130,13 @@ int InsetTabular::plaintext(odocstringstream & os,
 }
 
 
-int InsetTabular::docbook(odocstream & os, OutputParams const & runparams) const
+void InsetTabular::docbook(XMLStream & xs, OutputParams const & runparams) const
 {
-       int ret = 0;
-       Inset * master = 0;
-
-       // FIXME: Why not pass a proper DocIterator here?
-#if 0
-       // if the table is inside a float it doesn't need the informaltable
-       // wrapper. Search for it.
-       for (master = owner(); master; master = master->owner())
-               if (master->lyxCode() == FLOAT_CODE)
-                       break;
-#endif
-
-       if (!master) {
-               os << "<informaltable>";
-               ++ret;
-       }
-       ret += tabular.docbook(os, runparams);
-       if (!master) {
-               os << "</informaltable>";
-               ++ret;
-       }
-       return ret;
+       tabular.docbook(xs, runparams);
 }
 
 
-docstring InsetTabular::xhtml(XHTMLStream & xs, OutputParams const & rp) const
+docstring InsetTabular::xhtml(XMLStream & xs, OutputParams const & rp) const
 {
        return tabular.xhtml(xs, rp);
 }
@@ -6048,7 +6225,7 @@ void InsetTabular::setCursorFromCoordinates(Cursor & cur, int x, int y) const
 }
 
 
-InsetTabular::idx_type InsetTabular::getNearestCell(BufferView & bv, int x, int y) const
+idx_type InsetTabular::getNearestCell(BufferView & bv, int x, int y) const
 {
        idx_type idx_min = 0;
        int dist_min = numeric_limits<int>::max();
@@ -6995,23 +7172,23 @@ bool InsetTabular::copySelection(Cursor & cur)
        paste_tabular.reset(new Tabular(tabular));
 
        for (row_type r = 0; r < rs; ++r)
-               paste_tabular->deleteRow(0);
+               paste_tabular->deleteRow(0, true);
 
        row_type const rows = re - rs + 1;
        while (paste_tabular->nrows() > rows)
-               paste_tabular->deleteRow(rows);
+               paste_tabular->deleteRow(rows, true);
 
        for (col_type c = 0; c < cs; ++c)
-               paste_tabular->deleteColumn(0);
+               paste_tabular->deleteColumn(0, true);
 
        col_type const columns = ce - cs + 1;
        while (paste_tabular->ncols() > columns)
-               paste_tabular->deleteColumn(columns);
+               paste_tabular->deleteColumn(columns, true);
 
        paste_tabular->setBuffer(tabular.buffer());
 
        odocstringstream os;
-       OutputParams const runparams(0);
+       OutputParams const runparams(nullptr);
        paste_tabular->plaintext(os, runparams, 0, true, '\t', INT_MAX);
        // Needed for the "Edit->Paste recent" menu and the system clipboard.
        cap::copySelection(cur, os.str());
@@ -7039,12 +7216,15 @@ bool InsetTabular::pasteClipboard(Cursor & cur)
                getSelection(cur, actrow, re, actcol, ce);
        }
 
-       for (row_type r1 = 0, r2 = actrow;
-            r1 < paste_tabular->nrows() && r2 < tabular.nrows();
-            ++r1, ++r2) {
-               for (col_type c1 = 0, c2 = actcol;
-                   c1 < paste_tabular->ncols() && c2 < tabular.ncols();
-                   ++c1, ++c2) {
+       col_type const oldncols = tabular.ncols();
+       for (row_type r1 = 0, r2 = actrow; r1 < paste_tabular->nrows(); ++r1, ++r2) {
+               // Append rows if needed
+               if (r2 == tabular.nrows())
+                       tabular.insertRow(r2 - 1, false);
+               for (col_type c1 = 0, c2 = actcol; c1 < paste_tabular->ncols(); ++c1, ++c2) {
+                       // Append columns if needed
+                       if (c2 == tabular.ncols())
+                               tabular.insertColumn(c2 - 1, false);
                        if (paste_tabular->isPartOfMultiColumn(r1, c1) &&
                              tabular.isPartOfMultiColumn(r2, c2))
                                continue;
@@ -7062,13 +7242,18 @@ bool InsetTabular::pasteClipboard(Cursor & cur)
                        // FIXME?: why do we need to do this explicitly? (EL)
                        tabular.cellInset(r2, c2)->setBuffer(tabular.buffer());
 
-                       // FIXME: change tracking (MG)
-                       inset->setChange(Change(buffer().params().track_changes ?
+                       if (!lyxrc.ct_markup_copied) {
+                               // do not paste deleted text
+                               inset->acceptChanges();
+                               inset->setChange(Change(buffer().params().track_changes ?
                                                Change::INSERTED : Change::UNCHANGED));
+                       }
                        cur.pos() = 0;
                        cur.pit() = 0;
                }
        }
+       // amend cursor position if cols have been appended
+       cur.idx() += actrow * (tabular.ncols() - oldncols);
        return true;
 }
 
@@ -7135,6 +7320,22 @@ docstring InsetTabular::asString(idx_type stidx, idx_type enidx,
 }
 
 
+ParagraphList InsetTabular::asParList(idx_type stidx, idx_type enidx)
+{
+       LASSERT(stidx <= enidx, return ParagraphList());
+       ParagraphList retval;
+       col_type const col1 = tabular.cellColumn(stidx);
+       col_type const col2 = tabular.cellColumn(enidx);
+       row_type const row1 = tabular.cellRow(stidx);
+       row_type const row2 = tabular.cellRow(enidx);
+       for (col_type col = col1; col <= col2; col++)
+               for (row_type row = row1; row <= row2; row++)
+                       for (auto const & par : tabular.cellInset(row, col)->paragraphs())
+                               retval.push_back(par);
+       return retval;
+}
+
+
 void InsetTabular::getSelection(Cursor & cur,
        row_type & rs, row_type & re, col_type & cs, col_type & ce) const
 {
@@ -7154,16 +7355,20 @@ void InsetTabular::getSelection(Cursor & cur,
 
 Text * InsetTabular::getText(int idx) const
 {
-       return size_t(idx) < nargs() ? cell(idx)->getText(0) : 0;
+       return size_t(idx) < nargs() ? cell(idx)->getText(0) : nullptr;
 }
 
 
 bool InsetTabular::isChanged() const
 {
-       for (idx_type idx = 0; idx < nargs(); ++idx)
+       for (idx_type idx = 0; idx < nargs(); ++idx) {
                if (cell(idx)->isChanged())
                        return true;
-       // FIXME: shall we look at row/columns changed status?
+               if (tabular.row_info[tabular.cellRow(idx)].change.changed())
+                       return true;
+               if (tabular.column_info[tabular.cellColumn(idx)].change.changed())
+                       return true;
+       }
        return false;
 }
 
@@ -7191,6 +7396,7 @@ void InsetTabular::acceptChanges()
                else if (tabular.column_info[col].change.deleted())
                        tabular.deleteColumn(col, true);
        }
+       tabular.updateIndexes();
 }
 
 
@@ -7210,6 +7416,7 @@ void InsetTabular::rejectChanges()
                else if (tabular.column_info[col].change.inserted())
                        tabular.deleteColumn(col, true);
        }
+       tabular.updateIndexes();
 }
 
 
@@ -7384,7 +7591,7 @@ bool InsetTabular::showCompletionCursor() const
 
 CompletionList const * InsetTabular::createCompletionList(Cursor const & cur) const
 {
-       return completionSupported(cur) ? cur.text()->createCompletionList(cur) : 0;
+       return completionSupported(cur) ? cur.text()->createCompletionList(cur) : nullptr;
 }
 
 
@@ -7453,8 +7660,8 @@ string InsetTabular::params2string(InsetTabular const & inset)
 
 void InsetTabular::setLayoutForHiddenCells(DocumentClass const & dc)
 {
-       for (Tabular::col_type c = 0; c < tabular.ncols(); ++c) {
-               for (Tabular::row_type r = 0; r < tabular.nrows(); ++r) {
+       for (col_type c = 0; c < tabular.ncols(); ++c) {
+               for (row_type r = 0; r < tabular.nrows(); ++r) {
                        if (!tabular.isPartOfMultiColumn(r,c) &&
                            !tabular.isPartOfMultiRow(r,c))
                                continue;