]> git.lyx.org Git - features.git/commitdiff
Parse \multicolumn in math (bug 396)
authorGeorg Baum <baum@lyx.org>
Sun, 17 May 2015 11:43:37 +0000 (13:43 +0200)
committerGeorg Baum <baum@lyx.org>
Sun, 17 May 2015 11:43:37 +0000 (13:43 +0200)
The math parser could not handle multicolumn grids. This is a problem because
there is no true ERT in math (everything is parsed).
Now multicolumn cells are parsed correctly. The display is also somewhat OK,
but apart from that any multicolumn related UI is missing. Since the file
format change is now done the UI can be added at any later point. The most
important part of bug 396 is now fixed: tex2lyx does not create invalid .lyx
files anymore for formulas containing \multicolumn.

I updated the tex2lyx test cases that produce correct output. tex2lyx does
still produce invalid output for the test cases which are not updated because
of the previous format change.

24 files changed:
development/FORMAT
lib/lyx2lyx/LyX.py
lib/lyx2lyx/lyx_2_2.py
src/mathed/InsetMathGrid.cpp
src/mathed/InsetMathGrid.h
src/mathed/InsetMathHull.cpp
src/mathed/InsetMathHull.h
src/mathed/InsetMathMatrix.cpp
src/mathed/MathParser.cpp
src/tex2lyx/test/CJK.lyx.lyx
src/tex2lyx/test/CJKutf8.lyx.lyx
src/tex2lyx/test/DummyDocument.lyx.lyx
src/tex2lyx/test/Dummy~Document.lyx.lyx
src/tex2lyx/test/XeTeX-polyglossia.lyx.lyx
src/tex2lyx/test/algo2e.lyx.lyx
src/tex2lyx/test/test-insets.lyx.lyx
src/tex2lyx/test/test-insets.tex
src/tex2lyx/test/test-memoir.lyx.lyx
src/tex2lyx/test/test-modules.lyx.lyx
src/tex2lyx/test/test-refstyle-theorems.lyx.lyx
src/tex2lyx/test/test-scr.lyx.lyx
src/tex2lyx/test/test-structure.lyx.lyx
src/tex2lyx/test/verbatim.lyx.lyx
src/version.h

index 11b1ec117f17f818a8242eef9448c7189782f5c6..dca3aa5a91a4e4b81b8c479dc943eeb15e48604d 100644 (file)
@@ -12,6 +12,10 @@ adjustments are made to tex2lyx and bugs are fixed in lyx2lyx.
 -----------------------
 
 
+2015-05-17 Georg Baum  <Georg.Baum@post.rwth-aachen.de>
+       * Format incremented to 493
+         Support \multicolumn in math formulas
+
 2015-05-16 Uwe Stöhr <uwestoehr@web.de>
        * Format incremented to 492: support for \colorbox and \fcolorbox
          in the box dialog.
index c7815f7fd4a977206566df485e36776b31988f05..181d2ddbfba1c83758183d11d07546484079688c 100644 (file)
@@ -85,7 +85,7 @@ format_relation = [("0_06",    [200], minor_versions("0.6" , 4)),
                    ("1_6", list(range(277,346)), minor_versions("1.6" , 10)),
                    ("2_0", list(range(346,414)), minor_versions("2.0" , 8)),
                    ("2_1", list(range(414,475)), minor_versions("2.1" , 0)),
-                   ("2_2", list(range(475,493)), minor_versions("2.2" , 0))
+                   ("2_2", list(range(475,494)), minor_versions("2.2" , 0))
                   ]
 
 ####################################################################
index cd8ec9830facb015a155da3f85c8be1963be5cb5..bca3b56b3be519cff516796ec38a5882d2d00875 100644 (file)
@@ -1130,6 +1130,30 @@ def revert_colorbox(document):
         i = i + 11
 
 
+def revert_mathmulticol(document):
+    " Convert formulas to ERT if they contain multicolumns "
+
+    i = 0
+    while True:
+        i = find_token(document.body, '\\begin_inset Formula', i)
+        if i == -1:
+            return
+        j = find_end_of_inset(document.body, i)
+        if j == -1:
+            document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
+            i += 1
+            continue
+        lines = document.body[i:j]
+        lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
+        code = "\n".join(lines)
+        if code.find("\\multicolumn") != -1:
+            ert = put_cmd_in_ert(code)
+            document.body[i:j+1] = ert
+            i = find_end_of_inset(document.body, i)
+        else:
+            i = j
+
+
 ##
 # Conversion hub
 #
@@ -1156,10 +1180,12 @@ convert = [
            [489, [convert_BoxFeatures]],
            [490, [convert_origin]],
            [491, []],
-           [492, [convert_colorbox]]
+           [492, [convert_colorbox]],
+           [493, []]
           ]
 
 revert =  [
+           [492, [revert_mathmulticol]],
            [491, [revert_colorbox]],
            [490, [revert_textcolor]],
            [489, [revert_origin]],
index d3eb677197dcd2535d717745dae7c2caf3f5f7ba..2591e5d9d8dfa889ce25fa33946accece798090a 100644 (file)
@@ -81,7 +81,7 @@ static void resetGrid(InsetMathGrid & grid)
 
 
 InsetMathGrid::CellInfo::CellInfo()
-       : dummy_(false)
+       : multi_(CELL_NORMAL)
 {}
 
 
@@ -172,6 +172,8 @@ void InsetMathGrid::setDefaults()
                colinfo_[col].skip_  = defaultColSpace(col);
                colinfo_[col].special_.clear();
        }
