]> git.lyx.org Git - features.git/commitdiff
support for multirows in tables:
authorUwe Stöhr <uwestoehr@web.de>
Thu, 11 Feb 2010 01:07:54 +0000 (01:07 +0000)
committerUwe Stöhr <uwestoehr@web.de>
Thu, 11 Feb 2010 01:07:54 +0000 (01:07 +0000)
- fileformat change (lyx2lyx code follows the next days)
- the patch is a joint work of Edwin and me

The patch is huge but the major parts work already. I put it in now as the minor known issues can be solved step by step the next days. I think this is the best solution because collaborating with such large patches already lead to copy/paste bugs.

The known issues are listed here:
http://wiki.lyx.org/Devel/Multirow

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@33414 a592a061-630c-0410-9148-cb99ea01b6c8

15 files changed:
development/FORMAT
development/scons/scons_manifest.py
lib/Makefile.am
lib/images/tabular-feature_multirow.png [new file with mode: 0644]
lib/lyx2lyx/lyx_2_0.py
lib/ui/stdtoolbars.inc
src/Buffer.cpp
src/LaTeXFeatures.cpp
src/LyXAction.cpp
src/frontends/qt4/GuiPrefs.cpp
src/frontends/qt4/GuiTabular.cpp
src/frontends/qt4/GuiTabular.h
src/frontends/qt4/ui/TabularUi.ui
src/insets/InsetTabular.cpp
src/insets/InsetTabular.h

index 09be652d1dd6cad2f333b19480b2df7e5e9fd851..5a861d808d92f9539e94f663e16604f5daf26c92 100644 (file)
@@ -1,6 +1,10 @@
 LyX file-format changes
 -----------------------
 
+2010-02-11 Uwe Stöhr <uwestoehr@web.de> and Edwin Leuven <e.leuven@uva.nl>
+       * Format incremented to 377: support for multirow cells in
+         tables
+
 2010-01-10 Jürgen Spitzmüller <spitz@lyx.org>
        * Format incremented to 376: new buffer param
          \maintain_unincluded_children. If true, the aux files of
