Parse \multicolumn in math (bug 396)

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.
This commit is contained in:
Georg Baum 2015-05-17 13:43:37 +02:00
parent 46671b5836
commit 39329935f3
24 changed files with 405 additions and 83 deletions

View 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> 2015-05-16 Uwe Stöhr <uwestoehr@web.de>
* Format incremented to 492: support for \colorbox and \fcolorbox * Format incremented to 492: support for \colorbox and \fcolorbox
in the box dialog. in the box dialog.

View 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)), ("1_6", list(range(277,346)), minor_versions("1.6" , 10)),
("2_0", list(range(346,414)), minor_versions("2.0" , 8)), ("2_0", list(range(346,414)), minor_versions("2.0" , 8)),
("2_1", list(range(414,475)), minor_versions("2.1" , 0)), ("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))
] ]
#################################################################### ####################################################################

View File

@ -1130,6 +1130,30 @@ def revert_colorbox(document):
i = i + 11 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 # Conversion hub
# #
@ -1156,10 +1180,12 @@ convert = [
[489, [convert_BoxFeatures]], [489, [convert_BoxFeatures]],
[490, [convert_origin]], [490, [convert_origin]],
[491, []], [491, []],
[492, [convert_colorbox]] [492, [convert_colorbox]],
[493, []]
] ]
revert = [ revert = [
[492, [revert_mathmulticol]],
[491, [revert_colorbox]], [491, [revert_colorbox]],
[490, [revert_textcolor]], [490, [revert_textcolor]],
[489, [revert_origin]], [489, [revert_origin]],

View File

@ -81,7 +81,7 @@ static void resetGrid(InsetMathGrid & grid)
InsetMathGrid::CellInfo::CellInfo() InsetMathGrid::CellInfo::CellInfo()
: dummy_(false) : multi_(CELL_NORMAL)
{} {}
@ -172,6 +172,8 @@ void InsetMathGrid::setDefaults()
colinfo_[col].skip_ = defaultColSpace(col); colinfo_[col].skip_ = defaultColSpace(col);
colinfo_[col].special_.clear(); 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) void InsetMathGrid::vcrskip(Length const & crskip, row_type row)
{ {
rowinfo_[row].crskip_ = crskip; rowinfo_[row].crskip_ = crskip;
@ -373,7 +392,12 @@ Length InsetMathGrid::vcrskip(row_type row) const
void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const
{ {
// let the cells adjust themselves // 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; BufferView & bv = *mi.base.bv;
@ -382,10 +406,13 @@ void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const
int asc = 0; int asc = 0;
int desc = 0; int desc = 0;
for (col_type col = 0; col < ncols(); ++col) { for (col_type col = 0; col < ncols(); ++col) {
Dimension const & dimc = cell(index(row, col)).dimension(bv); 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); asc = max(asc, dimc.asc);
desc = max(desc, dimc.des); desc = max(desc, dimc.des);
} }
}
rowinfo_[row].ascent_ = asc; rowinfo_[row].ascent_ = asc;
rowinfo_[row].descent_ = desc; rowinfo_[row].descent_ = desc;
} }
@ -421,11 +448,33 @@ void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const
rowinfo_[row].offset_ -= h; 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 // compute absolute sizes of horizontal structure
for (col_type col = 0; col < ncols(); ++col) { for (col_type col = 0; col < ncols(); ++col) {
int wid = 0; int wid = 0;
for (row_type row = 0; row < nrows(); ++row) for (row_type row = 0; row < nrows(); ++row) {
wid = max(wid, cell(index(row, col)).dimension(bv).wid); 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_[col].width_ = wid;
} }
colinfo_[ncols()].width_ = 0; colinfo_[ncols()].width_ = 0;
@ -441,6 +490,35 @@ void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const
colinfo_[col].lines_ * vlinesep(); 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_ dim.wid = colinfo_[ncols() - 1].offset_
+ colinfo_[ncols() - 1].width_ + colinfo_[ncols() - 1].width_
@ -526,11 +604,47 @@ void InsetMathGrid::drawWithMargin(PainterInfo & pi, int x, int y,
Dimension const dim = dimension(*pi.base.bv); Dimension const dim = dimension(*pi.base.bv);
BufferView const & bv = *pi.base.bv; BufferView const & bv = *pi.base.bv;
for (idx_type idx = 0; idx < nargs(); ++idx) for (idx_type idx = 0; idx < nargs(); ++idx) {
cell(idx).draw(pi, x + lmargin + cellXOffset(bv, idx), if (cellinfo_[idx].multi_ != CELL_PART_OF_MULTICOLUMN) {
cell(idx).draw(pi,
x + lmargin + cellXOffset(bv, idx),
y + cellYOffset(idx)); y + cellYOffset(idx));
for (row_type row = 0; row <= nrows(); ++row) // 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);
}
}
}
}
// 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) { for (unsigned int i = 0; i < rowinfo_[row].lines_; ++i) {
int yy = y + rowinfo_[row].offset_ - rowinfo_[row].ascent_ int yy = y + rowinfo_[row].offset_ - rowinfo_[row].ascent_
- i * hlinesep() - hlinesep()/2 - rowsep()/2; - i * hlinesep() - hlinesep()/2 - rowsep()/2;
@ -539,7 +653,7 @@ void InsetMathGrid::drawWithMargin(PainterInfo & pi, int x, int y,
Color_foreground); 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) { for (unsigned int i = 0; i < colinfo_[col].lines_; ++i) {
int xx = x + lmargin + colinfo_[col].offset_ int xx = x + lmargin + colinfo_[col].offset_
- i * vlinesep() - vlinesep()/2 - colsep()/2; - i * vlinesep() - vlinesep()/2 - colsep()/2;
@ -554,8 +668,8 @@ void InsetMathGrid::drawWithMargin(PainterInfo & pi, int x, int y,
void InsetMathGrid::metricsT(TextMetricsInfo const & mi, Dimension & dim) const void InsetMathGrid::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
{ {
// let the cells adjust themselves // let the cells adjust themselves
//InsetMathNest::metrics(mi);
for (idx_type i = 0; i < nargs(); ++i) for (idx_type i = 0; i < nargs(); ++i)
if (cellinfo_[i].multi_ != CELL_PART_OF_MULTICOLUMN)
cell(i).metricsT(mi, dim); cell(i).metricsT(mi, dim);
// compute absolute sizes of vertical structure // compute absolute sizes of vertical structure
@ -563,12 +677,15 @@ void InsetMathGrid::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
int asc = 0; int asc = 0;
int desc = 0; int desc = 0;
for (col_type col = 0; col < ncols(); ++col) { for (col_type col = 0; col < ncols(); ++col) {
//MathData const & c = cell(index(row, col)); idx_type const i = index(row, col);
if (cellinfo_[i].multi_ != CELL_PART_OF_MULTICOLUMN) {
//MathData const & c = cell(i);
// FIXME: BROKEN! // FIXME: BROKEN!
Dimension dimc; Dimension dimc;
asc = max(asc, dimc.ascent()); asc = max(asc, dimc.ascent());
desc = max(desc, dimc.descent()); desc = max(desc, dimc.descent());
} }
}
rowinfo_[row].ascent_ = asc; rowinfo_[row].ascent_ = asc;
rowinfo_[row].descent_ = desc; rowinfo_[row].descent_ = desc;
} }
@ -609,7 +726,9 @@ void InsetMathGrid::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
int wid = 0; int wid = 0;
for (row_type row = 0; row < nrows(); ++row) { for (row_type row = 0; row < nrows(); ++row) {
// FIXME: BROKEN! // 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; colinfo_[col].width_ = wid;
} }
@ -647,6 +766,7 @@ void InsetMathGrid::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
void InsetMathGrid::drawT(TextPainter & /*pain*/, int /*x*/, int /*y*/) const void InsetMathGrid::drawT(TextPainter & /*pain*/, int /*x*/, int /*y*/) const
{ {
// for (idx_type idx = 0; idx < nargs(); ++idx) // for (idx_type idx = 0; idx < nargs(); ++idx)
// if (cellinfo_[idx].multi_ != CELL_PART_OF_MULTICOLUMN)
// cell(idx).drawT(pain, x + cellXOffset(idx), y + cellYOffset(idx)); // cell(idx).drawT(pain, x + cellXOffset(idx), y + cellYOffset(idx));
} }
@ -655,6 +775,7 @@ void InsetMathGrid::updateBuffer(ParIterator const & it, UpdateType utype)
{ {
// pass down // pass down
for (idx_type idx = 0; idx < nargs(); ++idx) for (idx_type idx = 0; idx < nargs(); ++idx)
if (cellinfo_[idx].multi_ != CELL_PART_OF_MULTICOLUMN)
cell(idx).updateBuffer(it, utype); 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 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); col_type c = col(idx);
int x = colinfo_[c].offset_; int x = colinfo_[c].offset_;
char align = displayColAlign(c, row(idx)); char align = displayColAlign(idx);
Dimension const & celldim = cell(idx).dimension(bv); Dimension const & celldim = cell(idx).dimension(bv);
if (align == 'r' || align == 'R') if (align == 'r' || align == 'R')
x += colinfo_[c].width_ - celldim.wid; x += cellWidth(idx) - celldim.wid;
if (align == 'c' || align == 'C') if (align == 'c' || align == 'C')
x += (colinfo_[c].width_ - celldim.wid) / 2; x += (cellWidth(idx) - celldim.wid) / 2;
return x; 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 bool InsetMathGrid::idxUpDown(Cursor & cur, bool up) const
{ {
if (up) { if (up) {
@ -830,6 +974,11 @@ bool InsetMathGrid::idxUpDown(Cursor & cur, bool up) const
return false; return false;
cur.idx() += ncols(); 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())); cur.pos() = cur.cell().x2pos(&cur.bv(), cur.x_target() - cur.cell().xo(cur.bv()));
return true; return true;
} }
@ -841,6 +990,11 @@ bool InsetMathGrid::idxBackward(Cursor & cur) const
if (cur.col() == 0) if (cur.col() == 0)
return false; return false;
--cur.idx(); --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(); cur.pos() = cur.lastpos();
return true; return true;
} }
@ -852,6 +1006,13 @@ bool InsetMathGrid::idxForward(Cursor & cur) const
if (cur.col() + 1 == ncols()) if (cur.col() + 1 == ncols())
return false; return false;
++cur.idx(); ++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; cur.pos() = 0;
return true; return true;
} }
@ -869,6 +1030,11 @@ bool InsetMathGrid::idxFirst(Cursor & cur) const
default: default:
cur.idx() = ((nrows() - 1) / 2) * ncols(); 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; cur.pos() = 0;
return true; return true;
} }
@ -886,6 +1052,11 @@ bool InsetMathGrid::idxLast(Cursor & cur) const
default: default:
cur.idx() = ((nrows() - 1) / 2 + 1) * ncols() - 1; 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(); cur.pos() = cur.lastpos();
return true; return true;
} }
@ -936,9 +1107,15 @@ void InsetMathGrid::idxGlue(idx_type idx)
delRow(row(idx) + 1); delRow(row(idx) + 1);
} }
} else { } 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) 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(); 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 void InsetMathGrid::normalize(NormalStream & os) const
{ {
os << "[grid "; os << "[grid ";
for (row_type row = 0; row < nrows(); ++row) { for (row_type row = 0; row < nrows(); ++row) {
os << "[row "; os << "[row ";
for (col_type col = 0; col < ncols(); ++col) for (col_type col = 0; col < ncols(); ++col) {
os << "[cell " << cell(index(row, 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 << ']';
} }
os << ']'; os << ']';
@ -992,10 +1181,17 @@ void InsetMathGrid::mathmlize(MathStream & os) const
if (havetable) if (havetable)
os << MTag("mtr"); os << MTag("mtr");
for (col_type col = 0; col < ncols(); ++col) { for (col_type col = 0; col < ncols(); ++col) {
os << MTag(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 << cell(index(row, col));
os << ETag(celltag); os << ETag(celltag);
} }
}
if (havetable) if (havetable)
os << ETag("mtr"); os << ETag("mtr");
} }
@ -1017,10 +1213,17 @@ void InsetMathGrid::htmlize(HtmlStream & os, string attrib) const
for (row_type row = 0; row < nrows(); ++row) { for (row_type row = 0; row < nrows(); ++row) {
os << MTag("tr"); os << MTag("tr");
for (col_type col = 0; col < ncols(); ++col) { for (col_type col = 0; col < ncols(); ++col) {
os << MTag("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 << cell(index(row, col));
os << ETag("td"); os << ETag("td");
} }
}
os << ETag("tr"); os << ETag("tr");
} }
os << ETag("table"); os << ETag("table");
@ -1052,7 +1255,10 @@ void InsetMathGrid::write(WriteStream & os,
bool emptyline = true; bool emptyline = true;
bool last_eoln = true; bool last_eoln = true;
for (col_type col = beg_col; col < end_col; ++col) { 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) if (!empty_cell)
last_eoln = false; last_eoln = false;
if (!empty_cell || colinfo_[col + 1].lines_) { if (!empty_cell || colinfo_[col + 1].lines_) {
@ -1060,11 +1266,26 @@ void InsetMathGrid::write(WriteStream & os,
emptyline = false; emptyline = false;
} }
} }
for (col_type col = beg_col; col < lastcol; ++col) { for (col_type col = beg_col; col < lastcol;) {
os << cell(index(row, col)); 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()) if (os.pendingBrace())
ModeSpecifier specifier(os, TEXT_MODE); ModeSpecifier specifier(os, TEXT_MODE);
if (cellinfo_[idx].multi_ == CELL_BEGIN_OF_MULTICOLUMN)
os << '}';
os << eocString(col, lastcol); os << eocString(col, lastcol);
col += nccols;
} }
eol = eolString(row, os.fragile(), os.latex(), last_eoln); eol = eolString(row, os.fragile(), os.latex(), last_eoln);
os << eol; os << eol;
@ -1125,11 +1346,30 @@ void InsetMathGrid::splitCell(Cursor & cur)
ar.erase(0, cur.pos()); ar.erase(0, cur.pos());
cur.cell().erase(cur.pos(), cur.lastpos()); cur.cell().erase(cur.pos(), cur.lastpos());
++cur.idx(); ++cur.idx();
while (cur.idx() << nargs() &&
cellinfo_[cur.idx()].multi_ == CELL_BEGIN_OF_MULTICOLUMN)
++cur.idx();
cur.pos() = 0; cur.pos() = 0;
cur.cell().insert(0, ar); 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) void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd)
{ {
//lyxerr << "*** InsetMathGrid: request: " << cmd << endl; //lyxerr << "*** InsetMathGrid: request: " << cmd << endl;

View File

@ -25,13 +25,23 @@ namespace lyx {
class InsetMathGrid : public InsetMathNest { class InsetMathGrid : public InsetMathNest {
public: 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 /// additional per-cell information
class CellInfo { class CellInfo {
public: public:
/// ///
CellInfo(); CellInfo();
/// a dummy cell before a multicolumn cell /// multicolumn flag
int dummy_; Multicolumn multi_;
/// special multi colums alignment /// special multi colums alignment
docstring align_; docstring align_;
/// these should be a per-cell property, but ok to have it here /// these should be a per-cell property, but ok to have it here
@ -79,7 +89,7 @@ public:
mutable int offset_; mutable int offset_;
/// how many lines to the left of this column? /// how many lines to the left of this column?
unsigned int lines_; unsigned int lines_;
/// additional amount to be skipped when drawing /// additional amount to the right to be skipped when drawing
int skip_; int skip_;
/// Special alignment. /// Special alignment.
/// This does also contain align_ and lines_ if it is nonempty. /// This does also contain align_ and lines_ if it is nonempty.
@ -152,6 +162,8 @@ public:
col_type col(idx_type idx) const; col_type col(idx_type idx) const;
/// ///
row_type row(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; bool idxUpDown(Cursor &, bool up) const;
@ -237,6 +249,8 @@ protected:
int cellXOffset(BufferView const &, idx_type idx) const; int cellXOffset(BufferView const &, idx_type idx) const;
/// returns y offset of cell compared to inset /// returns y offset of cell compared to inset
int cellYOffset(idx_type idx) const; 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 /// returns proper 'end of line' code for LaTeX
virtual docstring eolString(row_type row, bool fragile, bool latex, virtual docstring eolString(row_type row, bool fragile, bool latex,
bool last_eoln) const; bool last_eoln) const;
@ -244,9 +258,9 @@ protected:
virtual docstring eocString(col_type col, col_type lastcol) const; virtual docstring eocString(col_type col, col_type lastcol) const;
/// splits cells and shifts right part to the next cell /// splits cells and shifts right part to the next cell
void splitCell(Cursor & cur); 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! /// 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. /// row info.

View 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 (type_ == hullMultline) {
if (row == 0) row_type const r = row(idx);
if (r == 0)
return 'l'; return 'l';
if (row == nrows() - 1) if (r == nrows() - 1)
return 'r'; return 'r';
} }
return InsetMathGrid::displayColAlign(col, row); return InsetMathGrid::displayColAlign(idx);
} }

View File

@ -111,8 +111,7 @@ public:
/// ///
char defaultColAlign(col_type col); char defaultColAlign(col_type col);
/// ///
/// char displayColAlign(idx_type idx) const;
char displayColAlign(col_type col, row_type row) const;
/// ///
bool idxFirst(Cursor &) const; bool idxFirst(Cursor &) const;
/// ///

View File

@ -99,8 +99,16 @@ void InsetMathMatrix::mathmlize(MathStream & os) const
os << MTag("mtable"); os << MTag("mtable");
for (row_type row = 0; row < nrows(); ++row) { for (row_type row = 0; row < nrows(); ++row) {
os << MTag("mtr"); os << MTag("mtr");
for (col_type col = 0; col < ncols(); ++col) for (col_type col = 0; col < ncols(); ++col) {
os << MTag("mtd") << cell(index(row, col)) << ETag("mtd"); 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("mtr");
} }
os << ETag("mtable"); os << ETag("mtable");
@ -124,8 +132,18 @@ void InsetMathMatrix::htmlize(HtmlStream & os) const
os << MTag("tr") << '\n'; os << MTag("tr") << '\n';
if (row == 0) if (row == 0)
os << MTag("td", lattrib) << ETag("td") << '\n'; os << MTag("td", lattrib) << ETag("td") << '\n';
for (col_type col = 0; col < ncols(); ++col) for (col_type col = 0; col < ncols(); ++col) {
os << MTag("td") << cell(index(row, col)) << ETag("td") << '\n'; 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) if (row == 0)
os << MTag("td", rattrib) << ETag("td") << '\n'; os << MTag("td", rattrib) << ETag("td") << '\n';
os << ETag("tr") << '\n'; os << ETag("tr") << '\n';

View File

@ -64,6 +64,7 @@ following hack as starting point to write some macros:
#include "InsetMathString.h" #include "InsetMathString.h"
#include "InsetMathTabular.h" #include "InsetMathTabular.h"
#include "MathMacroTemplate.h" #include "MathMacroTemplate.h"
#include "MathExtern.h"
#include "MathFactory.h" #include "MathFactory.h"
#include "MathMacroArgument.h" #include "MathMacroArgument.h"
#include "MathSupport.h" #include "MathSupport.h"
@ -1374,7 +1375,6 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
} }
} }
#if 0
else if (t.cs() == "multicolumn") { else if (t.cs() == "multicolumn") {
// extract column count and insert dummy cells // extract column count and insert dummy cells
MathData count; MathData count;
@ -1382,30 +1382,30 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
int cols = 1; int cols = 1;
if (!extractNumber(count, cols)) { if (!extractNumber(count, cols)) {
success_ = false; 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 // resize the table if necessary
size_t first = 0;
for (int i = 0; i < cols; ++i) { for (int i = 0; i < cols; ++i) {
if (addCol(grid, cellcol)) { if (addCol(grid, cellcol)) {
cell = &grid.cell(grid.index( size_t const idx = grid.index(cellrow, cellcol);
cellrow, cellcol)); if (i == 0)
// mark this as dummy first = idx;
grid.cellinfo(grid.index( grid.cellinfo(idx).multi_ =
cellrow, cellcol)).dummy_ = true; 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 // read special alignment
MathData align; grid.cellinfo(first).align_ = parse_verbatim_item();
parse(align, FLAG_ITEM, mode);
//grid.cellinfo(grid.index(cellrow, cellcol)).align_ = extractString(align);
// parse the remaining contents into the "real" cell // parse the remaining contents into the "real" cell
parse(*cell, FLAG_ITEM, mode); parse(*cell, FLAG_ITEM, mode);
} }
#endif
else if (t.cs() == "limits" || t.cs() == "nolimits") { else if (t.cs() == "limits" || t.cs() == "nolimits") {
CatCode const cat = nextToken().cat(); CatCode const cat = nextToken().cat();

View File

@ -1,5 +1,5 @@
#LyX file created by tex2lyx 2.2 #LyX file created by tex2lyx 2.2
\lyxformat 491 \lyxformat 493
\begin_document \begin_document
\begin_header \begin_header
\origin roundtrip \origin roundtrip

View File

@ -1,5 +1,5 @@
#LyX file created by tex2lyx 2.2 #LyX file created by tex2lyx 2.2
\lyxformat 491 \lyxformat 493
\begin_document \begin_document
\begin_header \begin_header
\origin roundtrip \origin roundtrip

View File

@ -1,5 +1,5 @@
#LyX file created by tex2lyx 2.2 #LyX file created by tex2lyx 2.2
\lyxformat 491 \lyxformat 493
\begin_document \begin_document
\begin_header \begin_header
\origin roundtrip \origin roundtrip

View File

@ -1,5 +1,5 @@
#LyX file created by tex2lyx 2.2 #LyX file created by tex2lyx 2.2
\lyxformat 491 \lyxformat 493
\begin_document \begin_document
\begin_header \begin_header
\origin roundtrip \origin roundtrip

View File

@ -1,5 +1,5 @@
#LyX file created by tex2lyx 2.2 #LyX file created by tex2lyx 2.2
\lyxformat 491 \lyxformat 493
\begin_document \begin_document
\begin_header \begin_header
\origin roundtrip \origin roundtrip

View File

@ -1,5 +1,5 @@
#LyX file created by tex2lyx 2.2 #LyX file created by tex2lyx 2.2
\lyxformat 491 \lyxformat 493
\begin_document \begin_document
\begin_header \begin_header
\origin roundtrip \origin roundtrip

View File

@ -6937,6 +6937,19 @@ $
. .
\end_layout \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 \begin_layout Section
Lists/Indices Lists/Indices
\end_layout \end_layout

View File

@ -691,6 +691,13 @@ Let $f:\left[ a,b\right] \rightarrow%
%EndExpansion %EndExpansion
$. $.
\[
\begin{array}{rclccc}
1 + 2 & = & 3
\multicolumn{3}{c}{4 < 5 \leq 6 }
\end{array}
\]
\section{Lists/Indices} \section{Lists/Indices}

View File

@ -1,5 +1,5 @@
#LyX file created by tex2lyx 2.2 #LyX file created by tex2lyx 2.2
\lyxformat 491 \lyxformat 493
\begin_document \begin_document
\begin_header \begin_header
\origin roundtrip \origin roundtrip

View File

@ -1,5 +1,5 @@
#LyX file created by tex2lyx 2.2 #LyX file created by tex2lyx 2.2
\lyxformat 491 \lyxformat 493
\begin_document \begin_document
\begin_header \begin_header
\origin roundtrip \origin roundtrip

View File

@ -1,5 +1,5 @@
#LyX file created by tex2lyx 2.2 #LyX file created by tex2lyx 2.2
\lyxformat 491 \lyxformat 493
\begin_document \begin_document
\begin_header \begin_header
\origin roundtrip \origin roundtrip

View File

@ -1,5 +1,5 @@
#LyX file created by tex2lyx 2.2 #LyX file created by tex2lyx 2.2
\lyxformat 491 \lyxformat 493
\begin_document \begin_document
\begin_header \begin_header
\origin roundtrip \origin roundtrip

View File

@ -1,5 +1,5 @@
#LyX file created by tex2lyx 2.2 #LyX file created by tex2lyx 2.2
\lyxformat 491 \lyxformat 493
\begin_document \begin_document
\begin_header \begin_header
\origin roundtrip \origin roundtrip

View File

@ -1,5 +1,5 @@
#LyX file created by tex2lyx 2.2 #LyX file created by tex2lyx 2.2
\lyxformat 491 \lyxformat 493
\begin_document \begin_document
\begin_header \begin_header
\origin roundtrip \origin roundtrip

View File

@ -32,8 +32,8 @@ extern char const * const lyx_version_info;
// Do not remove the comment below, so we get merge conflict in // Do not remove the comment below, so we get merge conflict in
// independent branches. Instead add your own. // independent branches. Instead add your own.
#define LYX_FORMAT_LYX 492 // uwestoehr: support for colorboxes #define LYX_FORMAT_LYX 493 // gb math multicolumn
#define LYX_FORMAT_TEX2LYX 492 #define LYX_FORMAT_TEX2LYX 493
#if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX
#ifndef _MSC_VER #ifndef _MSC_VER