+       for (idx_type idx = 0; idx < nargs(); ++idx)
+               cellinfo_[idx].multi_ = CELL_NORMAL;
 }
 
 
@@ -358,6 +360,23 @@ InsetMathGrid::row_type InsetMathGrid::row(idx_type idx) const
 }
 
 
+InsetMathGrid::col_type InsetMathGrid::ncellcols(idx_type idx) const
+{
+       col_type cols = 1;
+       if (cellinfo_[idx].multi_ == CELL_NORMAL)
+               return cols;
+       // If the cell at idx is already CELL_PART_OF_MULTICOLUMN we return
+       // the number of remaining columns, not the ones of the complete
+       // multicolumn cell. This makes it possible to always go to the next
+       // cell with idx + ncellcols(idx) - 1.
+       row_type const r = row(idx);
+       while (idx+cols < nargs() && row(idx+cols) == r &&
+              cellinfo_[idx+cols].multi_ == CELL_PART_OF_MULTICOLUMN)
+               cols++;
+       return cols;
+}
+
+
 void InsetMathGrid::vcrskip(Length const & crskip, row_type row)
 {
        rowinfo_[row].crskip_ = crskip;
@@ -373,7 +392,12 @@ Length InsetMathGrid::vcrskip(row_type row) const
 void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const
 {
        // let the cells adjust themselves
-       InsetMathNest::metrics(mi);
+       for (idx_type i = 0; i < nargs(); ++i) {
+               if (cellinfo_[i].multi_ != CELL_PART_OF_MULTICOLUMN) {
+                       Dimension dimc;
+                       cell(i).metrics(mi, dimc);
+               }
+       }
 
        BufferView & bv = *mi.base.bv;
 
@@ -382,9 +406,12 @@ void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const
                int asc  = 0;
                int desc = 0;
                for (col_type col = 0; col < ncols(); ++col) {
-                       Dimension const & dimc = cell(index(row, col)).dimension(bv);
-                       asc  = max(asc,  dimc.asc);
-                       desc = max(desc, dimc.des);
+                       idx_type const i = index(row, col);
+                       if (cellinfo_[i].multi_ != CELL_PART_OF_MULTICOLUMN) {
+                               Dimension const & dimc = cell(i).dimension(bv);
+                               asc  = max(asc,  dimc.asc);
+                               desc = max(desc, dimc.des);
+                       }
                }
                rowinfo_[row].ascent_  = asc;
                rowinfo_[row].descent_ = desc;
@@ -421,11 +448,33 @@ void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const
                rowinfo_[row].offset_ -= h;
 
 
+       // multicolumn cell widths, as a map from first column to width in a
+       // vector of last columns.
+       // This is only used if the grid has more than one row, since for
+       // one-row grids multicolumn cells do not need special handling
+       vector<map<col_type, int> > mcolwidths(ncols());
+
        // compute absolute sizes of horizontal structure
        for (col_type col = 0; col < ncols(); ++col) {
                int wid = 0;
-               for (row_type row = 0; row < nrows(); ++row)
-                       wid = max(wid, cell(index(row, col)).dimension(bv).wid);
+               for (row_type row = 0; row < nrows(); ++row) {
+                       idx_type const i = index(row, col);
+                       if (cellinfo_[i].multi_ != CELL_PART_OF_MULTICOLUMN) {
+                               int const w = cell(i).dimension(bv).wid;
+                               col_type const cols = ncellcols(i);
+                               if (cols > 1 && nrows() > 1) {
+                                       col_type last = col+cols-1;
+                                       LASSERT(last < ncols(), last = ncols()-1);
+                                       map<col_type, int>::iterator it =
+                                               mcolwidths[last].find(col);
+                                       if (it == mcolwidths[last].end())
+                                               mcolwidths[last][col] = w;
+                                       else
+                                               it->second = max(it->second, w);
+                               } else
+                                       wid = max(wid, w);
+                       }
+               }
                colinfo_[col].width_ = wid;
        }
        colinfo_[ncols()].width_  = 0;
@@ -441,6 +490,35 @@ void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const
                        colinfo_[col].lines_ * vlinesep();
        }
 
+       // increase column widths for multicolumn cells if needed
+       // FIXME: multicolumn lines are not yet considered
+       for (col_type last = 0; last < ncols(); ++last) {
+               map<col_type, int> const & widths = mcolwidths[last];
+               // We increase the width of the last column of the multicol
+               // cell (some sort of left alignment). Since we iterate through
+               // the last and the first columns from left to right, we ensure
+               // that increased widths of previous columns are correctly
+               // taken into account for later columns, thus preventing
+               // unneeded width increasing.
+               for (map<col_type, int>::const_iterator it = widths.begin();
+                    it != widths.end(); ++it) {
+                       int const wid = it->second;
+                       col_type const first = it->first;
+                       int const nextoffset =
+                               colinfo_[first].offset_ +
+                               wid +
+                               colinfo_[last].skip_ +
+                               colsep() +
+                               colinfo_[last+1].lines_ * vlinesep();
+                       int const dx = nextoffset - colinfo_[last+1].offset_;
+                       if (dx > 0) {
+                               colinfo_[last].width_ += dx;
+                               for (col_type col = last + 1; col <= ncols(); ++col)
+                                       colinfo_[col].offset_ += dx;
+                       }
+               }
+       }
+
 
        dim.wid = colinfo_[ncols() - 1].offset_
                + colinfo_[ncols() - 1].width_
@@ -526,11 +604,47 @@ void InsetMathGrid::drawWithMargin(PainterInfo & pi, int x, int y,
        Dimension const dim = dimension(*pi.base.bv);
        BufferView const & bv = *pi.base.bv;
 
-       for (idx_type idx = 0; idx < nargs(); ++idx)
-               cell(idx).draw(pi, x + lmargin + cellXOffset(bv, idx),
-                       y + cellYOffset(idx));
+       for (idx_type idx = 0; idx < nargs(); ++idx) {
+               if (cellinfo_[idx].multi_ != CELL_PART_OF_MULTICOLUMN) {
+                       cell(idx).draw(pi,
+                               x + lmargin + cellXOffset(bv, idx),
+                               y + cellYOffset(idx));
+
+                       // draw inner lines cell by cell because of possible multicolumns
+                       // FIXME: multicolumn lines are not yet considered
+                       row_type const r = row(idx);
+                       col_type const c = col(idx);
+                       if (r > 0 && r < nrows()) {
+                               for (unsigned int i = 0; i < rowinfo_[r].lines_; ++i) {
+                                       int yy = y + rowinfo_[r].offset_
+                                               - rowinfo_[r].ascent_
+                                               - i * hlinesep()
+                                               - hlinesep()/2 - rowsep()/2;
+                                       pi.pain.line(
+                                               x + lmargin + colinfo_[c].offset_,
+                                               yy,
+                                               x + lmargin + colinfo_[c+1].offset_,
+                                               yy, Color_foreground);
+                               }
+                       }
+                       if (c > 0 && c < ncols()) {
+                               for (unsigned int i = 0; i < colinfo_[c].lines_; ++i) {
+                                       int xx = x + lmargin
+                                               + colinfo_[c].offset_
+                                               - i * vlinesep()
+                                               - vlinesep()/2 - colsep()/2;
+                                       pi.pain.line(xx,
+                                               rowinfo_[r].offset_ - rowinfo_[r].ascent_,
+                                               xx,
+                                               rowinfo_[r].offset_ + rowinfo_[r].descent_,
+                                               Color_foreground);
+                               }
+                       }
+               }
+       }
 
-       for (row_type row = 0; row <= nrows(); ++row)
+       // draw outer lines in one go
+       for (row_type row = 0; row <= nrows(); row += nrows())
                for (unsigned int i = 0; i < rowinfo_[row].lines_; ++i) {
                        int yy = y + rowinfo_[row].offset_ - rowinfo_[row].ascent_
                                - i * hlinesep() - hlinesep()/2 - rowsep()/2;
@@ -539,7 +653,7 @@ void InsetMathGrid::drawWithMargin(PainterInfo & pi, int x, int y,
                                     Color_foreground);
                }
 
-       for (col_type col = 0; col <= ncols(); ++col)
+       for (col_type col = 0; col <= ncols(); col += ncols())
                for (unsigned int i = 0; i < colinfo_[col].lines_; ++i) {
                        int xx = x + lmargin + colinfo_[col].offset_
                                - i * vlinesep() - vlinesep()/2 - colsep()/2;
@@ -554,20 +668,23 @@ void InsetMathGrid::drawWithMargin(PainterInfo & pi, int x, int y,
 void InsetMathGrid::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
 {
        // let the cells adjust themselves
-       //InsetMathNest::metrics(mi);
        for (idx_type i = 0; i < nargs(); ++i)
-               cell(i).metricsT(mi, dim);
+               if (cellinfo_[i].multi_ != CELL_PART_OF_MULTICOLUMN)
+                       cell(i).metricsT(mi, dim);
 
        // compute absolute sizes of vertical structure
        for (row_type row = 0; row < nrows(); ++row) {
                int asc  = 0;
                int desc = 0;
                for (col_type col = 0; col < ncols(); ++col) {
-                       //MathData const & c = cell(index(row, col));
-                       // FIXME: BROKEN!
-                       Dimension dimc;
-                       asc  = max(asc,  dimc.ascent());
-                       desc = max(desc, dimc.descent());
+                       idx_type const i = index(row, col);
+                       if (cellinfo_[i].multi_ != CELL_PART_OF_MULTICOLUMN) {
+                               //MathData const & c = cell(i);
+                               // FIXME: BROKEN!
+                               Dimension dimc;
+                               asc  = max(asc,  dimc.ascent());
+                               desc = max(desc, dimc.descent());
+                       }
                }
                rowinfo_[row].ascent_  = asc;
                rowinfo_[row].descent_ = desc;
@@ -609,7 +726,9 @@ void InsetMathGrid::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
                int wid = 0;
                for (row_type row = 0; row < nrows(); ++row) {
                        // FIXME: BROKEN!
-                       //wid = max(wid, cell(index(row, col)).width());
+                       //idx_type const i = index(row, col);
+                       //if (cellinfo_[i].multi_ != CELL_PART_OF_MULTICOLUMN)
+                       //      wid = max(wid, cell(i).width());
                }
                colinfo_[col].width_ = wid;
        }
@@ -647,7 +766,8 @@ void InsetMathGrid::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
 void InsetMathGrid::drawT(TextPainter & /*pain*/, int /*x*/, int /*y*/) const
 {
 //     for (idx_type idx = 0; idx < nargs(); ++idx)
-//             cell(idx).drawT(pain, x + cellXOffset(idx), y + cellYOffset(idx));
+//             if (cellinfo_[idx].multi_ != CELL_PART_OF_MULTICOLUMN)
+//                     cell(idx).drawT(pain, x + cellXOffset(idx), y + cellYOffset(idx));
 }
 
 
@@ -655,7 +775,8 @@ void InsetMathGrid::updateBuffer(ParIterator const & it, UpdateType utype)
 {
        // pass down
        for (idx_type idx = 0; idx < nargs(); ++idx)
-               cell(idx).updateBuffer(it, utype);
+               if (cellinfo_[idx].multi_ != CELL_PART_OF_MULTICOLUMN)
+                       cell(idx).updateBuffer(it, utype);
 }
 
 
@@ -801,14 +922,16 @@ void InsetMathGrid::swapCol(col_type col)
 
 int InsetMathGrid::cellXOffset(BufferView const & bv, idx_type idx) const
 {
+       if (cellinfo_[idx].multi_ == CELL_PART_OF_MULTICOLUMN)
+               return 0;
        col_type c = col(idx);
        int x = colinfo_[c].offset_;
-       char align = displayColAlign(c, row(idx));
+       char align = displayColAlign(idx);
        Dimension const & celldim = cell(idx).dimension(bv);
        if (align == 'r' || align == 'R')
-               x += colinfo_[c].width_ - celldim.wid;
+               x += cellWidth(idx) - celldim.wid;
        if (align == 'c' || align == 'C')
-               x += (colinfo_[c].width_ - celldim.wid) / 2;
+               x += (cellWidth(idx) - celldim.wid) / 2;
        return x;
 }
 
@@ -819,6 +942,27 @@ int InsetMathGrid::cellYOffset(idx_type idx) const
 }
 
 
+int InsetMathGrid::cellWidth(idx_type idx) const
+{
+       switch (cellinfo_[idx].multi_) {
+       case CELL_NORMAL:
+               return colinfo_[col(idx)].width_;
+       case CELL_BEGIN_OF_MULTICOLUMN: {
+               col_type c1 = col(idx);
+               col_type c2 = c1 + ncellcols(idx);
+               return colinfo_[c2].offset_
+                       - colinfo_[c1].offset_
+                       - colinfo_[c2].skip_
+                       - colsep()
+                       - colinfo_[c2].lines_ * vlinesep();
+       }
+       case CELL_PART_OF_MULTICOLUMN:
+               return 0;
+       }
+       return 0;
+}
+
+
 bool InsetMathGrid::idxUpDown(Cursor & cur, bool up) const
 {
        if (up) {
@@ -830,6 +974,11 @@ bool InsetMathGrid::idxUpDown(Cursor & cur, bool up) const
                        return false;
                cur.idx() += ncols();
        }
+       // If we are in a multicolumn cell, move to the "real" cell
+       while (cellinfo_[cur.idx()].multi_ == CELL_PART_OF_MULTICOLUMN) {
+               LASSERT(cur.idx() > 0, return false);
+               --cur.idx();
+       }
        cur.pos() = cur.cell().x2pos(&cur.bv(), cur.x_target() - cur.cell().xo(cur.bv()));
        return true;
 }
@@ -841,6 +990,11 @@ bool InsetMathGrid::idxBackward(Cursor & cur) const
        if (cur.col() == 0)
                return false;
        --cur.idx();
+       // If we are in a multicolumn cell, move to the "real" cell
+       while (cellinfo_[cur.idx()].multi_ == CELL_PART_OF_MULTICOLUMN) {
+               LASSERT(cur.idx() > 0, return false);
+               --cur.idx();
+       }
        cur.pos() = cur.lastpos();
        return true;
 }
@@ -852,6 +1006,13 @@ bool InsetMathGrid::idxForward(Cursor & cur) const
        if (cur.col() + 1 == ncols())
                return false;
        ++cur.idx();
+       // If we are in a multicolumn cell, move to the next cell
+       while (cellinfo_[cur.idx()].multi_ == CELL_PART_OF_MULTICOLUMN) {
+               // leave matrix if at the back edge
+               if (cur.col() + 1 == ncols())
+                       return false;
+               ++cur.idx();
+       }
        cur.pos() = 0;
        return true;
 }
@@ -869,6 +1030,11 @@ bool InsetMathGrid::idxFirst(Cursor & cur) const
                default:
                        cur.idx() = ((nrows() - 1) / 2) * ncols();
        }
+       // If we are in a multicolumn cell, move to the "real" cell
+       while (cellinfo_[cur.idx()].multi_ == CELL_PART_OF_MULTICOLUMN) {
+               LASSERT(cur.idx() > 0, return false);
+               --cur.idx();
+       }
        cur.pos() = 0;
        return true;
 }