index 61161b9533f88f9d0e15373b168305e7f23c50ce..72198618938f15f920a98f13fbf68e1a20532369 100644 (file)
@@ -1517,6 +1517,7 @@ lib_images_files = Split('''
     tabular-feature_delete-column.png
     tabular-feature_delete-row.png
     tabular-feature_multicolumn.png
+    tabular-feature_multirow.png
     tabular-feature_set-all-lines.png
     tabular-feature_set-border-lines.png
     tabular-feature_set-longtabular.png
index 56a69e476385063081125a897fabcae118898e3e..a63711fc846c8fe66bdf82f6c4b0197533adf013 100644 (file)
@@ -415,6 +415,7 @@ dist_images_DATA = \
        images/tabular-feature_delete-column.png \
        images/tabular-feature_delete-row.png \
        images/tabular-feature_multicolumn.png \
+       images/tabular-feature_multirow.png \
        images/tabular-feature_set-all-lines.png \
        images/tabular-feature_set-longtabular.png \
        images/tabular-feature_set-rotate-cell.png \
diff --git a/lib/images/tabular-feature_multirow.png b/lib/images/tabular-feature_multirow.png
new file mode 100644 (file)
index 0000000..11ecd2c
Binary files /dev/null and b/lib/images/tabular-feature_multirow.png differ
index a9090f4b320224ed815b8306163b566033520ef5..4b976ddb1c37f3eceefd9547e36fd9346c611824 100644 (file)
@@ -1197,10 +1197,12 @@ convert = [[346, []],
            [373, [merge_gbrief]],
            [374, []],
            [375, []],
-           [376, []]
+           [376, []],
+           [377, []]
           ]
 
-revert =  [[375, [revert_includeall]],
+revert =  [[376, []],
+           [375, [revert_includeall]],
            [374, [revert_includeonly]],
            [373, [revert_html_options]],
            [372, [revert_gbrief]],
index 3aab9d4109380906a46f606218a081f2927cf19a..21bae0fb9344a60847e5073e1307b41335372f36 100644 (file)
@@ -161,6 +161,7 @@ ToolbarSet
                Item "Rotate cell" "tabular-feature toggle-rotate-cell"
                Item "Rotate table" "tabular-feature toggle-rotate-tabular"
                Item "Set multi-column" "tabular-feature multicolumn"
+               Item "Set multi-row" "tabular-feature multirow"
        End
        
        Toolbar "math" "Math"
index b2b0be18365feb273d6208c1a0cd6d6ebaf2649f..14dac1d46c3fabe65ee05a8af99d4807826d4612 100644 (file)
@@ -126,7 +126,7 @@ namespace {
 
 // Do not remove the comment below, so we get merge conflict in
 // independent branches. Instead add your own.
-int const LYX_FORMAT = 376; // jspitzm: support for unincluded file maintenance
+int const LYX_FORMAT = 377; // uwestoehr: support for multirows
 
 typedef map<string, bool> DepClean;
 typedef map<docstring, pair<InsetLabel const *, Buffer::References> > RefCache;
index 1629186280413fe8ba5d87bcd18f183f107ed095..6b99d976df9dd716af195f29d35d0c10f0542509 100644 (file)
@@ -546,7 +546,8 @@ char const * simplefeatures[] = {
        "bm",
        "pdfpages",
        "amscd",
-       "slashed"
+       "slashed",
+       "multirow"
 };
 
 int const nb_simplefeatures = sizeof(simplefeatures) / sizeof(char const *);
index fc24fb7052ceea65a79a957284582b01544c76b8..673a2308cff805bb80355c725cbe377bda847b1b 100644 (file)
@@ -2069,9 +2069,10 @@ void LyXAction::init()
                        set-rotate-cell|unset-rotate-cell|toggle-rotate-cell|set-usebox|set-lthead|
                        unset-lthead|set-ltfirsthead|unset-ltfirsthead|set-ltfoot|unset-ltfoot|
                        set-ltlastfoot|unset-ltlastfoot|set-ltnewpage|toggle-ltcaption|
-                       set-special-column|set-special-multi|set-booktabs|unset-booktabs|
-                       set-top-space|set-bottom-space|set-interline-space|set-border-lines|
-                       tabular-valign-top|tabular-valign-middle|tabular-valign-bottom \n
+                       set-special-column|set-special-multicolumn|set-special-multirow|
+                       set-booktabs|unset-booktabs|set-top-space|set-bottom-space|
+                       set-interline-space|set-border-lines|tabular-valign-top|
+                       tabular-valign-middle|tabular-valign-bottom \n
                <ARG>: additional argument for some commands, use debug mode to explore its values.
  * \li Origin: Jug, 28 Jul 2000
  * \endvar
index 4518838456d131f635df510acedb43869c3f44fb..2b32d36922259e292d28aa0c1a5c720a62744dcb 100644 (file)
@@ -2805,7 +2805,8 @@ void PrefShortcuts::shortcutOkPressed()
        
        // make sure this key isn't already bound---and, if so, not unbound
        FuncCode const unbind = user_unbind_.getBinding(k).action;
-       if (oldBinding.action != LFUN_UNKNOWN_ACTION && unbind == LFUN_UNKNOWN_ACTION)
+       if (oldBinding.action != LFUN_UNKNOWN_ACTION && unbind == LFUN_UNKNOWN_ACTION
+               && toqstr(makeCmdString(oldBinding)) != save_lfun_)
        {
                // FIXME Perhaps we should offer to over-write the old shortcut?
                // If so, we'll need to remove it from our list, etc.
@@ -2817,7 +2818,7 @@ void PrefShortcuts::shortcutOkPressed()
                return;
        }
 
-       if (!save_lfun_.isEmpty() && new_lfun == save_lfun_)
+       if (!save_lfun_.isEmpty())
                // real modification of the lfun's shortcut,
                // so remove the previous one
                removeShortcut();
index 1ae5f92e2af554b65c7d0573549d5319d78184c2..c3bf97d361816c89d16f1c10ae5c721a04bdf80f 100644 (file)
@@ -96,6 +96,8 @@ GuiTabular::GuiTabular(GuiView & lv)
                this, SLOT(vAlign_changed(int)));
        connect(multicolumnCB, SIGNAL(clicked()),
                this, SLOT(multicolumn_clicked()));
+       connect(multirowCB, SIGNAL(clicked()),
+               this, SLOT(multirow_clicked()));
        connect(newpageCB, SIGNAL(clicked()),
                this, SLOT(ltNewpage_clicked()));
        connect(headerStatusCB, SIGNAL(clicked()),
@@ -172,6 +174,7 @@ GuiTabular::GuiTabular(GuiView & lv)
        bc().addReadOnly(booktabsRB);
 
        bc().addReadOnly(multicolumnCB);
+       bc().addReadOnly(multirowCB);
        bc().addReadOnly(rotateCellCB);
        bc().addReadOnly(rotateTabularCB);
        bc().addReadOnly(specialAlignmentED);
@@ -396,6 +399,12 @@ void GuiTabular::multicolumn_clicked()
        changed();
 }
 
+void GuiTabular::multirow_clicked()
+{
+       toggleMultiRow();
+       changed();
+}
+
 
 void GuiTabular::rotateTabular()
 {
@@ -654,7 +663,7 @@ Length getColumnPWidth(Tabular const & t, size_t cell)
 
 Length getMColumnPWidth(Tabular const & t, size_t cell)
 {
-       if (t.isMultiColumn(cell))
+       if (t.isMultiColumn(cell) || t.isMultiRow(cell))
                return t.cellInfo(cell).p_width;
        return Length();
 }
@@ -662,7 +671,8 @@ Length getMColumnPWidth(Tabular const & t, size_t cell)
 
 docstring getAlignSpecial(Tabular const & t, size_t cell, int what)
 {
-       if (what == Tabular::SET_SPECIAL_MULTI)
+       if (what == Tabular::SET_SPECIAL_MULTICOLUMN
+               || what == Tabular::SET_SPECIAL_MULTIROW)
                return t.cellInfo(cell).align_special;
        return t.column_info[t.cellColumn(cell)].align_special;
 }
@@ -670,7 +680,6 @@ docstring getAlignSpecial(Tabular const & t, size_t cell, int what)
 }
 
 
-
 void GuiTabular::updateContents()
 {
        initialiseParams(string());
@@ -684,9 +693,11 @@ void GuiTabular::updateContents()
        tabularColumnED->setText(QString::number(col + 1));
 
        bool const multicol(tabular_.isMultiColumn(cell));
-
        multicolumnCB->setChecked(multicol);
 
+       bool const multirow(tabular_.isMultiRow(cell));
+       multirowCB->setChecked(multirow);
+
        rotateCellCB->setChecked(tabular_.getRotateCell(cell));
        rotateTabularCB->setChecked(tabular_.rotate);
 
@@ -699,7 +710,11 @@ void GuiTabular::updateContents()
 
        if (multicol) {
                special = getAlignSpecial(tabular_, cell,
-                       Tabular::SET_SPECIAL_MULTI);
+                       Tabular::SET_SPECIAL_MULTICOLUMN);
+               pwidth = getMColumnPWidth(tabular_, cell);
+       } else if (multirow) {
+               special = getAlignSpecial(tabular_, cell,
+                       Tabular::SET_SPECIAL_MULTIROW);
                pwidth = getMColumnPWidth(tabular_, cell);
        } else {
                special = getAlignSpecial(tabular_, cell,
@@ -832,7 +847,8 @@ void GuiTabular::updateContents()
        vAlignCB->setCurrentIndex(valign);
 
        hAlignCB->setEnabled(true);
-       vAlignCB->setEnabled(!pwidth.zero());
+       if (!multirow && !pwidth.zero())
+       vAlignCB->setEnabled(true);
 
        int tableValign = 1;
        switch (tabular_.tabular_valignment) {
@@ -927,6 +943,7 @@ void GuiTabular::updateContents()
        // When a row is set as longtable caption, it must not be allowed
        // to unset that this row is a multicolumn.
        multicolumnCB->setEnabled(funcEnabled(Tabular::MULTICOLUMN));
+       multirowCB->setEnabled(funcEnabled(Tabular::MULTIROW));
 
        Tabular::ltType ltt;
        bool use_empty;
@@ -1021,15 +1038,16 @@ void GuiTabular::closeGUI()
        // apply the fixed width values
        size_t const cell = getActiveCell();
        bool const multicol = tabular_.isMultiColumn(cell);
+       bool const multirow = tabular_.isMultiRow(cell);
        string width = widgetsToLength(widthED, widthUnitCB);
        string width2;
 
        Length llen = getColumnPWidth(tabular_, cell);
        Length llenMulti = getMColumnPWidth(tabular_, cell);
 
-       if (multicol && !llenMulti.zero())
+       if (multicol && multirow && !llenMulti.zero())
                width2 = llenMulti.asString();
-       else if (!multicol && !llen.zero())
+       else if (!multicol && !multirow && !llen.zero())
                width2 = llen.asString();
 
        // apply the special alignment
@@ -1038,14 +1056,19 @@ void GuiTabular::closeGUI()
 
        if (multicol)
                sa2 = getAlignSpecial(tabular_, cell,
-                       Tabular::SET_SPECIAL_MULTI);
+                       Tabular::SET_SPECIAL_MULTICOLUMN);
+       else if (multirow)
+               sa2 = getAlignSpecial(tabular_, cell,
+                       Tabular::SET_SPECIAL_MULTIROW);
        else
                sa2 = getAlignSpecial(tabular_, cell,
                        Tabular::SET_SPECIAL_COLUMN);
 
        if (sa1 != sa2) {
                if (multicol)
-                       set(Tabular::SET_SPECIAL_MULTI, to_utf8(sa1));
+                       set(Tabular::SET_SPECIAL_MULTICOLUMN, to_utf8(sa1));
+               if (multirow)
+                       set(Tabular::SET_SPECIAL_MULTIROW, to_utf8(sa1));
                else
                        set(Tabular::SET_SPECIAL_COLUMN, to_utf8(sa1));
        }
@@ -1162,7 +1185,9 @@ void GuiTabular::set(Tabular::Feature f, string const & arg)
 void GuiTabular::setSpecial(string const & special)
 {
        if (tabular_.isMultiColumn(getActiveCell()))
-               set(Tabular::SET_SPECIAL_MULTI, special);
+               set(Tabular::SET_SPECIAL_MULTICOLUMN, special);
+       else if (tabular_.isMultiRow(getActiveCell()))
+               set(Tabular::SET_SPECIAL_MULTIROW, special);
        else
                set(Tabular::SET_SPECIAL_COLUMN, special);
 }
@@ -1186,6 +1211,13 @@ void GuiTabular::toggleMultiColumn()
 }
 
 
+void GuiTabular::toggleMultiRow()
+{
+       set(Tabular::MULTIROW);
+       updateView();
+}
+
+
 void GuiTabular::rotateTabular(bool yes)
 {
        if (yes)
@@ -1255,7 +1287,8 @@ void GuiTabular::valign(GuiTabular::VALIGN v)
                        break;
        }
 
-       if (tabular_.isMultiColumn(getActiveCell()))
+       if (tabular_.isMultiColumn(getActiveCell())
+               || tabular_.isMultiRow(getActiveCell()))
                set(multi_num);
        else
                set(num);
index 158a0ad6f1434842904c5f32203a4fde510f7773..8b42c74029bbf4cbb434cf1acc7a958267120a7e 100644 (file)
@@ -45,6 +45,7 @@ private Q_SLOTS:
        void topBorder_changed();
        void bottomBorder_changed();
        void multicolumn_clicked();
+       void multirow_clicked();
        void rotateTabular();
        void rotateCell();
        void hAlign_changed(int align);
@@ -101,6 +102,7 @@ private:
        void setWidth(std::string const & width);
 
        void toggleMultiColumn();
+       void toggleMultiRow();
 
        void rotateTabular(bool yes);
        void rotateCell(bool yes);
index 6385ac9ea67b9a60db73e9d20cc7293887026deb..7aa451d851809f97fbfd70f4038180ae9bd1ef14 100644 (file)
       <attribute name="title">
        <string>&amp;Table Settings</string>
       </attribute>
-      <layout class="QGridLayout" name="gridLayout_6">
-       <item row="0" column="0">
+      <layout class="QGridLayout" name="gridLayout">
+       <item row="0" column="0" colspan="2">
         <widget class="QGroupBox" name="GroupBox12">
          <property name="title">
           <string>Column settings</string>
           <item row="3" column="0">
            <widget class="QCheckBox" name="multicolumnCB">
             <property name="toolTip">
-             <string>Merge cells</string>
+             <string>Merge cells of different columns</string>
             </property>
             <property name="text">
              <string>&amp;Multicolumn</string>
        <item row="1" column="0">
         <widget class="QGroupBox" name="groupBox">
          <property name="title">
-          <string>Cell setting</string>
+          <string>Row setting</string>
          </property>
          <layout class="QGridLayout" name="gridLayout_3">
+          <item row="0" column="0">
+           <widget class="QCheckBox" name="multirowCB">
+            <property name="toolTip">
+             <string>Merge cells of different rows</string>
+            </property>
+            <property name="text">
+             <string>M&amp;ultirow</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QGroupBox" name="groupBox_2">
+         <property name="title">
+          <string>Cell setting</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_6">
           <item row="0" column="0">
            <widget class="QCheckBox" name="rotateCellCB">
             <property name="toolTip">
          </layout>
         </widget>
        </item>
-       <item row="2" column="0">
+       <item row="2" column="0" colspan="2">
         <widget class="QGroupBox" name="tabAlignmentGB">
          <property name="enabled">
           <bool>true</bool>
          </layout>
         </widget>
        </item>
-       <item row="3" column="0">
+       <item row="3" column="0" colspan="2">
         <layout class="QHBoxLayout" name="horizontalLayout">
          <item>
           <widget class="QLabel" name="specialAlignmentLA">
          </item>
         </layout>
        </item>
-       <item row="4" column="0">
+       <item row="4" column="1">
         <spacer name="verticalSpacer_2">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
-           <width>20</width>
-           <height>68</height>
+           <width>17</width>
+           <height>0</height>
           </size>
          </property>
         </spacer>
   <tabstop>vAlignCB</tabstop>
   <tabstop>multicolumnCB</tabstop>
   <tabstop>rotateTabularCB</tabstop>
+  <tabstop>multirowCB</tabstop>
   <tabstop>rotateCellCB</tabstop>
   <tabstop>specialAlignmentED</tabstop>
   <tabstop>closePB</tabstop>
index b3ae3059b5aa7998802555b40a7c4c2d155d3d01..c3497de3d9259dd5e9a0db8c3abc43f2d346b807 100644 (file)
@@ -12,6 +12,7 @@
  * \author André Pönitz
  * \author Jürgen Vigna
  * \author Uwe Stöhr
+ * \author Edwin Leuven
  *
  * Full author contact details are available in file CREDITS.
  */
@@ -70,7 +71,6 @@ using namespace std;
 using namespace lyx::support;
 
 using boost::shared_ptr;
-using boost::dynamic_pointer_cast;
 
 
 namespace lyx {
@@ -130,6 +130,7 @@ TabularFeature tabularFeature[] =
        { Tabular::M_VALIGN_BOTTOM, "m-valign-bottom" },
        { Tabular::M_VALIGN_MIDDLE, "m-valign-middle" },
        { Tabular::MULTICOLUMN, "multicolumn" },
+       { Tabular::MULTIROW, "multirow" },
        { Tabular::SET_ALL_LINES, "set-all-lines" },
        { Tabular::UNSET_ALL_LINES, "unset-all-lines" },
        { Tabular::SET_LONGTABULAR, "set-longtabular" },
@@ -154,7 +155,8 @@ TabularFeature tabularFeature[] =
        { Tabular::SET_LTNEWPAGE, "set-ltnewpage" },
        { Tabular::TOGGLE_LTCAPTION, "toggle-ltcaption" },
        { Tabular::SET_SPECIAL_COLUMN, "set-special-column" },
-       { Tabular::SET_SPECIAL_MULTI, "set-special-multi" },
+       { Tabular::SET_SPECIAL_MULTICOLUMN, "set-special-multicolumn" },
+       { Tabular::SET_SPECIAL_MULTIROW, "set-special-multirow" },
        { Tabular::SET_BOOKTABS, "set-booktabs" },
        { Tabular::UNSET_BOOKTABS, "unset-booktabs" },
        { Tabular::SET_TOP_SPACE, "set-top-space" },
@@ -519,6 +521,7 @@ Tabular::CellData::CellData(Buffer * buf)
        : cellno(0),
          width(0),
          multicolumn(Tabular::CELL_NORMAL),
+         multirow(Tabular::CELL_NORMAL),
          alignment(LYX_ALIGN_CENTER),
          valignment(LYX_VALIGN_TOP),
          top_line(false),
@@ -537,6 +540,7 @@ Tabular::CellData::CellData(CellData const & cs)
        : cellno(cs.cellno),
          width(cs.width),
          multicolumn(cs.multicolumn),
+         multirow(cs.multirow),
          alignment(cs.alignment),
          valignment(cs.valignment),
          top_line(cs.top_line),
@@ -562,6 +566,7 @@ void Tabular::CellData::swap(CellData & rhs)
        std::swap(cellno, rhs.cellno);
        std::swap(width, rhs.width);
        std::swap(multicolumn, rhs.multicolumn);
+       std::swap(multirow, rhs.multirow);
        std::swap(alignment, rhs.alignment);
        std::swap(valignment, rhs.valignment);
        std::swap(top_line, rhs.top_line);
@@ -615,11 +620,10 @@ Tabular::Tabular(Buffer * buffer, row_type rows_arg, col_type columns_arg)
 void Tabular::setBuffer(Buffer & buffer)
 {
        buffer_ = &buffer;
-       size_t row_count = row_info.size();
-       size_t column_count = column_info.size();
-       // set silly default lines
-       for (row_type i = 0; i < row_count; ++i)
-               for (col_type j = 0; j < column_count; ++j)
+       row_type const nrows = row_info.size();
+       col_type const ncols = column_info.size();
+       for (row_type i = 0; i < nrows; ++i)
+               for (col_type j = 0; j < ncols; ++j)
                        cell_info[i][j].inset->setBuffer(*buffer_);
 }
 
@@ -657,30 +661,25 @@ void Tabular::init(Buffer * buf, row_type rows_arg,
 
 void Tabular::appendRow(idx_type const cell)
 {
-       BufferParams const & bp = buffer_->params();
        row_type const row = cellRow(cell);
 
-       row_vector::iterator rit = row_info.begin() + row;
-       row_info.insert(rit, RowData());
-       // now set the values of the row before
-       row_info[row] = row_info[row + 1];
+       row_info.insert(row_info.begin() + row + 1, RowData());
+       row_info[row + 1] = row_info[row];
 
-       row_type const nrows = row_info.size();
        col_type const ncols = column_info.size();
-
-       cell_vvector old(nrows - 1);
-       for (row_type i = 0; i < nrows - 1; ++i)
-               swap(cell_info[i], old[i]);
-
-       cell_info = cell_vvector(nrows, cell_vector(ncols, CellData(buffer_)));
-
-       for (row_type i = 0; i <= row; ++i)
-               swap(cell_info[i], old[i]);
-       for (row_type i = row + 2; i < nrows; ++i)
-               swap(cell_info[i], old[i - 1]);
-
+       cell_info.insert(cell_info.begin() + row + 1,
+               cell_vector(ncols, CellData(buffer_)));
+       for (col_type c = 0; c < ncols; ++c) {
+               if (cell_info[row][c].multirow == CELL_BEGIN_OF_MULTIROW)
+                       cell_info[row + 1][c].multirow = CELL_PART_OF_MULTIROW;
+               else
+                       cell_info[row + 1][c].multirow = cell_info[row][c].multirow;
+       }
        updateIndexes();
+
        for (col_type c = 0; c < ncols; ++c) {
+               if (isPartOfMultiRow(row, c))
+                       continue;
                // inherit line settings
                idx_type const i = cellIndex(row + 1, c);
                idx_type const j = cellIndex(row, c);
@@ -692,7 +691,7 @@ void Tabular::appendRow(idx_type const cell)
                        setBottomLine(j, false);
                }
                // mark track changes
-               if (bp.trackChanges)
+               if (buffer().params().trackChanges)
                        cellInfo(i).inset->setChange(Change(Change::INSERTED));
        }
 }
@@ -704,6 +703,15 @@ void Tabular::deleteRow(row_type const row)
        if (row_info.size() == 1)
                return;
 
+       size_t const column_count = column_info.size();
+       for (col_type i = 0; i < column_count; ++i) {
+               // Care about multirow cells
+               if (row + 1 < row_info.size() &&
+                   cell_info[row][i].multirow == CELL_BEGIN_OF_MULTIROW &&
+                   cell_info[row][i + 1].multirow == CELL_PART_OF_MULTIROW) {
+                               cell_info[row][i + 1].multirow = CELL_BEGIN_OF_MULTIROW;
+               }
+       }
        row_info.erase(row_info.begin() + row);
        cell_info.erase(cell_info.begin() + row);
        updateIndexes();
@@ -726,23 +734,18 @@ void Tabular::copyRow(row_type const row)
 void Tabular::appendColumn(idx_type const cell)
 {
        col_type const c = cellColumn(cell);
-       column_vector::iterator cit = column_info.begin() + c + 1;
-       column_info.insert(cit, ColumnData());
-       row_type const nrows = row_info.size();
-       // set the column values of the column before
+       
+       column_info.insert(column_info.begin() + c + 1, ColumnData());
        column_info[c + 1] = column_info[c];
 
+       row_type const nrows = row_info.size();
        for (row_type r = 0; r < nrows; ++r) {
                cell_info[r].insert(cell_info[r].begin() + c + 1, 
                        CellData(buffer_));
-#if 0
-// FIXME: This code does not work. It deletes the cell's content and
-// it triggers an assertion if the cursor is at pos > 0.
                if (cell_info[r][c].multicolumn == CELL_BEGIN_OF_MULTICOLUMN)
                        cell_info[r][c + 1].multicolumn = CELL_PART_OF_MULTICOLUMN;
                else
                        cell_info[r][c + 1].multicolumn = cell_info[r][c].multicolumn;
-#endif
        }
        updateIndexes();
        for (row_type r = 0; r < nrows; ++r) {
@@ -756,47 +759,44 @@ void Tabular::appendColumn(idx_type const cell)
                        setRightLine(i, true);
                        setRightLine(j, false);
                }
-               //
-               cellInfo(i).inset->clear();
                if (buffer().params().trackChanges)
                        cellInfo(i).inset->setChange(Change(Change::INSERTED));
        }
 }
 
 
-void Tabular::deleteColumn(col_type const column)
+void Tabular::deleteColumn(col_type const col)
 {
        // Not allowed to delete last column
        if (column_info.size() == 1)
                return;
 
-       size_t const row_count = row_info.size();
-       for (row_type i = 0; i < row_count; ++i) {
+       row_type const nrows = row_info.size();
+       for (row_type r = 0; r < nrows; ++r) {
                // Care about multicolumn cells
-               if (column + 1 < column_info.size() &&
-                   cell_info[i][column].multicolumn == CELL_BEGIN_OF_MULTICOLUMN &&
-                   cell_info[i][column + 1].multicolumn == CELL_PART_OF_MULTICOLUMN) {
-                       cell_info[i][column + 1].multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
+               if (col + 1 < column_info.size() &&
+                   cell_info[r][col].multicolumn == CELL_BEGIN_OF_MULTICOLUMN &&
+                   cell_info[r][col + 1].multicolumn == CELL_PART_OF_MULTICOLUMN) {
+                               cell_info[r][col + 1].multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
                }
-               cell_info[i].erase(cell_info[i].begin() + column);
+               cell_info[r].erase(cell_info[r].begin() + col);
        }
-       column_info.erase(column_info.begin() + column);
+       column_info.erase(column_info.begin() + col);
        updateIndexes();
 }
 
 
-void Tabular::copyColumn(col_type const column)
+void Tabular::copyColumn(col_type const col)
 {
        BufferParams const & bp = buffer().params();
-       column_info.insert(column_info.begin() + column, column_info[column]);
+       column_info.insert(column_info.begin() + col, column_info[col]);
 
-       size_t row_count = row_info.size();
-       for (row_type i = 0; i < row_count; ++i)
-               cell_info[i].insert(cell_info[i].begin() + column, cell_info[i][column]);
-
-       if (bp.trackChanges)
-               for (row_type i = 0; i < row_count; ++i)
-                       cell_info[i][column + 1].inset->setChange(Change(Change::INSERTED));
+       row_type nrows = row_info.size();
+       for (row_type r = 0; r < nrows; ++r) {
+               cell_info[r].insert(cell_info[r].begin() + col, cell_info[r][col]);
+               if (bp.trackChanges)
+                       cell_info[r][col + 1].inset->setChange(Change(Change::INSERTED));
+       }
        updateIndexes();
 }
 
@@ -809,9 +809,13 @@ void Tabular::updateIndexes()
        numberofcells = 0;
        for (row_type row = 0; row < nrows; ++row)
                for (col_type column = 0; column < ncols; ++column) {
-                       if (!isPartOfMultiColumn(row, column))
+                       if (!isPartOfMultiColumn(row, column)
+                               && !isPartOfMultiRow(row, column))
                                ++numberofcells;
-                       cell_info[row][column].cellno = numberofcells - 1;
+                       if (isPartOfMultiRow(row, column))
+                               cell_info[row][column].cellno = cell_info[row - 1][column].cellno;
+                       else
+                               cell_info[row][column].cellno = numberofcells - 1;
                }
 
        rowofcell.resize(numberofcells);
@@ -819,7 +823,8 @@ void Tabular::updateIndexes()
        idx_type i = 0;
        for (row_type row = 0; row < nrows; ++row)
                for (col_type column = 0; column < ncols; ++column) {
-                       if (isPartOfMultiColumn(row, column))
+                       if (isPartOfMultiColumn(row, column)
+                               || isPartOfMultiRow(row, column))
                                continue;
                        rowofcell[i] = row;
                        columnofcell[i] = column;
@@ -871,11 +876,11 @@ bool Tabular::rightLine(idx_type cell) const
 
 bool Tabular::topAlreadyDrawn(idx_type cell) const
 {
-       row_type row = cellRow(cell);
+       row_type const row = cellRow(cell);
        if (row == 0)
                return false;
-       idx_type i = cellIndex(row - 1, cellColumn(cell));
-       return !rowTopLine(row) && bottomLine(i);
+       
+       return !rowTopLine(row) && bottomLine(cellAbove(cell));
 }
 
 
@@ -895,7 +900,7 @@ bool Tabular::isLastRow(idx_type cell) const
 }
 
 
-int Tabular::getAdditionalHeight(row_type row) const
+int Tabular::interRowSpace(row_type row) const
 {
        if (!row || row >= row_info.size())
                return 0;
@@ -909,11 +914,11 @@ int Tabular::getAdditionalHeight(row_type row) const
 }
 
 
-int Tabular::getAdditionalWidth(idx_type cell) const
+int Tabular::interColumnSpace(idx_type cell) const
 {
        col_type const nextcol = cellColumn(cell) + columnSpan(cell);
-       if (rightLine(cell) 
-               && nextcol < column_info.size() && leftLine(cellIndex(cellRow(cell), nextcol)))
+       if (rightLine(cell) && nextcol < column_info.size()
+               && leftLine(cellIndex(cellRow(cell), nextcol)))
                return WIDTH_OF_LINE;
        return 0;
 }
@@ -930,6 +935,24 @@ int Tabular::columnWidth(idx_type cell) const
 }
 
 
+int Tabular::rowHeight(idx_type cell) const
+{
+       row_type const span = rowSpan(cell);
+       row_type const row = cellRow(cell);
+       int h = rowAscent(row) + rowDescent(row);
+
+       for(row_type r = row; r < row + span ; ++r) {
+               if (r > row) {
+                       h += rowAscent(r);
+                       h += interRowSpace(r);
+               }
+               if (r < row + span - 1)
+                       h += rowDescent(r);
+       }
+       return h;
+}
+
+
 bool Tabular::updateColumnWidths()
 {
        col_type const ncols = column_info.size();
@@ -984,7 +1007,7 @@ int Tabular::width() const
 void Tabular::setCellWidth(idx_type cell, int new_width)
 {
        cellInfo(cell).width = new_width + 2 * WIDTH_OF_LINE 
-               + getAdditionalWidth(cell);
+               + interColumnSpace(cell);
 }
 
 
@@ -1098,7 +1121,7 @@ bool Tabular::setMColumnPWidth(Cursor & cur, idx_type cell,
 void Tabular::setAlignSpecial(idx_type cell, docstring const & special,
                                 Tabular::Feature what)
 {
-       if (what == SET_SPECIAL_MULTI)
+       if (what == SET_SPECIAL_MULTICOLUMN)
                cellInfo(cell).align_special = special;
        else
                column_info[cellColumn(cell)].align_special = special;
@@ -1139,22 +1162,24 @@ void Tabular::setRightLine(idx_type cell, bool line)
 
 bool Tabular::rowTopLine(row_type r) const
 {
-       idx_type i0 = getFirstCellInRow(r);
-       idx_type i1 = getLastCellInRow(r);
+       col_type const ncols = column_info.size();
        bool all_rows_set = true;
-       for (idx_type j = i0; all_rows_set && j <= i1; ++j)
-               all_rows_set = cellInfo(j).top_line;
+       for (col_type c = 0; all_rows_set && c < ncols; ++c) {
+               idx_type const i = cellIndex(r, c);
+               all_rows_set = cellInfo(i).top_line;
+       }
        return all_rows_set;
 }
 
 
 bool Tabular::rowBottomLine(row_type r) const
 {
-       idx_type i0 = getFirstCellInRow(r);
-       idx_type i1 = getLastCellInRow(r);
+       col_type const ncols = column_info.size();
        bool all_rows_set = true;
-       for (idx_type j = i0; all_rows_set && j <= i1; ++j)
-               all_rows_set = cellInfo(j).bottom_line;
+       for (col_type c = 0; all_rows_set && c < ncols; ++c) {
+               idx_type const i = cellIndex(r, c);
+               all_rows_set = cellInfo(i).bottom_line;
+       }
        return all_rows_set;
 }
 
@@ -1234,9 +1259,10 @@ int Tabular::cellWidth(idx_type cell) const
 }
 
 
-int Tabular::getBeginningOfTextInCell(idx_type cell) const
+int Tabular::textHOffset(idx_type cell) const
 {
-       int x = 0;
+       // the LaTeX Way :-(
+       int x = WIDTH_OF_LINE;
 
        switch (getAlignment(cell)) {
        case LYX_ALIGN_CENTER:
@@ -1244,22 +1270,34 @@ int Tabular::getBeginningOfTextInCell(idx_type cell) const
                break;
        case LYX_ALIGN_RIGHT:
                x += columnWidth(cell) - cellWidth(cell);
-               // + getAdditionalWidth(cell);
+               // + interColumnSpace(cell);
                break;
        default:
                // LYX_ALIGN_LEFT: nothing :-)
                break;
        }
 
-       // the LaTeX Way :-(
-       x += WIDTH_OF_LINE;
        return x;
 }
 
 
-bool Tabular::isFirstCellInRow(idx_type cell) const
+int Tabular::textVOffset(idx_type cell) const
 {
-       return cellColumn(cell) == 0;
+       int h = rowHeight(cell);
+       
+       int y = 0;
+       switch (getVAlignment(cell)) {
+          case LYX_VALIGN_TOP:
+                  break;
+          case LYX_VALIGN_MIDDLE:
+                  y += h/2;
+                  break;
+          case LYX_VALIGN_BOTTOM:
+                  y += h;
+                  break;
+       }
+       
+       return y;
 }
 
 
@@ -1267,6 +1305,11 @@ Tabular::idx_type Tabular::getFirstCellInRow(row_type row) const
 {
        if (row > row_info.size() - 1)
                row = row_info.size() - 1;
+
+       col_type c = 0;
+       while (cell_info[row][c].multirow == CELL_PART_OF_MULTIROW)
+               ++c;
+
        return cell_info[row][0].cellno;
 }
 
@@ -1379,6 +1422,7 @@ void Tabular::write(ostream & os) const
                for (col_type j = 0; j < column_info.size(); ++j) {
                        os << "<cell"
                           << write_attribute("multicolumn", cell_info[i][j].multicolumn)
+                          << write_attribute("multirow", cell_info[i][j].multirow)
                           << write_attribute("alignment", cell_info[i][j].alignment)
                           << write_attribute("valignment", cell_info[i][j].valignment)
                           << write_attribute("topline", cell_info[i][j].top_line)
@@ -1486,6 +1530,7 @@ void Tabular::read(Lexer & lex)
                                return;
                        }
                        getTokenValue(line, "multicolumn", cell_info[i][j].multicolumn);
+                       getTokenValue(line, "multirow", cell_info[i][j].multirow);
                        getTokenValue(line, "alignment", cell_info[i][j].alignment);
                        getTokenValue(line, "valignment", cell_info[i][j].valignment);
                        getTokenValue(line, "topline", cell_info[i][j].top_line);
@@ -1525,14 +1570,19 @@ void Tabular::read(Lexer & lex)
 
 bool Tabular::isMultiColumn(idx_type cell) const
 {
-       return cellInfo(cell).multicolumn != CELL_NORMAL;
+       if (cellInfo(cell).multicolumn == CELL_BEGIN_OF_MULTICOLUMN
+               || cellInfo(cell).multicolumn == CELL_PART_OF_MULTICOLUMN)
+               return true;
+       else
+               return false;
 }
 
 
 bool Tabular::isMultiColumnReal(idx_type cell) const
 {
        return cellColumn(cell) != cellRightColumn(cell) &&
-                       cellInfo(cell).multicolumn != CELL_NORMAL;
+               (cellInfo(cell).multicolumn == CELL_BEGIN_OF_MULTICOLUMN
+                || cellInfo(cell).multicolumn == CELL_PART_OF_MULTICOLUMN);
 }
 
 
@@ -1558,40 +1608,80 @@ void Tabular::setMultiColumn(idx_type cell, idx_type number)
 }
 
 
+bool Tabular::isMultiRow(idx_type cell) const
+{
+       return (cellInfo(cell).multirow == CELL_BEGIN_OF_MULTIROW
+               || cellInfo(cell).multirow == CELL_PART_OF_MULTIROW);
+}
+
+
+void Tabular::setMultiRow(idx_type cell, idx_type number)
+{
+       idx_type const column = cellColumn(cell);
+       idx_type row = cellRow(cell);
+       idx_type const ncolumns = column_info.size();
+       CellData & cs = cellInfo(cell);
+       cs.multirow = CELL_BEGIN_OF_MULTIROW;
+       // FIXME: the horizontal alignment can only be changed for
+       // the whole table, support for this needs to be implemented
+       // assigning this to uwestoehr
+       cs.valignment = LYX_VALIGN_MIDDLE;
+       // set the bottom row of the last selected cell
+       setBottomLine(cell, bottomLine(cell + (number - 1)*ncolumns));
+       for (idx_type i = 1; i < number; ++i) {
+               CellData & cs1 = cell_info[row + i][column];
+               cs1.multirow = CELL_PART_OF_MULTIROW;
+               cs.inset->appendParagraphs(cs1.inset->paragraphs());
+               cs1.inset->clear();
+       }
+       updateIndexes();
+}
+
+
 Tabular::idx_type Tabular::columnSpan(idx_type cell) const
 {
        row_type const row = cellRow(cell);
        col_type const ncols = column_info.size();
-       idx_type result = 1;
        col_type column = cellColumn(cell) + 1;
-       while (column < ncols && isPartOfMultiColumn(row, column)) {
-               ++result;
+       while (column < ncols && isPartOfMultiColumn(row, column))
                ++column;
-       }
-       return result;
+
+       return column - cellColumn(cell);
+}
+
+
+Tabular::idx_type Tabular::rowSpan(idx_type cell) const
+{
+       row_type const nrows = row_info.size();
+       col_type const column = cellColumn(cell);
+       idx_type result = 1;
+       col_type row = cellRow(cell) + 1;
+       while (row < nrows && isPartOfMultiRow(row, column))
+               ++row;
+       
+       return row - cellRow(cell);
 }
 
 
-Tabular::idx_type Tabular::unsetMultiColumn(idx_type cell)
+void Tabular::unsetMultiColumn(idx_type cell)
 {
        row_type const row = cellRow(cell);
-       col_type column = cellColumn(cell);
+       col_type const column = cellColumn(cell);
+       row_type const span = columnSpan(cell);
+       for (col_type c = 0; c < span; ++c)
+               cell_info[row][column + c].multicolumn = CELL_NORMAL;
+       updateIndexes();
+}
 
-       idx_type result = 0;
 
-       if (cell_info[row][column].multicolumn == CELL_BEGIN_OF_MULTICOLUMN) {
-               cell_info[row][column].multicolumn = CELL_NORMAL;
-               ++column;
-               while (column < column_info.size() &&
-                          cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN)
-               {
-                       cell_info[row][column].multicolumn = CELL_NORMAL;
-                       ++column;
-                       ++result;
-               }
-       }
+void Tabular::unsetMultiRow(idx_type cell)
+{
+       row_type const row = cellRow(cell);
+       col_type const column = cellColumn(cell);
+       row_type const span = rowSpan(cell);
+       for (row_type r = 0; r < span; ++r)
+               cell_info[row + r][column].multirow = CELL_NORMAL;
        updateIndexes();
-       return result;
 }
 
 
@@ -1629,25 +1719,31 @@ bool Tabular::isLastCell(idx_type cell) const
 
 Tabular::idx_type Tabular::cellAbove(idx_type cell) const
 {
-       if (cellRow(cell) > 0)
-               return cell_info[cellRow(cell)-1][cellColumn(cell)].cellno;
-       return cell;
+       if (cellRow(cell) == 0)
+               return cell;
+       
+       col_type const col = cellColumn(cell);
+       row_type r = cellRow(cell) - 1;
+       while (r > 0 && cell_info[r][col].multirow == CELL_PART_OF_MULTIROW)
+               --r;
+
+       return cell_info[r][col].cellno;
 }
 
 
 Tabular::idx_type Tabular::cellBelow(idx_type cell) const
 {
-       if (cellRow(cell) + 1 < row_info.size())
-               return cell_info[cellRow(cell)+1][cellColumn(cell)].cellno;
+       row_type const nextrow = cellRow(cell) + rowSpan(cell);
+       if (nextrow < row_info.size())
+               return cell_info[nextrow][cellColumn(cell)].cellno;
        return cell;
 }
 
 
-Tabular::idx_type Tabular::cellIndex(row_type row,
-                                              col_type column) const
+Tabular::idx_type Tabular::cellIndex(row_type row, col_type column) const
 {
-       BOOST_ASSERT(column != npos && column < column_info.size()
-               && row != npos && row < row_info.size());
+       LASSERT(column != npos && column < column_info.size()
+               && row != npos && row < row_info.size(), /**/);
        return cell_info[row][column].cellno;
 }
 
@@ -1842,8 +1938,7 @@ void Tabular::setRowDescent(row_type row, int height)
 
 int Tabular::rowAscent(row_type row) const
 {
-       if (row >= row_info.size())
-               return 0;
+       LASSERT(row < row_info.size(), /**/);
        return row_info[row].ascent;
 }
 
@@ -1860,7 +1955,7 @@ int Tabular::height() const
        int height = 0;
        for (row_type row = 0; row < row_info.size(); ++row)
                height += rowAscent(row) + rowDescent(row) +
-                       getAdditionalHeight(row);
+                       interRowSpace(row);
        return height;
 }
 
@@ -1873,6 +1968,14 @@ bool Tabular::isPartOfMultiColumn(row_type row, col_type column) const
 }
 
 
+bool Tabular::isPartOfMultiRow(row_type row, col_type column) const
+{
+       LASSERT(row < row_info.size(), /**/);
+       LASSERT(column < column_info.size(), /**/);
+       return cell_info[row][column].multirow == CELL_PART_OF_MULTIROW;
+}
+
+
 int Tabular::TeXTopHLine(odocstream & os, row_type row, string const lang) const
 {
        // we only output complete row lines and the 1st row here, the rest
@@ -1884,6 +1987,12 @@ int Tabular::TeXTopHLine(odocstream & os, row_type row, string const lang) const
        col_type nset = 0;
        for (col_type c = 0; c < ncols; ++c) {
                topline.push_back(topLine(cellIndex(row, c)));
+               // If cell is part of a multirow and not the first cell of the
+               // multirow, no line must be drawn.
+               if (row != 0)
+                       if (isMultiRow(cellIndex(row, c))
+                               && isMultiRow(cellIndex(row - 1, c)))
+                                       topline[c] = false;
                if (topline[c])
                        ++nset;
        }
@@ -1935,6 +2044,14 @@ int Tabular::TeXBottomHLine(odocstream & os, row_type row, string const lang) co
        for (col_type c = 0; c < ncols; ++c) {
                bottomline.push_back(bottomLine(cellIndex(row, c)));
                topline.push_back(!lastrow && topLine(cellIndex(row + 1, c)));
+               // If cell is part of a multirow and not the last or first cell of the
+               // multirow, no line must be drawn.
+               if (!lastrow)
+                       if (isMultiRow(cellIndex(row, c))
+                               && isMultiRow(cellIndex(row + 1, c))) {
+                                       bottomline[c] = false;
+                                       topline[c] = false;
+                               }
                nextrowset &= topline[c];
        }
 
@@ -1955,7 +2072,7 @@ int Tabular::TeXBottomHLine(odocstream & os, row_type row, string const lang) co
                if (use_booktabs)
                        os << (lastrow ? "\\bottomrule" : "\\midrule");
                else
-                       os << "\\hline";
+                       os << "\\hline ";
        } else {
                for (col_type c = 0; c < ncols; ++c) {
                        if (bottomline[c]) {
@@ -1979,7 +2096,8 @@ int Tabular::TeXBottomHLine(odocstream & os, row_type row, string const lang) co
 }
 
 
-int Tabular::TeXCellPreamble(odocstream & os, idx_type cell, bool & ismulticol) const
+int Tabular::TeXCellPreamble(odocstream & os, idx_type cell,
+                                                        bool & ismulticol, bool & ismultirow) const
 {
        int ret = 0;
        row_type const r = cellRow(cell);
@@ -1999,7 +2117,8 @@ int Tabular::TeXCellPreamble(odocstream & os, idx_type cell, bool & ismulticol)
                && leftLine(cellIndex(r, nextcol));
        bool coldouble = colright && nextcolleft;
        bool celldouble = rightLine(cell) && nextcellleft;
-       ismulticol = isMultiColumn(cell) 
+
+       ismulticol = isMultiColumn(cell)
                || (c == 0 && colleft != leftLine(cell))
                || ((colright || nextcolleft) && !rightLine(cell) && !nextcellleft)
                || (!colright && !nextcolleft && (rightLine(cell) || nextcellleft))
@@ -2059,7 +2178,21 @@ int Tabular::TeXCellPreamble(odocstream & os, idx_type cell, bool & ismulticol)
                        // add extra vertical line if we want a double one
                        os << '|';
                os << "}{";
-               }
+               } // end if ismulticol
+
+       // we only need code for the first multirow cell
+       ismultirow = isMultiRow(cell);
+       if (ismultirow) {
+               os << "\\multirow{" << rowSpan(cell) << "}{";
+               if (!getPWidth(cell).zero())
+                       os << from_ascii(getPWidth(cell).asLatexString());
+               else
+                       // we need to set a default value
+                       // needs to be discussed
+                       os << "2.0cm";
+               os << "}{";
+               } // end if ismultirow
+
        if (getRotateCell(cell)) {
                os << "\\begin{sideways}\n";
                ++ret;
@@ -2100,7 +2233,8 @@ int Tabular::TeXCellPreamble(odocstream & os, idx_type cell, bool & ismulticol)
 }
 
 
-int Tabular::TeXCellPostamble(odocstream & os, idx_type cell, bool ismulticol) const
+int Tabular::TeXCellPostamble(odocstream & os, idx_type cell,
+                                                         bool ismulticol, bool ismultirow) const
 {
        int ret = 0;
        row_type const r = cellRow(cell);
@@ -2118,7 +2252,7 @@ int Tabular::TeXCellPostamble(odocstream & os, idx_type cell, bool ismulticol) c
                os << "%\n\\end{sideways}";
                ++ret;
        }
-       if (ismulticol) {
+       if (ismulticol || ismultirow) {
                os << '}';
        }
        return ret;
@@ -2267,10 +2401,14 @@ int Tabular::TeXRow(odocstream & os, row_type i,
                ++ret;
        }
        bool ismulticol = false;
+       bool ismultirow = false;
        for (col_type j = 0; j < column_info.size(); ++j) {
-               if (isPartOfMultiColumn(i, j))
+               if (isPartOfMultiRow(i, j))
+                       os << " & "; // we need to add a further column
+               if (isPartOfMultiColumn(i, j) || isPartOfMultiRow(i, j))
                        continue;
-               ret += TeXCellPreamble(os, cell, ismulticol);
+               cell = cellIndex(i, j);
+               ret += TeXCellPreamble(os, cell, ismulticol, ismultirow);
                shared_ptr<InsetTableCell> inset = cellInset(cell);
 
                Paragraph const & par = inset->paragraphs().front();
@@ -2302,11 +2440,10 @@ int Tabular::TeXRow(odocstream & os, row_type i,
                if (rtl)
                        os << '}';
 
-               ret += TeXCellPostamble(os, cell, ismulticol);
+               ret += TeXCellPostamble(os, cell, ismulticol, ismultirow);
                if (!isLastCellInRow(cell)) { // not last cell in row
                        os << " & ";
                }
-               ++cell;
        }
        if (row_info[i].caption && !endfirsthead.empty && !haveLTFirstHead())
                // if no first header and no empty first header is used,
@@ -2941,6 +3078,8 @@ void Tabular::validate(LaTeXFeatures & features) const
        if (needRotating())
                features.require("rotating");
        for (idx_type cell = 0; cell < numberofcells; ++cell) {
+               if (isMultiRow(cell))
+                       features.require("multirow");
                if (getVAlignment(cell) != LYX_VALIGN_TOP
                    || !getPWidth(cell).zero())
                        features.require("array");
@@ -3117,11 +3256,10 @@ int InsetTabular::rowFromY(Cursor & cur, int y) const
        int h = yo(cur.bv()) - tabular.rowAscent(0);
        size_t nrows = tabular.row_info.size();
        row_type r = 0;
-       for (; r < nrows && y > h; ++r) {
-               h += tabular.rowAscent(r);
-               h += tabular.rowDescent(r);
-               h += tabular.getAdditionalHeight(r);
-       }
+       for (; r < nrows && y > h; ++r)
+               h += tabular.rowAscent(r) + tabular.rowDescent(r)
+               + tabular.interRowSpace(r);
+
        return r - 1;
 }
 
@@ -3147,14 +3285,15 @@ void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const
                LASSERT(false, /**/);
        }
 
-       row_type i = 0;
-       for (idx_type cell = 0; i < tabular.row_info.size(); ++i) {
+       for (row_type i = 0; i < tabular.row_info.size(); ++i) {
                int maxAsc = 0;
                int maxDesc = 0;
                for (col_type j = 0; j < tabular.column_info.size(); ++j) {
-                       if (tabular.isPartOfMultiColumn(i, j))
-                               // Multicolumn cell, but not first one
+                       if (tabular.isPartOfMultiColumn(i, j)
+                               || tabular.isPartOfMultiRow(i, j))
+                               // multicolumn or multirow cell, but not first one
                                continue;
+                       idx_type const cell = tabular.cellIndex(i, j);
                        Dimension dim;
                        MetricsInfo m = mi;
                        Length p_width;
@@ -3171,7 +3310,6 @@ void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const
                        tabular.setCellWidth(cell, dim.wid);
                        maxAsc  = max(maxAsc, dim.asc);
                        maxDesc = max(maxDesc, dim.des);
-                       ++cell;
                }
                int const top_space = tabular.row_info[i].top_space_default ?
                        default_line_space :
@@ -3217,59 +3355,50 @@ bool InsetTabular::isCellSelected(Cursor & cur, row_type row, col_type col)
 
 void InsetTabular::draw(PainterInfo & pi, int x, int y) const
 {
-       //lyxerr << "InsetTabular::draw: " << x << " " << y << endl;
+       x += scx_ + ADD_TO_TABULAR_WIDTH;
+
        BufferView * bv = pi.base.bv;
        Cursor & cur = pi.base.bv->cursor();
+       resetPos(cur);
 
        // FIXME: As the full background is painted in drawSelection(),
        // we have no choice but to do a full repaint for the Text cells.
        pi.full_repaint = true;
 
-       resetPos(bv->cursor());
-
-       x += scx_;
-       x += ADD_TO_TABULAR_WIDTH;
-
-       bool const original_drawing_state = pi.pain.isDrawingEnabled();
        bool const original_selection_state = pi.selected;
 
        idx_type idx = 0;
        first_visible_cell = Tabular::npos;
        for (row_type i = 0; i < tabular.row_info.size(); ++i) {
                int nx = x;
-               int const a = tabular.rowAscent(i);
-               int const d = tabular.rowDescent(i);
-               idx = tabular.cellIndex(i, 0);
                for (col_type j = 0; j < tabular.column_info.size(); ++j) {
                        if (tabular.isPartOfMultiColumn(i, j))
                                continue;
+                       
+                       idx = tabular.cellIndex(i, j);
+                       
+                       if (tabular.isPartOfMultiRow(i, j)) {
+                               nx += tabular.columnWidth(idx);
+                               continue;
+                       }
+
                        if (first_visible_cell == Tabular::npos)
                                first_visible_cell = idx;
 
                        pi.selected |= isCellSelected(cur, i, j);
-                       int const cx = nx + tabular.getBeginningOfTextInCell(idx);
+                       int const cx = nx + tabular.textHOffset(idx);
+                       int const cy = y  + tabular.textVOffset(idx);
                        // Cache the Inset position.
                        bv->coordCache().insets().add(cell(idx).get(), cx, y);
-                       if (nx + tabular.columnWidth(idx) < 0
-                           || nx > bv->workWidth()
-                           || y + d < 0
-                           || y - a > bv->workHeight()) {
-                               pi.pain.setDrawingEnabled(false);
-                               cell(idx)->draw(pi, cx, y);
-                               drawCellLines(pi.pain, nx, y, i, idx, pi.change_);
-                               pi.pain.setDrawingEnabled(original_drawing_state);
-                       } else {
-                               cell(idx)->draw(pi, cx, y);
-                               drawCellLines(pi.pain, nx, y, i, idx, pi.change_);
-                       }
+                       cell(idx)->draw(pi, cx, cy);
+                       drawCellLines(pi.pain, nx, y, i, idx, pi.change_);
                        nx += tabular.columnWidth(idx);
-                       ++idx;
                        pi.selected = original_selection_state;
                }
 
                if (i + 1 < tabular.row_info.size())
-                       y += d + tabular.rowAscent(i + 1) +
-                               tabular.getAdditionalHeight(i + 1);
+                       y += tabular.rowDescent(i) + tabular.rowAscent(i + 1)
+                            + tabular.interRowSpace(i + 1);
        }
 }
 
@@ -3302,27 +3431,32 @@ void InsetTabular::drawSelection(PainterInfo & pi, int x, int y) const
 
        if (cur.selIsMultiCell() || full_cell_selected) {
                y -= tabular.rowAscent(0);
-               for (row_type j = 0; j < tabular.row_info.size(); ++j) {
-                       int const a = tabular.rowAscent(j);
-                       int const h = a + tabular.rowDescent(j);
+               for (row_type r = 0; r < tabular.row_info.size(); ++r) {
                        int xx = x;
-                       y += tabular.getAdditionalHeight(j);
-                       for (col_type i = 0; i < tabular.column_info.size(); ++i) {
-                               if (tabular.isPartOfMultiColumn(j, i))
+                       for (col_type c = 0; c < tabular.column_info.size(); ++c) {
+                               if (tabular.isPartOfMultiColumn(r, c))
+                                       continue;
+
+                               idx_type const cell = tabular.cellIndex(r, c);
+
+                               if (tabular.isPartOfMultiRow(r, c)) {
+                                       xx += tabular.columnWidth(cell);
                                        continue;
-                               idx_type const cell =
-                                       tabular.cellIndex(j, i);
+                               }
                                int const w = tabular.columnWidth(cell);
-                               if (isCellSelected(cur, j, i))
+                               int const h = tabular.rowHeight(cell);
+                               if (isCellSelected(cur, r, c))
                                        pi.pain.fillRectangle(xx, y, w, h, Color_selection);
                                xx += w;
                        }
-                       y += h;
+                       if (r + 1 < tabular.row_info.size())
+                               y += tabular.rowDescent(r) + tabular.rowAscent(r + 1)
+                                    + tabular.interRowSpace(r + 1);
                }
 
        } else {
                x += cellXPos(cur.idx());
-               x += tabular.getBeginningOfTextInCell(cur.idx());
+               x += tabular.textHOffset(cur.idx());
                cell(cur.idx())->drawSelection(pi, x, 0 /* ignored */);
        }
 }
@@ -3331,7 +3465,9 @@ void InsetTabular::drawSelection(PainterInfo & pi, int x, int y) const
 void InsetTabular::drawCellLines(Painter & pain, int x, int y,
                                 row_type row, idx_type cell, Change const & change) const
 {
-       int x2 = x + tabular.columnWidth(cell);
+       y = y - tabular.rowAscent(row);
+       int const w = tabular.columnWidth(cell);
+       int const h = tabular.rowHeight(cell);
        bool on_off = false;
        Color col = Color_tabularline;
        Color onoffcol = Color_tabularonoffline;
@@ -3343,30 +3479,25 @@ void InsetTabular::drawCellLines(Painter & pain, int x, int y,
 
        if (!tabular.topAlreadyDrawn(cell)) {
                on_off = !tabular.topLine(cell);
-               pain.line(x, y - tabular.rowAscent(row),
-                         x2, y -  tabular.rowAscent(row),
-                         on_off ? onoffcol : col,
-                         on_off ? Painter::line_onoffdash : Painter::line_solid);
+               pain.line(x, y, x + w, y,
+                       on_off ? onoffcol : col,
+                       on_off ? Painter::line_onoffdash : Painter::line_solid);
        }
        on_off = !tabular.bottomLine(cell);
-       pain.line(x, y + tabular.rowDescent(row),
-                 x2, y + tabular.rowDescent(row),
-                 on_off ? onoffcol : col,
-                 on_off ? Painter::line_onoffdash : Painter::line_solid);
+       pain.line(x, y + h, x + w, y + h,
+               on_off ? onoffcol : col,
+               on_off ? Painter::line_onoffdash : Painter::line_solid);
        if (!tabular.leftAlreadyDrawn(cell)) {
                on_off = !tabular.leftLine(cell);
-               pain.line(x, y -  tabular.rowAscent(row),
-                         x, y +  tabular.rowDescent(row),
-                         on_off ? onoffcol : col,
-                         on_off ? Painter::line_onoffdash : Painter::line_solid);
+               pain.line(x, y, x, y + h,
+                       on_off ? onoffcol : col,
+                       on_off ? Painter::line_onoffdash : Painter::line_solid);
        }
        on_off = !tabular.rightLine(cell);
-       pain.line(x2 - tabular.getAdditionalWidth(cell),
-                 y -  tabular.rowAscent(row),
-                 x2 - tabular.getAdditionalWidth(cell),
-                 y +  tabular.rowDescent(row),
-                 on_off ? onoffcol : col,
-                 on_off ? Painter::line_onoffdash : Painter::line_solid);
+       pain.line(x + w - tabular.interColumnSpace(cell), y,
+               x + w - tabular.interColumnSpace(cell), y + h,
+               on_off ? onoffcol : col,
+               on_off ? Painter::line_onoffdash : Painter::line_solid);
 }
 
 
@@ -3944,7 +4075,8 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd,
                case Tabular::SET_PWIDTH:
                case Tabular::SET_MPWIDTH:
                case Tabular::SET_SPECIAL_COLUMN:
-               case Tabular::SET_SPECIAL_MULTI:
+               case Tabular::SET_SPECIAL_MULTICOLUMN:
+               case Tabular::SET_SPECIAL_MULTIROW:
                case Tabular::APPEND_ROW:
                case Tabular::APPEND_COLUMN:
                case Tabular::DELETE_ROW:
@@ -3965,6 +4097,14 @@ bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd,
                        status.setOnOff(tabular.isMultiColumn(cur.idx()));
                        break;
 
+               case Tabular::MULTIROW:
+                       // If a row is set as longtable caption, it must not be allowed
+                       // to unset that this row is a multirow.
+                       status.setEnabled(sel_col_start == sel_col_end
+                               && !tabular.ltCaption(tabular.cellRow(cur.idx())));
+                       status.setOnOff(tabular.isMultiRow(cur.idx()));
+                       break;
+
                case Tabular::SET_ALL_LINES:
                case Tabular::UNSET_ALL_LINES:
                case Tabular::SET_BORDER_LINES:
@@ -4353,27 +4493,27 @@ void InsetTabular::cursorPos(BufferView const & bv,
 {
        cell(sl.idx())->cursorPos(bv, sl, boundary, x, y);
 
-       // y offset     correction
        int const row = tabular.cellRow(sl.idx());
+       int const col = tabular.cellColumn(sl.idx());
+
+       // y offset     correction
        for (int i = 0; i <= row; ++i) {
+               if (tabular.isPartOfMultiRow(i, col))
+                       continue;
                if (i != 0) {
                        y += tabular.rowAscent(i);
-                       y += tabular.getAdditionalHeight(i);
+                       y += tabular.interRowSpace(i);
                }
                if (i != row)
                        y += tabular.rowDescent(i);
        }
+       y += tabular.textVOffset(sl.idx());
 
        // x offset correction
-       int const col = tabular.cellColumn(sl.idx());
-       int idx = tabular.cellIndex(row, 0);
-       for (int j = 0; j < col; ++j) {
-               if (tabular.isPartOfMultiColumn(row, j))
-                       continue;
-               x += tabular.columnWidth(idx);
-               ++idx;
-       }
-       x += tabular.getBeginningOfTextInCell(idx);
+       for (int c = 0; c < col; ++c)
+               x += tabular.column_info[c].width;
+       
+       x += tabular.textHOffset(sl.idx());
        x += ADD_TO_TABULAR_WIDTH;
        x += scx_;
 }
@@ -4385,11 +4525,11 @@ int InsetTabular::dist(BufferView & bv, idx_type const cell, int x, int y) const
        int yy = 0;
        Inset const & inset = *tabular.cellInset(cell);
        Point o = bv.coordCache().getInsets().xy(&inset);
-       int const xbeg = o.x_ - tabular.getBeginningOfTextInCell(cell);
+       int const xbeg = o.x_ - tabular.textHOffset(cell);
        int const xend = xbeg + tabular.columnWidth(cell);
        row_type const row = tabular.cellRow(cell);
-       int const ybeg = o.y_ - tabular.rowAscent(row) -
-                        tabular.getAdditionalHeight(row);
+       int const ybeg = o.y_ - tabular.rowAscent(row)
+               - tabular.interRowSpace(row);
        int const yend = o.y_ + tabular.rowDescent(row);
 
        if (x < xbeg)
@@ -4447,12 +4587,9 @@ InsetTabular::idx_type InsetTabular::getNearestCell(BufferView & bv, int x, int
 
 int InsetTabular::cellXPos(idx_type const cell) const
 {
-       idx_type c = cell;
-
-       for (; !tabular.isFirstCellInRow(c); --c)
-               ;
+       col_type col = tabular.cellColumn(cell);
        int lx = 0;
-       for (; c < cell; ++c)
+       for (col_type c = 0; c < col; ++c)
                lx += tabular.columnWidth(c);
 
        return lx;
@@ -4491,21 +4628,29 @@ void InsetTabular::resetPos(Cursor & cur) const
 
 void InsetTabular::moveNextCell(Cursor & cur, EntryDirection entry_from)
 {
+       row_type const row = tabular.cellRow(cur.idx());
+       col_type const col = tabular.cellColumn(cur.idx());
+
        if (isRightToLeft(cur)) {
-               if (tabular.isFirstCellInRow(cur.idx())) {
-                       row_type const row = tabular.cellRow(cur.idx());
+               if (tabular.cellColumn(cur.idx()) == 0) {
                        if (row == tabular.row_info.size() - 1)
                                return;
                        cur.idx() = tabular.cellBelow(tabular.getLastCellInRow(row));
                } else {
                        if (cur.idx() == 0)
                                return;
-                       --cur.idx();
+                       if (col == 0)
+                               cur.idx() = tabular.getLastCellInRow(row - 1);
+                       else
+                               cur.idx() = tabular.cellIndex(row, col - 1);
                }
        } else {
                if (tabular.isLastCell(cur.idx()))
                        return;
-               ++cur.idx();
+               if (tabular.isLastCellInRow(cur.idx()))
+                       cur.idx() = tabular.cellIndex(row + 1, 0);
+               else
+                       cur.idx() = tabular.cellIndex(row, col + 1);
        }
 
        cur.boundary(false);
@@ -4542,9 +4687,11 @@ void InsetTabular::moveNextCell(Cursor & cur, EntryDirection entry_from)
 
 void InsetTabular::movePrevCell(Cursor & cur, EntryDirection entry_from)
 {
+       row_type const row = tabular.cellRow(cur.idx());
+       col_type const col = tabular.cellColumn(cur.idx());
+
        if (isRightToLeft(cur)) {
                if (tabular.isLastCellInRow(cur.idx())) {
-                       row_type const row = tabular.cellRow(cur.idx());
                        if (row == 0)
                                return;
                        cur.idx() = tabular.getFirstCellInRow(row);
@@ -4552,12 +4699,18 @@ void InsetTabular::movePrevCell(Cursor & cur, EntryDirection entry_from)
                } else {
                        if (tabular.isLastCell(cur.idx()))
                                return;
-                       ++cur.idx();
+                       if (tabular.isLastCellInRow(cur.idx()))
+                               cur.idx() = tabular.cellIndex(row + 1, 0);
+                       else
+                               cur.idx() = tabular.cellIndex(row, col + 1);
                }
        } else {
                if (cur.idx() == 0) // first cell
                        return;
-               --cur.idx();
+               if (col == 0)
+                       cur.idx() = tabular.getLastCellInRow(row - 1);
+               else
+                       cur.idx() = tabular.cellIndex(row, col - 1);
        }
 
        if (cur.selIsMultiCell()) {
@@ -4723,7 +4876,7 @@ void InsetTabular::tabularFeatures(Cursor & cur,
                break;
 
        case Tabular::SET_SPECIAL_COLUMN:
-       case Tabular::SET_SPECIAL_MULTI:
+       case Tabular::SET_SPECIAL_MULTICOLUMN:
                tabular.setAlignSpecial(cur.idx(), from_utf8(value), feature);
                break;
 
@@ -4839,8 +4992,35 @@ void InsetTabular::tabularFeatures(Cursor & cur,
                // we have a selection so this means we just add all this
                // cells to form a multicolumn cell
                idx_type const s_start = cur.selBegin().idx();
+               row_type const row_start = tabular.cellRow(s_start);
+               row_type const row_end = tabular.cellRow(cur.selEnd().idx());
+               tabular.setMultiRow(s_start, row_end - row_start + 1);
+               cur.idx() = s_start;
+               cur.pit() = 0;
+               cur.pos() = 0;
+               cur.setSelection(false);
+               break;
+       }
+       
+       case Tabular::MULTIROW: {
+               if (!cur.selection()) {
+                       // just multirow for one single cell
+                       // check whether we are completely in a multirow
+                       if (tabular.isMultiRow(cur.idx()))
+                               tabular.unsetMultiRow(cur.idx());
+                       else
+                               tabular.setMultiRow(cur.idx(), 1);
+                       break;
+               }
+               // we have a selection so this means we just add all this
+               // cells to form a multirow cell
+               idx_type const s_start = cur.selBegin().idx();
                idx_type const s_end = cur.selEnd().idx();
-               tabular.setMultiColumn(s_start, s_end - s_start + 1);
+               // the cell index is counted from left to right, we therefore
+               // need to know the number of columns of the table to calculate
+               // the number of selected rows
+               idx_type const ncolumns = tabular.column_info.size();
+               tabular.setMultiRow(s_start, (s_end - s_start)/ncolumns + 1);
                cur.idx() = s_start;
                cur.pit() = 0;
                cur.pos() = 0;
@@ -5173,6 +5353,9 @@ bool InsetTabular::pasteClipboard(Cursor & cur)
                        shared_ptr<InsetTableCell> inset(
                                new InsetTableCell(*paste_tabular->cellInset(r1, c1)));
                        tabular.setCellInset(r2, c2, inset);
+                       // 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().trackChanges ?
                                                Change::INSERTED : Change::UNCHANGED));
index 47b9aa4c60fa090078a0e25634b9063fcb3dff50..a3c8de7ccdb79a69b309abbabc2f39376bf353da 100644 (file)
@@ -12,7 +12,6 @@
  * Full author contact details are available in file CREDITS.
  */
 
-
 // This is Juergen's rewrite of the tabular (table) support.
 
 // Things to think of when designing the new tabular support:
 // - multirow
 // - column styles
 
-// This is what I have written about tabular support in the LyX3-Tasks file:
-//
-//  o rewrite of table code. Should probably be written as some
-//    kind of an inset. [Done]
-// o enhance longtable support
-
-// Lgb
-
 #ifndef INSET_TABULAR_H
 #define INSET_TABULAR_H
 
@@ -120,6 +111,8 @@ public:
                ///
                MULTICOLUMN,
                ///
+               MULTIROW,
+               ///
                SET_ALL_LINES,
                ///
                UNSET_ALL_LINES,
@@ -164,7 +157,9 @@ public:
                ///
                SET_SPECIAL_COLUMN,
                ///
-               SET_SPECIAL_MULTI,
+               SET_SPECIAL_MULTICOLUMN,
+               ///
+               SET_SPECIAL_MULTIROW,
                ///
                SET_BOOKTABS,
                ///
@@ -199,7 +194,11 @@ public:
                ///
                CELL_BEGIN_OF_MULTICOLUMN,
                ///
-               CELL_PART_OF_MULTICOLUMN
+               CELL_PART_OF_MULTICOLUMN,
+               ///
+               CELL_BEGIN_OF_MULTIROW,
+               ///
+               CELL_PART_OF_MULTIROW
        };
 
        ///
@@ -277,14 +276,16 @@ public:
 
        /// return space occupied by the second horizontal line and
        /// interline space above row \p row in pixels
-       int getAdditionalHeight(row_type row) const;
+       int interRowSpace(row_type row) const;
        ///
-       int getAdditionalWidth(idx_type cell) const;
+       int interColumnSpace(idx_type cell) const;
 
        /* returns the maximum over all rows */
        ///
        int columnWidth(idx_type cell) const;
        ///
+       int rowHeight(idx_type cell) const;
+       ///
        int width() const;
        ///
        int height() const;
@@ -340,7 +341,9 @@ public:
        ///
        int cellWidth(idx_type cell) const;
        ///
-       int getBeginningOfTextInCell(idx_type cell) const;
+       int textHOffset(idx_type cell) const;
+       ///
+       int textVOffset(idx_type cell) const;
        ///
        void appendRow(idx_type cell);
        ///
@@ -354,8 +357,6 @@ public:
        ///
        void copyColumn(col_type);
        ///
-       bool isFirstCellInRow(idx_type cell) const;
-       ///
        idx_type getFirstCellInRow(row_type row) const;
        ///
        bool isLastCellInRow(idx_type cell) const;
@@ -384,10 +385,18 @@ public:
        ///
        void setMultiColumn(idx_type cell, idx_type number);
        ///
-       idx_type unsetMultiColumn(idx_type cell); // returns number of new cells
+       void unsetMultiColumn(idx_type cell);
        ///
        bool isPartOfMultiColumn(row_type row, col_type column) const;
        ///
+       bool isPartOfMultiRow(row_type row, col_type column) const;
+       ///
+       bool isMultiRow(idx_type cell) const;
+       ///
+       void setMultiRow(idx_type cell, idx_type number);
+       ///
+       void unsetMultiRow(idx_type cell);
+       ///
        row_type cellRow(idx_type cell) const;
        ///
        col_type cellColumn(idx_type cell) const;
@@ -482,6 +491,8 @@ public:
                ///
                int multicolumn;
                ///
+               int multirow;
+               ///
                LyXAlignment alignment;
                ///
                VAlignment valignment;
@@ -614,6 +625,8 @@ public:
        ///
        idx_type columnSpan(idx_type cell) const;
        ///
+       idx_type rowSpan(idx_type cell) const;
+       ///
        BoxType useParbox(idx_type cell) const;
        ///
        // helper function for Latex returns number of newlines
@@ -622,9 +635,9 @@ public:
        ///
        int TeXBottomHLine(odocstream &, row_type row, std::string const lang) const;
        ///
-       int TeXCellPreamble(odocstream &, idx_type cell, bool & ismulticol) const;
+       int TeXCellPreamble(odocstream &, idx_type cell, bool & ismulticol, bool & ismultirow) const;
        ///
-       int TeXCellPostamble(odocstream &, idx_type cell, bool ismulticol) const;
+       int TeXCellPostamble(odocstream &, idx_type cell, bool ismulticol, bool ismultirow) const;
        ///
        int TeXLongtableHeaderFooter(odocstream &, OutputParams const &) const;
        ///