@@ -886,6 +1052,11 @@ bool InsetMathGrid::idxLast(Cursor & cur) const
                default:
                        cur.idx() = ((nrows() - 1) / 2 + 1) * ncols() - 1;
        }
+       // If we are in a multicolumn cell, move to the "real" cell
+       while (cellinfo_[cur.idx()].multi_ == CELL_PART_OF_MULTICOLUMN) {
+               LASSERT(cur.idx() > 0, return false);
+               --cur.idx();
+       }
        cur.pos() = cur.lastpos();
        return true;
 }
@@ -936,9 +1107,15 @@ void InsetMathGrid::idxGlue(idx_type idx)
                        delRow(row(idx) + 1);
                }
        } else {
-               cell(idx).append(cell(idx + 1));
+               idx_type idx_next = idx + 1;
+               while (idx_next < nargs() &&
+                      cellinfo_[idx_next].multi_ == CELL_PART_OF_MULTICOLUMN)
+                       ++idx_next;
+               if (idx_next < nargs())
+                       cell(idx).append(cell(idx_next));
+               col_type oldcol = c + 1;
                for (col_type cc = c + 2; cc < ncols(); ++cc)
-                       cell(idx - c + cc - 1) = cell(idx - c + cc);
+                       cell(idx - oldcol + cc) = cell(idx - oldcol + 1 + cc);
                cell(idx - c + ncols() - 1).clear();
        }
 }
@@ -968,14 +1145,26 @@ bool InsetMathGrid::idxBetween(idx_type idx, idx_type from, idx_type to) const
 }
 
 
-
 void InsetMathGrid::normalize(NormalStream & os) const
 {
        os << "[grid ";
        for (row_type row = 0; row < nrows(); ++row) {
                os << "[row ";
-               for (col_type col = 0; col < ncols(); ++col)
-                       os << "[cell " << cell(index(row, col)) << ']';
+               for (col_type col = 0; col < ncols(); ++col) {
+                       idx_type const i = index(row, col);
+                       switch (cellinfo_[i].multi_) {
+                       case CELL_NORMAL:
+                               os << "[cell " << cell(i) << ']';
+                               break;
+                       case CELL_BEGIN_OF_MULTICOLUMN:
+                               os << "[cell colspan="
+                                  << static_cast<int>(ncellcols(i)) << ' '
+                                  << cell(i) << ']';
+                               break;
+                       case CELL_PART_OF_MULTICOLUMN:
+                               break;
+                       }
+               }
                os << ']';
        }
        os << ']';
@@ -992,9 +1181,16 @@ void InsetMathGrid::mathmlize(MathStream & os) const
                if (havetable)
                        os << MTag("mtr");
                for (col_type col = 0; col < ncols(); ++col) {
-                       os << MTag(celltag);
-                       os << cell(index(row, col));
-                       os << ETag(celltag);
+                       idx_type const i = index(row, col);
+                       if (cellinfo_[i].multi_ != CELL_PART_OF_MULTICOLUMN) {
+                               col_type const cellcols = ncellcols(i);
+                               ostringstream attr;
+                               if (havetable && cellcols > 1)
+                                       attr << "colspan='" << cellcols << '\'';
+                               os << MTag(celltag, attr.str());
+                               os << cell(index(row, col));
+                               os << ETag(celltag);
+                       }
                }
                if (havetable)
                        os << ETag("mtr");
@@ -1017,9 +1213,16 @@ void InsetMathGrid::htmlize(HtmlStream & os, string attrib) const
        for (row_type row = 0; row < nrows(); ++row) {
                os << MTag("tr");
                for (col_type col = 0; col < ncols(); ++col) {
-                       os << MTag("td");
-                       os << cell(index(row, col));
-                       os << ETag("td");
+                       idx_type const i = index(row, col);
+                       if (cellinfo_[i].multi_ != CELL_PART_OF_MULTICOLUMN) {
+                               col_type const cellcols = ncellcols(i);
+                               ostringstream attr;
+                               if (cellcols > 1)
+                                       attr << "colspan='" << cellcols << '\'';
+                               os << MTag("td", attr.str());
+                               os << cell(index(row, col));
+                               os << ETag("td");
+                       }
                }
                os << ETag("tr");
        }
@@ -1052,7 +1255,10 @@ void InsetMathGrid::write(WriteStream & os,
                bool emptyline = true;
                bool last_eoln = true;
                for (col_type col = beg_col; col < end_col; ++col) {
-                       bool const empty_cell = cell(index(row, col)).empty();
+                       idx_type const idx = index(row, col);
+                       if (cellinfo_[idx].multi_ == CELL_PART_OF_MULTICOLUMN)
+                               continue;
+                       bool const empty_cell = cell(idx).empty();
                        if (!empty_cell)
                                last_eoln = false;
                        if (!empty_cell || colinfo_[col + 1].lines_) {
@@ -1060,11 +1266,26 @@ void InsetMathGrid::write(WriteStream & os,
                                emptyline = false;
                        }
                }
-               for (col_type col = beg_col; col < lastcol; ++col) {
-                       os << cell(index(row, col));
+               for (col_type col = beg_col; col < lastcol;) {
+                       int nccols = 1;
+                       idx_type const idx = index(row, col);
+                       if (cellinfo_[idx].multi_ == CELL_BEGIN_OF_MULTICOLUMN) {
+                               size_t s = col + 1;
+                               while (s < ncols() &&
+                                      cellinfo_[index(row, s)].multi_ == CELL_PART_OF_MULTICOLUMN)
+                                       s++;
+                               nccols = s - col;
+                               os << "\\multicolumn{" << nccols
+                                  << "}{" << cellinfo_[idx].align_
+                                  << "}{";
+                       }
+                       os << cell(idx);
                        if (os.pendingBrace())
                                ModeSpecifier specifier(os, TEXT_MODE);
+                       if (cellinfo_[idx].multi_ == CELL_BEGIN_OF_MULTICOLUMN)
+                               os << '}';
                        os << eocString(col, lastcol);
+                       col += nccols;
                }
                eol = eolString(row, os.fragile(), os.latex(), last_eoln);
                os << eol;
@@ -1125,11 +1346,30 @@ void InsetMathGrid::splitCell(Cursor & cur)
        ar.erase(0, cur.pos());
        cur.cell().erase(cur.pos(), cur.lastpos());
        ++cur.idx();
+       while (cur.idx() << nargs() &&
+              cellinfo_[cur.idx()].multi_ == CELL_BEGIN_OF_MULTICOLUMN)
+               ++cur.idx();
        cur.pos() = 0;
        cur.cell().insert(0, ar);
 }
 
 
+char InsetMathGrid::displayColAlign(idx_type idx) const
+{
+       if (cellinfo_[idx].multi_ == CELL_BEGIN_OF_MULTICOLUMN) {
+               // align_ may also contain lines like "||r|", so this is
+               // not complete, but we catch at least the simple cases.
+               if (cellinfo_[idx].align_ == "c")
+                       return 'c';
+               if (cellinfo_[idx].align_ == "l")
+                       return 'l';
+               if (cellinfo_[idx].align_ == "r")
+                       return 'r';
+       }
+       return colinfo_[col(idx)].align_;
+}
+
+
 void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd)
 {
        //lyxerr << "*** InsetMathGrid: request: " << cmd << endl;
index 7ff533be998e4f978b16d500c3e9cb95c3a39434..bd3066db0b2e85b662925eff3c00af65d6987ab3 100644 (file)
@@ -25,13 +25,23 @@ namespace lyx {
 class InsetMathGrid : public InsetMathNest {
 public:
 
+       enum Multicolumn {
+               /// A normal cell
+               CELL_NORMAL = 0,
+               /// A multicolumn cell. The number of columns is <tt>1 + number
+               /// of CELL_PART_OF_MULTICOLUMN cells</tt> that follow directly
+               CELL_BEGIN_OF_MULTICOLUMN = 1,
+               /// This is a dummy cell (part of a multicolumn cell)
+               CELL_PART_OF_MULTICOLUMN = 2
+       };
+
        /// additional per-cell information
        class CellInfo {
        public:
                ///
                CellInfo();
-               /// a dummy cell before a multicolumn cell
-               int dummy_;
+               /// multicolumn flag
+               Multicolumn multi_;
                /// special multi colums alignment
                docstring align_;
                /// these should be a per-cell property, but ok to have it here
@@ -79,7 +89,7 @@ public:
                mutable int offset_;
                /// how many lines to the left of this column?
                unsigned int lines_;
-               /// additional amount to be skipped when drawing
+               /// additional amount to the right to be skipped when drawing
                int skip_;
                /// Special alignment.
                /// This does also contain align_ and lines_ if it is nonempty.
@@ -152,6 +162,8 @@ public:
        col_type col(idx_type idx) const;
        ///
        row_type row(idx_type idx) const;
+       /// number of columns of cell \p idx
+       col_type ncellcols(idx_type idx) const;
 
        ///
        bool idxUpDown(Cursor &, bool up) const;
@@ -237,6 +249,8 @@ protected:
        int cellXOffset(BufferView const &, idx_type idx) const;
        /// returns y offset of cell compared to inset
        int cellYOffset(idx_type idx) const;
+       /// Width of cell, taking combined columns into account
+       int cellWidth(idx_type idx) const;
        /// returns proper 'end of line' code for LaTeX
        virtual docstring eolString(row_type row, bool fragile, bool latex,
                        bool last_eoln) const;
@@ -244,9 +258,9 @@ protected:
        virtual docstring eocString(col_type col, col_type lastcol) const;
        /// splits cells and shifts right part to the next cell
        void splitCell(Cursor & cur);
-       /// Column aligmment for display of cell at (\p row, \p col).
+       /// Column aligmment for display of cell \p idx.
        /// Must not be written to file!
-       virtual char displayColAlign(col_type col, row_type) const { return colinfo_[col].align_; }
+       virtual char displayColAlign(idx_type idx) const;
 
 
        /// row info.
index 19206331152f3532a7dd742788dfdf766bece62a..961300ba78c4320d8b54d3bdf86dfcf4043ae441 100644 (file)
@@ -356,15 +356,16 @@ char InsetMathHull::defaultColAlign(col_type col)
 }
 
 
-char InsetMathHull::displayColAlign(col_type col, row_type row) const
+char InsetMathHull::displayColAlign(idx_type idx) const
 {
        if (type_ == hullMultline) {
-               if (row == 0)
+               row_type const r = row(idx);
+               if (r == 0)
                        return 'l';
-               if (row == nrows() - 1)
+               if (r == nrows() - 1)
                        return 'r';
        }
-       return InsetMathGrid::displayColAlign(col, row);
+       return InsetMathGrid::displayColAlign(idx);
 }
 
 
index fce431294c55b8ec82bae34ce422e7db237cb2fe..55275371fd796dc7f4cb33fe007ad4fd03bf48bc 100644 (file)
@@ -111,8 +111,7 @@ public:
        ///
        char defaultColAlign(col_type col);
        ///
-       ///
-       char displayColAlign(col_type col, row_type row) const;
+       char displayColAlign(idx_type idx) const;
        ///
        bool idxFirst(Cursor &) const;
        ///
index 4d309d13e88dba668401e28dd6b23e4562f17149..852cd893a5244230ee5d754f9c7a89927061d296 100644 (file)
@@ -99,8 +99,16 @@ void InsetMathMatrix::mathmlize(MathStream & os) const
        os << MTag("mtable");
        for (row_type row = 0; row < nrows(); ++row) {
                os << MTag("mtr");
-               for (col_type col = 0; col < ncols(); ++col)
-                       os << MTag("mtd") << cell(index(row, col)) << ETag("mtd");
+               for (col_type col = 0; col < ncols(); ++col) {
+                       idx_type const i = index(row, col);
+                       if (cellinfo_[i].multi_ != CELL_PART_OF_MULTICOLUMN) {
+                               col_type const cellcols = ncellcols(i);
+                               ostringstream attr;
+                               if (cellcols > 1)
+                                       attr << "columnspan='" << cellcols << '\'';
+                               os << MTag("mtd", attr.str()) << cell(i) << ETag("mtd");
+                       }
+               }
                os << ETag("mtr");
        }
        os << ETag("mtable");
@@ -124,8 +132,18 @@ void InsetMathMatrix::htmlize(HtmlStream & os) const
                os << MTag("tr") << '\n';
                if (row == 0)
                        os << MTag("td", lattrib) << ETag("td") << '\n';
-               for (col_type col = 0; col < ncols(); ++col)
-                       os << MTag("td") << cell(index(row, col)) << ETag("td") << '\n';
+               for (col_type col = 0; col < ncols(); ++col) {
+                       idx_type const i = index(row, col);
+                       if (cellinfo_[i].multi_ != CELL_PART_OF_MULTICOLUMN) {
+                               col_type const cellcols = ncellcols(i);
+                               ostringstream attr;
+                               if (cellcols > 1)
+                                       attr << "colspan='" << cellcols
+                                            << '\'';
+                               os << MTag("td", attr.str()) << cell(i)
+                                  << ETag("td") << '\n';
+                       }
+               }
                if (row == 0)
                        os << MTag("td", rattrib) << ETag("td") << '\n';
                os << ETag("tr") << '\n';
index f19480b4b73e1f947110b6b37a1099230f572f1c..0f10aa5ed75c7104c38d94e18e72b7c8dee8d8bd 100644 (file)
@@ -64,6 +64,7 @@ following hack as starting point to write some macros:
 #include "InsetMathString.h"
 #include "InsetMathTabular.h"
 #include "MathMacroTemplate.h"
+#include "MathExtern.h"
 #include "MathFactory.h"
 #include "MathMacroArgument.h"
 #include "MathSupport.h"
@@ -1374,7 +1375,6 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        }
                }
 
-#if 0
                else if (t.cs() == "multicolumn") {
                        // extract column count and insert dummy cells
                        MathData count;
@@ -1382,30 +1382,30 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        int cols = 1;
                        if (!extractNumber(count, cols)) {
                                success_ = false;
-                               lyxerr << " can't extract number of cells from " << count << endl;
+                               error("can't extract number of multicolumn cells");
                        }
                        // resize the table if necessary
+                       size_t first = 0;
                        for (int i = 0; i < cols; ++i) {
                                if (addCol(grid, cellcol)) {
-                                       cell = &grid.cell(grid.index(
-                                                       cellrow, cellcol));
-                                       // mark this as dummy
-                                       grid.cellinfo(grid.index(
-                                               cellrow, cellcol)).dummy_ = true;
+                                       size_t const idx = grid.index(cellrow, cellcol);
+                                       if (i == 0)
+                                               first = idx;
+                                       grid.cellinfo(idx).multi_ =
+                                               InsetMathGrid::CELL_PART_OF_MULTICOLUMN;
                                }
                        }
-                       // the last cell is the real thing, not a dummy
-                       grid.cellinfo(grid.index(cellrow, cellcol)).dummy_ = false;
+
+                       // the first cell is the real thing, not a dummy
+                       cell = &grid.cell(first);
+                       grid.cellinfo(first).multi_ = InsetMathGrid::CELL_BEGIN_OF_MULTICOLUMN;
 
                        // read special alignment
-                       MathData align;
-                       parse(align, FLAG_ITEM, mode);
-                       //grid.cellinfo(grid.index(cellrow, cellcol)).align_ = extractString(align);
+                       grid.cellinfo(first).align_ = parse_verbatim_item();
 
                        // parse the remaining contents into the "real" cell
                        parse(*cell, FLAG_ITEM, mode);
                }
-#endif
 
                else if (t.cs() == "limits" || t.cs() == "nolimits") {
                        CatCode const cat = nextToken().cat();
index 4bafa1696af889a4e83b14da0e8712597abb5fb8..521798ff80c8679d03a9f57ea5556dcc13193ead 100644 (file)
@@ -1,5 +1,5 @@
 #LyX file created by tex2lyx 2.2
-\lyxformat 491
+\lyxformat 493
 \begin_document
 \begin_header
 \origin roundtrip
index 39dbdafcb963b17812095a7bf6dda5797faa06a5..72c2afe893dda862660d94d3d9fb53a411bde377 100644 (file)
@@ -1,5 +1,5 @@
 #LyX file created by tex2lyx 2.2
-\lyxformat 491
+\lyxformat 493
 \begin_document
 \begin_header
 \origin roundtrip
index 846f128571406e671c9ba37e029599d4f9ef5bd4..9638025a3a21b2754979fddfb15787080c69fef0 100644 (file)
@@ -1,5 +1,5 @@
 #LyX file created by tex2lyx 2.2
-\lyxformat 491
+\lyxformat 493
 \begin_document
 \begin_header
 \origin roundtrip
index 3ca85cda8fc2b944086e0f8146105ddd855be736..4bde020a83e21ab3522c1907e765f53e802a3e96 100644 (file)
@@ -1,5 +1,5 @@
 #LyX file created by tex2lyx 2.2
-\lyxformat 491
+\lyxformat 493
 \begin_document
 \begin_header
 \origin roundtrip
index 002cc7a3414a54d238bb1064a0c275f16c7cf50b..cae1cea37272d6fcda5ecda54cc3a076a034876e 100644 (file)
@@ -1,5 +1,5 @@
 #LyX file created by tex2lyx 2.2
-\lyxformat 491
+\lyxformat 493
 \begin_document
 \begin_header
 \origin roundtrip
index a00314d90365080eb906d1a6675762e6b79306f1..cfc3d77df2f3ff4508e1feccb1b45eed25fc3d99 100644 (file)
@@ -1,5 +1,5 @@
 #LyX file created by tex2lyx 2.2
-\lyxformat 491
+\lyxformat 493
 \begin_document
 \begin_header
 \origin roundtrip
index 3e0df527b6cf805e83605edeb2466a7c1c02e918..c5b0e3a09b46a9830917ab6d6a7bef123629f0e3 100644 (file)
@@ -6937,6 +6937,19 @@ $
 .
 \end_layout
 
+\begin_layout Standard
+
+\begin_inset Formula \[
+\begin{array}{rclccc}
+1 + 2 & = & 3 
+\multicolumn{3}{c}{4 < 5 \leq 6 }
+\end{array}
+\]
+\end_inset
+
+
+\end_layout
+
 \begin_layout Section
 Lists/Indices
 \end_layout
index 028b970510b331d9f2213e7c14a944d771fb9106..013ac792529de89e1a53b339f290149998289852 100644 (file)
@@ -691,6 +691,13 @@ Let $f:\left[  a,b\right]  \rightarrow%
 %EndExpansion
 $.
 
+\[
+\begin{array}{rclccc}
+1 + 2 & = & 3 
+\multicolumn{3}{c}{4 < 5 \leq 6 }
+\end{array}
+\]
+
 
 \section{Lists/Indices}
 
index 452995b7c8df0747a538f09519305468a8de97e7..ba806e6907ad24234864a8a87d9d7177e57caffb 100644 (file)
@@ -1,5 +1,5 @@
 #LyX file created by tex2lyx 2.2
-\lyxformat 491
+\lyxformat 493
 \begin_document
 \begin_header
 \origin roundtrip
index a92f3a6dbd4fbcab51daafe4a915b461437ae984..a5c8ef1438d21a8a652c8c68a7658d261531c9cc 100644 (file)
@@ -1,5 +1,5 @@
 #LyX file created by tex2lyx 2.2
-\lyxformat 491
+\lyxformat 493
 \begin_document
 \begin_header
 \origin roundtrip
index 0bd7e050c98dbf0a2cd41a636f5a56e4f25e487a..bdea4cfe00473e3b5aca0cfaf22a6e93da90c55e 100644 (file)
@@ -1,5 +1,5 @@
 #LyX file created by tex2lyx 2.2
-\lyxformat 491
+\lyxformat 493
 \begin_document
 \begin_header
 \origin roundtrip
index 3a23b487d0fb7c5e1154aac11e9554535f549fdf..1660587cd1cb9972c952d33844f6a3dda33f44ed 100644 (file)
@@ -1,5 +1,5 @@
 #LyX file created by tex2lyx 2.2
-\lyxformat 491
+\lyxformat 493
 \begin_document
 \begin_header
 \origin roundtrip
index 22b79052198ed118667a832beae865664be43089..0ec1f7fdb2594148bebf8079ddf99dcfce1ffabf 100644 (file)
@@ -1,5 +1,5 @@
 #LyX file created by tex2lyx 2.2
-\lyxformat 491
+\lyxformat 493
 \begin_document
 \begin_header
 \origin roundtrip
index f96ca01439904c6b4a4f420857cfeb1c48953215..c0682af22d74c22b1fdcbf90b0d3b0362229cc3a 100644 (file)
@@ -1,5 +1,5 @@
 #LyX file created by tex2lyx 2.2
-\lyxformat 491
+\lyxformat 493
 \begin_document
 \begin_header
 \origin roundtrip
index 06ddc33472e1139d76f8f4de8a92868adfc383d9..6b80b599dcff84a8980e159517846323ee3df803 100644 (file)
@@ -32,8 +32,8 @@ extern char const * const lyx_version_info;
 
 // Do not remove the comment below, so we get merge conflict in
 // independent branches. Instead add your own.
-#define LYX_FORMAT_LYX 492 // uwestoehr: support for colorboxes
-#define LYX_FORMAT_TEX2LYX 492
+#define LYX_FORMAT_LYX 493 // gb math multicolumn
+#define LYX_FORMAT_TEX2LYX 493
 
 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX
 #ifndef _MSC_VER