X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2FInsetMathGrid.cpp;h=85511e6ddfad4af59d26c1dd50bd3af42506430e;hb=593bfe248a15be99bfce7e12cde6c59c92951f5f;hp=ed77f71f851be12a46fa959b5b0f9c7e81d0defc;hpb=612109bb6e5e61ba3c3693696f329eac3a288b5b;p=lyx.git diff --git a/src/mathed/InsetMathGrid.cpp b/src/mathed/InsetMathGrid.cpp index ed77f71f85..85511e6ddf 100644 --- a/src/mathed/InsetMathGrid.cpp +++ b/src/mathed/InsetMathGrid.cpp @@ -20,21 +20,23 @@ #include "MetricsInfo.h" #include "Buffer.h" +#include "BufferParams.h" #include "BufferView.h" -#include "CutAndPaste.h" -#include "FuncStatus.h" +#include "CoordCache.h" #include "Cursor.h" +#include "CutAndPaste.h" #include "FuncRequest.h" +#include "FuncStatus.h" +#include "LaTeXFeatures.h" +#include "TexRow.h" #include "frontends/Clipboard.h" -#include "frontends/FontMetrics.h" #include "frontends/Painter.h" #include "support/debug.h" #include "support/docstream.h" #include "support/gettext.h" #include "support/lstrings.h" - #include "support/lassert.h" #include @@ -76,42 +78,15 @@ static void resetGrid(InsetMathGrid & grid) } - ////////////////////////////////////////////////////////////// -InsetMathGrid::CellInfo::CellInfo() - : dummy_(false) -{} - - - -////////////////////////////////////////////////////////////// - - -InsetMathGrid::RowInfo::RowInfo() - : lines_(0), skip_(0), allow_newpage_(true) -{} - - - int InsetMathGrid::RowInfo::skipPixels(MetricsInfo const & mi) const { - frontend::FontMetrics const & fm = theFontMetrics(mi.base.font); - return crskip_.inPixels(mi.base.textwidth, - fm.width(char_type('M'))); + return mi.base.inPixels(crskip); } - -////////////////////////////////////////////////////////////// - - -InsetMathGrid::ColInfo::ColInfo() - : align_('c'), lines_(0) -{} - - ////////////////////////////////////////////////////////////// @@ -157,7 +132,7 @@ Inset * InsetMathGrid::clone() const } -InsetMath::idx_type InsetMathGrid::index(row_type row, col_type col) const +idx_type InsetMathGrid::index(row_type row, col_type col) const { return col + ncols() * row; } @@ -170,21 +145,23 @@ void InsetMathGrid::setDefaults() //if (nrows() <= 0) // lyxerr << "positive number of rows expected" << endl; for (col_type col = 0; col < ncols(); ++col) { - colinfo_[col].align_ = defaultColAlign(col); - colinfo_[col].skip_ = defaultColSpace(col); - colinfo_[col].special_.clear(); + colinfo_[col].align = defaultColAlign(col); + colinfo_[col].skip = defaultColSpace(col); + colinfo_[col].special.clear(); } + for (idx_type idx = 0; idx < nargs(); ++idx) + cellinfo_[idx].multi = CELL_NORMAL; } bool InsetMathGrid::interpretString(Cursor & cur, docstring const & str) { if (str == "\\hline") { - FuncRequest fr = FuncRequest(LFUN_INSET_MODIFY, "tabular add-hline-above"); + FuncRequest fr = FuncRequest(LFUN_TABULAR_FEATURE, "add-hline-above"); FuncStatus status; if (getStatus(cur, fr, status)) { if (status.enabled()) { - rowinfo_[cur.row()].lines_++; + rowinfo_[cur.row()].lines++; return true; } } @@ -199,7 +176,7 @@ void InsetMathGrid::setHorizontalAlignments(docstring const & hh) for (docstring::const_iterator it = hh.begin(); it != hh.end(); ++it) { char_type c = *it; if (c == '|') { - colinfo_[col].lines_++; + colinfo_[col].lines++; } else if ((c == 'p' || c == 'm' || c == 'b'|| c == '!' || c == '@' || c == '>' || c == '<') && it + 1 != hh.end() && *(it + 1) == '{') { @@ -212,18 +189,18 @@ void InsetMathGrid::setHorizontalAlignments(docstring const & hh) // Only intercolumn stuff is allowed // in the last dummy column break; - colinfo_[col].align_ = 'l'; + colinfo_[col].align = 'l'; } else { // this is intercolumn stuff - if (colinfo_[col].special_.empty()) + if (colinfo_[col].special.empty()) // Overtake possible lines - colinfo_[col].special_ = docstring(colinfo_[col].lines_, '|'); + colinfo_[col].special = docstring(colinfo_[col].lines, '|'); } int brace_open = 0; int brace_close = 0; while (it != hh.end()) { c = *it; - colinfo_[col].special_ += c; + colinfo_[col].special += c; if (c == '{') ++brace_open; else if (c == '}') @@ -234,32 +211,32 @@ void InsetMathGrid::setHorizontalAlignments(docstring const & hh) } --it; if (newcolumn) { - colinfo_[col].lines_ = count( - colinfo_[col].special_.begin(), - colinfo_[col].special_.end(), '|'); + colinfo_[col].lines = count( + colinfo_[col].special.begin(), + colinfo_[col].special.end(), '|'); LYXERR(Debug::MATHED, "special column separator: `" - << to_utf8(colinfo_[col].special_) << '\''); + << to_utf8(colinfo_[col].special) << '\''); ++col; - colinfo_[col].lines_ = 0; - colinfo_[col].special_.clear(); + colinfo_[col].lines = 0; + colinfo_[col].special.clear(); } } else if (col >= ncols()) { // Only intercolumn stuff is allowed in the last // dummy column break; } else if (c == 'c' || c == 'l' || c == 'r') { - colinfo_[col].align_ = static_cast(c); - if (!colinfo_[col].special_.empty()) { - colinfo_[col].special_ += c; - colinfo_[col].lines_ = count( - colinfo_[col].special_.begin(), - colinfo_[col].special_.end(), '|'); + colinfo_[col].align = static_cast(c); + if (!colinfo_[col].special.empty()) { + colinfo_[col].special += c; + colinfo_[col].lines = count( + colinfo_[col].special.begin(), + colinfo_[col].special.end(), '|'); LYXERR(Debug::MATHED, "special column separator: `" - << to_utf8(colinfo_[col].special_) << '\''); + << to_utf8(colinfo_[col].special) << '\''); } ++col; - colinfo_[col].lines_ = 0; - colinfo_[col].special_.clear(); + colinfo_[col].lines = 0; + colinfo_[col].special.clear(); } else { lyxerr << "unknown column separator: '" << c << "'" << endl; } @@ -270,17 +247,17 @@ void InsetMathGrid::setHorizontalAlignments(docstring const & hh) if (n > ncols()) n = ncols(); for (col_type col = 0; col < n; ++col) - colinfo_[col].align_ = hh[col]; + colinfo_[col].align = hh[col]; */ } -InsetMathGrid::col_type InsetMathGrid::guessColumns(docstring const & hh) +col_type InsetMathGrid::guessColumns(docstring const & hh) { col_type col = 0; - for (docstring::const_iterator it = hh.begin(); it != hh.end(); ++it) - if (*it == 'c' || *it == 'l' || *it == 'r'|| - *it == 'p' || *it == 'm' || *it == 'b') + for (char_type const c : hh) + if (c == 'c' || c == 'l' || c == 'r'|| + c == 'p' || c == 'm' || c == 'b') ++col; // let's have at least one column, even if we did not recognize its // alignment @@ -292,9 +269,9 @@ InsetMathGrid::col_type InsetMathGrid::guessColumns(docstring const & hh) void InsetMathGrid::setHorizontalAlignment(char h, col_type col) { - colinfo_[col].align_ = h; - if (!colinfo_[col].special_.empty()) { - char_type & c = colinfo_[col].special_[colinfo_[col].special_.size() - 1]; + colinfo_[col].align = h; + if (!colinfo_[col].special.empty()) { + char_type & c = colinfo_[col].special[colinfo_[col].special.size() - 1]; if (c == 'l' || c == 'c' || c == 'r') c = h; } @@ -304,7 +281,7 @@ void InsetMathGrid::setHorizontalAlignment(char h, col_type col) char InsetMathGrid::horizontalAlignment(col_type col) const { - return colinfo_[col].align_; + return colinfo_[col].align; } @@ -312,15 +289,15 @@ docstring InsetMathGrid::horizontalAlignments() const { docstring res; for (col_type col = 0; col < ncols(); ++col) { - if (colinfo_[col].special_.empty()) { - res += docstring(colinfo_[col].lines_, '|'); - res += colinfo_[col].align_; + if (colinfo_[col].special.empty()) { + res += docstring(colinfo_[col].lines, '|'); + res += colinfo_[col].align; } else - res += colinfo_[col].special_; + res += colinfo_[col].special; } - if (colinfo_[ncols()].special_.empty()) - return res + docstring(colinfo_[ncols()].lines_, '|'); - return res + colinfo_[ncols()].special_; + if (colinfo_[ncols()].special.empty()) + return res + docstring(colinfo_[ncols()].lines, '|'); + return res + colinfo_[ncols()].special; } @@ -336,46 +313,69 @@ char InsetMathGrid::verticalAlignment() const } -InsetMathGrid::col_type InsetMathGrid::ncols() const +col_type InsetMathGrid::ncols() const { return colinfo_.size() - 1; } -InsetMathGrid::row_type InsetMathGrid::nrows() const +row_type InsetMathGrid::nrows() const { return rowinfo_.size() - 1; } -InsetMathGrid::col_type InsetMathGrid::col(idx_type idx) const +col_type InsetMathGrid::col(idx_type idx) const { return idx % ncols(); } -InsetMathGrid::row_type InsetMathGrid::row(idx_type idx) const +row_type InsetMathGrid::row(idx_type idx) const { return idx / ncols(); } +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; + rowinfo_[row].crskip = crskip; } Length InsetMathGrid::vcrskip(row_type row) const { - return rowinfo_[row].crskip_; + return rowinfo_[row].crskip; } 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; + // the 'false' is to make sure that the cell is tall enough + cell(i).metrics(mi, dimc, false); + } + } BufferView & bv = *mi.base.bv; @@ -384,27 +384,29 @@ 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; + rowinfo_[row].ascent = asc; + rowinfo_[row].descent = desc; } - rowinfo_[0].ascent_ += hlinesep() * rowinfo_[0].lines_; - rowinfo_[nrows()].ascent_ = 0; - rowinfo_[nrows()].descent_ = 0; + rowinfo_[nrows()].ascent = 0; + rowinfo_[nrows()].descent = 0; // compute vertical offsets - rowinfo_[0].offset_ = 0; + rowinfo_[0].offset[&bv] = 0; for (row_type row = 1; row <= nrows(); ++row) { - rowinfo_[row].offset_ = - rowinfo_[row - 1].offset_ + - rowinfo_[row - 1].descent_ + + rowinfo_[row].offset[&bv] = + rowinfo_[row - 1].offset[&bv] + + rowinfo_[row - 1].descent + rowinfo_[row - 1].skipPixels(mi) + rowsep() + - rowinfo_[row].lines_ * hlinesep() + - rowinfo_[row].ascent_; + rowinfo_[row].lines * hlinesep() + + rowinfo_[row].ascent; } // adjust vertical offset @@ -414,59 +416,110 @@ void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const h = 0; break; case 'b': - h = rowinfo_[nrows() - 1].offset_; + h = rowinfo_[nrows() - 1].offset[&bv]; break; default: - h = rowinfo_[nrows() - 1].offset_ / 2; + h = rowinfo_[nrows() - 1].offset[&bv] / 2; } for (row_type row = 0; row <= nrows(); ++row) - rowinfo_[row].offset_ -= h; + rowinfo_[row].offset[&bv] -= 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 > 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); - colinfo_[col].width_ = 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::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; + colinfo_[ncols()].width = 0; // compute horizontal offsets - colinfo_[0].offset_ = border(); + colinfo_[0].offset = border() + colinfo_[0].lines * vlinesep(); for (col_type col = 1; col <= ncols(); ++col) { - colinfo_[col].offset_ = - colinfo_[col - 1].offset_ + - colinfo_[col - 1].width_ + - colinfo_[col - 1].skip_ + + colinfo_[col].offset = + colinfo_[col - 1].offset + + colinfo_[col - 1].width + + displayColSpace(col - 1) + colsep() + - 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 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::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 + + displayColSpace(last) + + 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_ - + vlinesep() * colinfo_[ncols()].lines_ + dim.wid = colinfo_[ncols() - 1].offset + + colinfo_[ncols() - 1].width + + vlinesep() * colinfo_[ncols()].lines + border(); - dim.asc = - rowinfo_[0].offset_ - + rowinfo_[0].ascent_ - + hlinesep() * rowinfo_[0].lines_ + dim.asc = - rowinfo_[0].offset[&bv] + + rowinfo_[0].ascent + + hlinesep() * rowinfo_[0].lines + border(); - dim.des = rowinfo_[nrows() - 1].offset_ - + rowinfo_[nrows() - 1].descent_ - + hlinesep() * rowinfo_[nrows()].lines_ - + border(); + dim.des = rowinfo_[nrows() - 1].offset[&bv] + + rowinfo_[nrows() - 1].descent + + hlinesep() * rowinfo_[nrows()].lines + + border() + 1; /* // Increase ws_[i] for 'R' columns (except the first one) for (int i = 1; i < nc_; ++i) - if (align_[i] == 'R') + if (align[i] == 'R') ws_[i] += 10 * df_width; // Increase ws_[i] for 'C' column - if (align_[0] == 'C') + if (align[0] == 'C') if (ws_[0] < 7 * workwidth / 8) ws_[0] = 7 * workwidth / 8; @@ -481,7 +534,7 @@ void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const cxrow->setTab(i, df_width); isvoid = true; } - switch (align_[i]) { + switch (align[i]) { case 'l': lf = 0; break; @@ -510,84 +563,115 @@ void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const cxrow->setBaseline(cxrow->getBaseline() - ascent); } */ - metricsMarkers2(dim); - // Cache the inset dimension. - setDimCache(mi, dim); + dim.wid += leftMargin() + rightMargin(); } -void InsetMathGrid::draw(PainterInfo & pi, int x, int y) const +int InsetMathGrid::vLineHOffset(col_type col, unsigned int line) const { - drawWithMargin(pi, x, y, 1, 1); + if (col < ncols()) + return leftMargin() + colinfo_[col].offset + - (colinfo_[col].lines - line - 1) * vlinesep() + - vlinesep()/2 - colsep()/2; + else { + LASSERT(col == ncols(), return 0); + return leftMargin() + colinfo_[col-1].offset + colinfo_[col-1].width + + line * vlinesep() + + vlinesep()/2 + colsep()/2; + } } -void InsetMathGrid::drawWithMargin(PainterInfo & pi, int x, int y, - int lmargin, int rmargin) const +int InsetMathGrid::hLineVOffset(BufferView const & bv, row_type row, + unsigned int line) const { - Dimension const dim = dimension(*pi.base.bv); - BufferView const & bv = *pi.base.bv; + return rowinfo_[row].offset[&bv] + - rowinfo_[row].ascent + - line * hlinesep() + - hlinesep()/2 - rowsep()/2; +} - for (idx_type idx = 0; idx < nargs(); ++idx) - cell(idx).draw(pi, x + lmargin + cellXOffset(bv, idx), - y + cellYOffset(idx)); - for (row_type row = 0; row <= nrows(); ++row) - 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; - pi.pain.line(x + lmargin + 1, yy, - x + dim.width() - rmargin - 1, yy, - Color_foreground); +void InsetMathGrid::draw(PainterInfo & pi, int x, int y) const +{ + BufferView const & bv = *pi.base.bv; + + for (idx_type idx = 0; idx < nargs(); ++idx) { + if (cellinfo_[idx].multi != CELL_PART_OF_MULTICOLUMN) { + cell(idx).draw(pi, + x + leftMargin() + cellXOffset(bv, idx), + y + cellYOffset(bv, idx)); + + row_type r = row(idx); + int const yy1 = y + hLineVOffset(bv, r, 0); + int const yy2 = y + hLineVOffset(bv, r + 1, rowinfo_[r + 1].lines - 1); + auto draw_left_borders = [&](col_type c) { + for (unsigned int i = 0; i < colinfo_[c].lines; ++i) { + int const xx = x + vLineHOffset(c, i); + pi.pain.line(xx, yy1, xx, yy2, Color_foreground); + } + }; + col_type c = col(idx); + // Draw inner left borders cell-by-cell because of multicolumns + draw_left_borders(c); + // Draw the right border (only once) + if (c == 0) + draw_left_borders(ncols()); } + } - for (col_type col = 0; col <= ncols(); ++col) - for (unsigned int i = 0; i < colinfo_[col].lines_; ++i) { - int xx = x + lmargin + colinfo_[col].offset_ - - i * vlinesep() - vlinesep()/2 - colsep()/2; - pi.pain.line(xx, y - dim.ascent() + 1, - xx, y + dim.descent() - 1, - Color_foreground); + // Draw horizontal borders + for (row_type r = 0; r <= nrows(); ++r) { + int const xx1 = x + vLineHOffset(0, 0); + int const xx2 = x + vLineHOffset(ncols(), colinfo_[ncols()].lines - 1); + for (unsigned int i = 0; i < rowinfo_[r].lines; ++i) { + int const yy = y + hLineVOffset(bv, r, i); + pi.pain.line(xx1, yy, xx2, yy, Color_foreground); } - drawMarkers2(pi, x, y); + } } -void InsetMathGrid::metricsT(TextMetricsInfo const & mi, Dimension & dim) const +void InsetMathGrid::metricsT(TextMetricsInfo const & /*mi*/, Dimension & /*dim*/) const { +// FIXME: this does not compile anymore with offset being a map +// It is not worth fixing it at this point since the code is basically dead. +#if 0 // 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; + rowinfo_[row].ascent = asc; + rowinfo_[row].descent = desc; } - //rowinfo_[0].ascent_ += hlinesep() * rowinfo_[0].lines_; - rowinfo_[nrows()].ascent_ = 0; - rowinfo_[nrows()].descent_ = 0; + rowinfo_[nrows()].ascent = 0; + rowinfo_[nrows()].descent = 0; // compute vertical offsets - rowinfo_[0].offset_ = 0; + rowinfo_[0].offset[&bv] = 0; for (row_type row = 1; row <= nrows(); ++row) { - rowinfo_[row].offset_ = - rowinfo_[row - 1].offset_ + - rowinfo_[row - 1].descent_ + + rowinfo_[row].offset[&bv] = + rowinfo_[row - 1].offset + + rowinfo_[row - 1].descent + //rowinfo_[row - 1].skipPixels(mi) + 1 + //rowsep() + - //rowinfo_[row].lines_ * hlinesep() + - rowinfo_[row].ascent_; + //rowinfo_[row].lines * hlinesep() + + rowinfo_[row].ascent; } // adjust vertical offset @@ -597,13 +681,13 @@ void InsetMathGrid::metricsT(TextMetricsInfo const & mi, Dimension & dim) const h = 0; break; case 'b': - h = rowinfo_[nrows() - 1].offset_; + h = rowinfo_[nrows() - 1].offset; break; default: - h = rowinfo_[nrows() - 1].offset_ / 2; + h = rowinfo_[nrows() - 1].offset / 2; } for (row_type row = 0; row <= nrows(); ++row) - rowinfo_[row].offset_ -= h; + rowinfo_[row].offset -= h; // compute absolute sizes of horizontal structure @@ -611,79 +695,84 @@ 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; + colinfo_[col].width = wid; } - colinfo_[ncols()].width_ = 0; + colinfo_[ncols()].width = 0; // compute horizontal offsets - colinfo_[0].offset_ = border(); + colinfo_[0].offset = border(); for (col_type col = 1; col <= ncols(); ++col) { - colinfo_[col].offset_ = - colinfo_[col - 1].offset_ + - colinfo_[col - 1].width_ + - colinfo_[col - 1].skip_ + + colinfo_[col].offset = + colinfo_[col - 1].offset + + colinfo_[col - 1].width + + displayColSpace(col - 1) + 1 ; //colsep() + - //colinfo_[col].lines_ * vlinesep(); + //colinfo_[col].lines * vlinesep(); } - dim.wid = colinfo_[ncols() - 1].offset_ - + colinfo_[ncols() - 1].width_ - //+ vlinesep() * colinfo_[ncols()].lines_ + dim.wid = colinfo_[ncols() - 1].offset + + colinfo_[ncols() - 1].width + //+ vlinesep() * colinfo_[ncols()].lines + 2; - dim.asc = -rowinfo_[0].offset_ - + rowinfo_[0].ascent_ - //+ hlinesep() * rowinfo_[0].lines_ + dim.asc = -rowinfo_[0].offset + + rowinfo_[0].ascent + //+ hlinesep() * rowinfo_[0].lines + 1; - dim.des = rowinfo_[nrows() - 1].offset_ - + rowinfo_[nrows() - 1].descent_ - //+ hlinesep() * rowinfo_[nrows()].lines_ + dim.des = rowinfo_[nrows() - 1].offset + + rowinfo_[nrows() - 1].descent + //+ hlinesep() * rowinfo_[nrows()].lines + 1; +#endif } 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)); } -void InsetMathGrid::updateBuffer(ParIterator const & it, UpdateType utype) +void InsetMathGrid::updateBuffer(ParIterator const & it, UpdateType utype, bool const deleted) { // 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, deleted); } -docstring InsetMathGrid::eolString(row_type row, bool fragile, +void InsetMathGrid::eol(TeXMathStream & os, row_type row, bool fragile, bool /*latex*/, bool last_eoln) const { docstring eol; - if (!rowinfo_[row].crskip_.zero()) - eol += '[' + from_utf8(rowinfo_[row].crskip_.asLatexString()) + ']'; - else if(!rowinfo_[row].allow_newpage_) + if (!rowinfo_[row].crskip.zero()) + eol += '[' + from_utf8(rowinfo_[row].crskip.asLatexString()) + ']'; + else if(!rowinfo_[row].allow_newpage) eol += '*'; // make sure an upcoming '[' does not break anything if (row + 1 < nrows()) { MathData const & c = cell(index(row + 1, 0)); - if (c.size() && c.front()->getChar() == '[') + if (!c.empty() && c.front()->getChar() == '[') //eol += "[0pt]"; eol += "{}"; } // only add \\ if necessary if (eol.empty() && row + 1 == nrows() && (nrows() == 1 || !last_eoln)) - return docstring(); + return; - return (fragile ? "\\protect\\\\" : "\\\\") + eol; + os << (fragile ? "\\protect\\\\" : "\\\\") << eol; } @@ -699,7 +788,7 @@ void InsetMathGrid::addRow(row_type row) { rowinfo_.insert(rowinfo_.begin() + row + 1, RowInfo()); cells_.insert - (cells_.begin() + (row + 1) * ncols(), ncols(), MathData()); + (cells_.begin() + (row + 1) * ncols(), ncols(), MathData(buffer_)); cellinfo_.insert (cellinfo_.begin() + (row + 1) * ncols(), ncols(), CellInfo()); } @@ -723,8 +812,11 @@ void InsetMathGrid::delRow(row_type row) void InsetMathGrid::copyRow(row_type row) { addRow(row); - for (col_type col = 0; col < ncols(); ++col) + for (col_type col = 0; col < ncols(); ++col) { cells_[(row + 1) * ncols() + col] = cells_[row * ncols() + col]; + // copying the cell does not set the buffer + cells_[(row + 1) * ncols() + col].setBuffer(*buffer_); + } } @@ -754,11 +846,13 @@ void InsetMathGrid::addCol(col_type newcol) = cellinfo_[row * nc + col]; } swap(cells_, new_cells); + // copying cells loses the buffer reference + setBuffer(*buffer_); swap(cellinfo_, new_cellinfo); ColInfo inf; - inf.skip_ = defaultColSpace(newcol); - inf.align_ = defaultColAlign(newcol); + inf.skip = defaultColSpace(newcol); + inf.align = defaultColAlign(newcol); colinfo_.insert(colinfo_.begin() + newcol, inf); } @@ -776,6 +870,8 @@ void InsetMathGrid::delCol(col_type col) tmpcellinfo.push_back(cellinfo_[i]); } swap(cells_, tmpcells); + // copying cells loses the buffer reference + setBuffer(*buffer_); swap(cellinfo_, tmpcellinfo); colinfo_.erase(colinfo_.begin() + col); @@ -785,8 +881,11 @@ void InsetMathGrid::delCol(col_type col) void InsetMathGrid::copyCol(col_type col) { addCol(col+1); - for (row_type row = 0; row < nrows(); ++row) + for (row_type row = 0; row < nrows(); ++row) { cells_[row * ncols() + col + 1] = cells_[row * ncols() + col]; + // copying the cell does not set the buffer + cells_[row * ncols() + col + 1].setBuffer(*buffer_); + } } @@ -803,21 +902,44 @@ 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)); + int x = colinfo_[c].offset; + 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; } -int InsetMathGrid::cellYOffset(idx_type idx) const +int InsetMathGrid::cellYOffset(BufferView const & bv, idx_type idx) const +{ + return rowinfo_[row(idx)].offset[&bv]; +} + + +int InsetMathGrid::cellWidth(idx_type idx) const { - return rowinfo_[row(idx)].offset_; + 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 + - displayColSpace(c2) + - colsep() + - colinfo_[c2].lines * vlinesep(); + } + case CELL_PART_OF_MULTICOLUMN: + return 0; + } + return 0; } @@ -832,7 +954,19 @@ bool InsetMathGrid::idxUpDown(Cursor & cur, bool up) const return false; cur.idx() += ncols(); } - cur.pos() = cur.cell().x2pos(&cur.bv(), cur.x_target() - cur.cell().xo(cur.bv())); + // 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(); + } + // FIXME: this is only a workaround to avoid a crash if the inset + // in not in coord cache. The best would be to force a FitCursor + // operation. + CoordCache::Arrays const & arraysCache = cur.bv().coordCache().arrays(); + if (arraysCache.has(&cur.cell())) + cur.pos() = cur.cell().x2pos(&cur.bv(), cur.x_target() - cur.cell().xo(cur.bv())); + else + cur.pos() = 0; return true; } @@ -843,6 +977,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; } @@ -854,42 +993,59 @@ 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; } -bool InsetMathGrid::idxFirst(Cursor & cur) const +idx_type InsetMathGrid::firstIdx() const { + size_type idx = 0; switch (v_align_) { case 't': - cur.idx() = 0; + //idx = 0; break; case 'b': - cur.idx() = (nrows() - 1) * ncols(); + idx = (nrows() - 1) * ncols(); break; default: - cur.idx() = ((nrows() - 1) / 2) * ncols(); + idx = ((nrows() - 1) / 2) * ncols(); } - cur.pos() = 0; - return true; + // If we are in a multicolumn cell, move to the "real" cell + while (cellinfo_[idx].multi == CELL_PART_OF_MULTICOLUMN) { + LASSERT(idx > 0, return 0); + --idx; + } + return idx; } -bool InsetMathGrid::idxLast(Cursor & cur) const +idx_type InsetMathGrid::lastIdx() const { + size_type idx = 0; switch (v_align_) { case 't': - cur.idx() = ncols() - 1; + idx = ncols() - 1; break; case 'b': - cur.idx() = nargs() - 1; + idx = nargs() - 1; break; default: - cur.idx() = ((nrows() - 1) / 2 + 1) * ncols() - 1; + idx = ((nrows() - 1) / 2 + 1) * ncols() - 1; } - cur.pos() = cur.lastpos(); - return true; + // If we are in a multicolumn cell, move to the "real" cell + while (cellinfo_[idx].multi == CELL_PART_OF_MULTICOLUMN) { + LASSERT(idx > 0, return false); + --idx; + } + return idx; } @@ -905,7 +1061,7 @@ bool InsetMathGrid::idxDelete(idx_type & idx) // try to delete entire sequence of ncols() empty cells if possible for (idx_type i = idx; i < idx + ncols(); ++i) - if (cell(i).size()) + if (!cell(i).empty()) return false; // move cells if necessary @@ -938,9 +1094,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(); } } @@ -970,45 +1132,64 @@ 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(ncellcols(i)) << ' ' + << cell(i) << ']'; + break; + case CELL_PART_OF_MULTICOLUMN: + break; + } + } os << ']'; } os << ']'; } -void InsetMathGrid::mathmlize(MathStream & os) const +void InsetMathGrid::mathmlize(MathMLStream & ms) const { bool const havetable = nrows() > 1 || ncols() > 1; if (havetable) - os << MTag("mtable"); + ms << MTag("mtable"); char const * const celltag = havetable ? "mtd" : "mrow"; for (row_type row = 0; row < nrows(); ++row) { if (havetable) - os << MTag("mtr"); + ms << 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 << '\''; + ms << MTag(celltag, attr.str()); + ms << cell(index(row, col)); + ms << ETag(celltag); + } } if (havetable) - os << ETag("mtr"); + ms << ETag("mtr"); } if (havetable) - os << ETag("mtable"); + ms << ETag("mtable"); } // FIXME XHTML // We need to do something about alignment here. -void InsetMathGrid::htmlize(HtmlStream & os, string attrib) const +void InsetMathGrid::htmlize(HtmlStream & os, string const & attrib) const { bool const havetable = nrows() > 1 || ncols() > 1; if (!havetable) { @@ -1019,9 +1200,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"); } @@ -1035,52 +1223,94 @@ void InsetMathGrid::htmlize(HtmlStream & os) const } -void InsetMathGrid::write(WriteStream & os) const +void InsetMathGrid::validate(LaTeXFeatures & features) const +{ + if (features.runparams().math_flavor == OutputParams::MathAsHTML + && (nrows() > 1 || ncols() > 1)) { + // CSS taken from InsetMathCases + features.addCSSSnippet( + "table.mathtable{display: inline-block; text-align: center; border: none;" + "border-left: thin solid black; vertical-align: middle; padding-left: 0.5ex;}\n" + "table.mathtable td {text-align: left; border: none;}"); + } + InsetMathNest::validate(features); +} + + +void InsetMathGrid::write(TeXMathStream & os) const { write(os, 0, 0, nrows(), ncols()); } -void InsetMathGrid::write(WriteStream & os, - row_type beg_row, col_type beg_col, - row_type end_row, col_type end_col) const +void InsetMathGrid::write(TeXMathStream & os, + row_type beg_row, col_type beg_col, + row_type end_row, col_type end_col) const { MathEnsurer ensurer(os, false); - docstring eol; + docstring eolstr; + // As of 2018 (with amendment in LaTeX 2021/06), + // \\ is a robust command and its protection + // is no longer necessary + bool const fragile = os.fragile() + && !LaTeXFeatures::isAvailable("LaTeX-2021/06/01"); for (row_type row = beg_row; row < end_row; ++row) { - os << verboseHLine(rowinfo_[row].lines_); + os << verboseHLine(rowinfo_[row].lines); // don't write & and empty cells at end of line, // unless there are vertical lines col_type lastcol = 0; 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(); - if (!empty_cell) + idx_type const idx = index(row, col); + bool const empty_cell = cell(idx).empty(); + if (last_eoln && (!empty_cell || cellinfo_[idx].multi != CELL_NORMAL)) last_eoln = false; - if (!empty_cell || colinfo_[col + 1].lines_) { + if (!empty_cell || cellinfo_[idx].multi != CELL_NORMAL || + colinfo_[col + 1].lines) { lastcol = col + 1; emptyline = false; } } - for (col_type col = beg_col; col < lastcol; ++col) { - os << cell(index(row, col)); + for (col_type col = beg_col; col < end_col;) { + int nccols = 1; + idx_type const idx = index(row, col); + TexRow::RowEntry const entry = TexRow::mathEntry(id(),idx); + os.texrow().start(entry); + if (col >= lastcol) { + ++col; + continue; + } + Changer dummy = os.changeRowEntry(entry); + 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); - os << eocString(col, lastcol); + if (cellinfo_[idx].multi == CELL_BEGIN_OF_MULTICOLUMN) + os << '}'; + os << eocString(col + nccols - 1, lastcol); + col += nccols; } - eol = eolString(row, os.fragile(), os.latex(), last_eoln); - os << eol; + eol(os, row, fragile, os.latex(), last_eoln); // append newline only if line wasn't completely empty // and the formula is not written on a single line - bool const empty = emptyline && eol.empty(); + bool const empty = emptyline && eolstr.empty(); if (!empty && nrows() > 1) os << "\n"; } // @TODO use end_row instead of nrows() ? - docstring const s = verboseHLine(rowinfo_[nrows()].lines_); + docstring const s = verboseHLine(rowinfo_[nrows()].lines); if (!s.empty()) { - if (eol.empty()) { - if (os.fragile()) + if (eolstr.empty()) { + if (fragile) os << "\\protect"; os << "\\\\"; } @@ -1127,11 +1357,35 @@ 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; +} + + +int InsetMathGrid::displayColSpace(col_type col) const +{ + return colinfo_[col].skip; +} + void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) { //lyxerr << "*** InsetMathGrid: request: " << cmd << endl; @@ -1142,7 +1396,7 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) switch (act) { // insert file functions - case LFUN_LINE_DELETE: + case LFUN_LINE_DELETE_FORWARD: cur.recordUndoInset(); //autocorrect_ = false; //macroModeClose(); @@ -1163,25 +1417,6 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) splitCell(cur); break; - case LFUN_CELL_BACKWARD: - // See below. - cur.setSelection(false); - if (!idxPrev(cur)) { - cmd = FuncRequest(LFUN_FINISHED_BACKWARD); - cur.undispatched(); - } - break; - - case LFUN_CELL_FORWARD: - // Can't handle selection by additional 'shift' as this is - // hard bound to LFUN_CELL_BACKWARD - cur.setSelection(false); - if (!idxNext(cur)) { - cmd = FuncRequest(LFUN_FINISHED_FORWARD); - cur.undispatched(); - } - break; - case LFUN_NEWLINE_INSERT: { cur.recordUndoInset(); row_type const r = cur.row(); @@ -1193,7 +1428,8 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) // split cell splitCell(cur); - swap(cell(cur.idx()), cell(cur.idx() + ncols() - 1)); + if (ncols() > 1) + swap(cell(cur.idx()), cell(cur.idx() + ncols() - 1)); if (cur.idx() > 0) --cur.idx(); cur.pos() = cur.lastpos(); @@ -1203,17 +1439,12 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) break; } - case LFUN_INSET_MODIFY: { + case LFUN_TABULAR_FEATURE: { cur.recordUndoInset(); //lyxerr << "handling tabular-feature " << to_utf8(cmd.argument()) << endl; istringstream is(to_utf8(cmd.argument())); string s; is >> s; - if (s != "tabular") { - InsetMathNest::doDispatch(cur, cmd); - return; - } - is >> s; if (s == "valign-top") setVerticalAlignment('t'); else if (s == "valign-middle") @@ -1254,13 +1485,13 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) cur.pos() = 0; } else if (s == "add-hline-above") - rowinfo_[cur.row()].lines_++; + rowinfo_[cur.row()].lines++; else if (s == "add-hline-below") - rowinfo_[cur.row()+1].lines_++; + rowinfo_[cur.row()+1].lines++; else if (s == "delete-hline-above") - rowinfo_[cur.row()].lines_--; + rowinfo_[cur.row()].lines--; else if (s == "delete-hline-below") - rowinfo_[cur.row()+1].lines_--; + rowinfo_[cur.row()+1].lines--; else if (s == "append-column") { row_type const r = cur.row(); col_type const c = cur.col(); @@ -1288,30 +1519,30 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) cur.pos() = 0; // trick, see above } else if (s == "add-vline-left") { - colinfo_[cur.col()].lines_++; - if (!colinfo_[cur.col()].special_.empty()) - colinfo_[cur.col()].special_ += '|'; + colinfo_[cur.col()].lines++; + if (!colinfo_[cur.col()].special.empty()) + colinfo_[cur.col()].special += '|'; } else if (s == "add-vline-right") { - colinfo_[cur.col()+1].lines_++; - if (!colinfo_[cur.col()+1].special_.empty()) - colinfo_[cur.col()+1].special_.insert(0, 1, '|'); + colinfo_[cur.col()+1].lines++; + if (!colinfo_[cur.col()+1].special.empty()) + colinfo_[cur.col()+1].special.insert(0, 1, '|'); } else if (s == "delete-vline-left") { - colinfo_[cur.col()].lines_--; - docstring & special = colinfo_[cur.col()].special_; + colinfo_[cur.col()].lines--; + docstring & special = colinfo_[cur.col()].special; if (!special.empty()) { docstring::size_type i = special.rfind('|'); - LASSERT(i != docstring::npos, /**/); + LASSERT(i != docstring::npos, break); special.erase(i, 1); } } else if (s == "delete-vline-right") { - colinfo_[cur.col()+1].lines_--; - docstring & special = colinfo_[cur.col()+1].special_; + colinfo_[cur.col()+1].lines--; + docstring & special = colinfo_[cur.col()+1].special; if (!special.empty()) { docstring::size_type i = special.find('|'); - LASSERT(i != docstring::npos, /**/); + LASSERT(i != docstring::npos, break); special.erase(i, 1); } } @@ -1328,41 +1559,42 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) parseflg |= Parse::VERBATIM; // fall through case LFUN_PASTE: { - if (cur.currentMode() <= TEXT_MODE) + if (cur.currentMode() != MATH_MODE) parseflg |= Parse::TEXTMODE; cur.message(_("Paste")); cap::replaceSelection(cur); docstring topaste; if (cmd.argument().empty() && !theClipboard().isInternal()) - topaste = theClipboard().getAsText(); + topaste = theClipboard().getAsText(frontend::Clipboard::PlainTextType); else { idocstringstream is(cmd.argument()); int n = 0; is >> n; - topaste = cap::selection(n); + topaste = cap::selection(n, make_pair(buffer().params().documentClassPtr(), + buffer().params().authors()), true); } InsetMathGrid grid(buffer_, 1, 1); if (!topaste.empty()) - if ((topaste.size() == 1 && topaste.at(0) < 0x80) + if ((topaste.size() == 1 && isAscii(topaste)) || !mathed_parse_normal(grid, topaste, parseflg)) { resetGrid(grid); mathed_parse_normal(grid, topaste, parseflg | Parse::VERBATIM); } bool hline_enabled = false; - FuncRequest fr = FuncRequest(LFUN_INSET_MODIFY, "tabular add-hline-above"); + FuncRequest fr = FuncRequest(LFUN_TABULAR_FEATURE, "add-hline-above"); FuncStatus status; if (getStatus(cur, fr, status)) hline_enabled = status.enabled(); if (grid.nargs() == 1) { // single cell/part of cell - cur.recordUndo(); + cur.recordUndoInset(); cur.cell().insert(cur.pos(), grid.cell(0)); cur.pos() += grid.cell(0).size(); if (hline_enabled) - rowinfo_[cur.row()].lines_ += grid.rowinfo_[0].lines_; + rowinfo_[cur.row()].lines += grid.rowinfo_[0].lines; else { - for (unsigned int l = 0; l < grid.rowinfo_[0].lines_; ++l) { + for (unsigned int l = 0; l < grid.rowinfo_[0].lines; ++l) { cur.cell().insert(0, MathAtom(new InsetMathUnknown(from_ascii("\\hline")))); cur.pos()++; @@ -1371,38 +1603,49 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) } else { // multiple cells cur.recordUndoInset(); - col_type const numcols = - min(grid.ncols(), ncols() - col(cur.idx())); + col_type startcol = col(cur.idx()); + row_type startrow = cur.row(); + col_type oldncols = ncols(); + col_type numcols = + min(grid.ncols(), ncols() - startcol); row_type const numrows = min(grid.nrows(), nrows() - cur.row()); for (row_type r = 0; r < numrows; ++r) { for (col_type c = 0; c < numcols; ++c) { - idx_type i = index(r + cur.row(), c + col(cur.idx())); - cell(i).insert(0, grid.cell(grid.index(r, c))); + idx_type i = index(r + startrow, c + startcol); + pos_type const ipos = min(cur.pos(), pos_type(cell(i).size())); + cell(i).insert(ipos, grid.cell(grid.index(r, c))); } if (hline_enabled) - rowinfo_[r].lines_ += grid.rowinfo_[r].lines_; + rowinfo_[r].lines += grid.rowinfo_[r].lines; else { - for (unsigned int l = 0; l < grid.rowinfo_[r].lines_; ++l) { - idx_type i = index(r + cur.row(), 0); + for (unsigned int l = 0; l < grid.rowinfo_[r].lines; ++l) { + idx_type i = index(r + startrow, 0); cell(i).insert(0, MathAtom(new InsetMathUnknown(from_ascii("\\hline")))); } } - // append the left over horizontal cells to the last column - idx_type i = index(r + cur.row(), ncols() - 1); - for (InsetMath::col_type c = numcols; c < grid.ncols(); ++c) + // append columns for the left over horizontal cells + for (col_type c = numcols; c < grid.ncols(); ++c) { + addCol(c + startcol); + idx_type i = index(r + startrow, min(c + startcol, ncols() - 1)); cell(i).append(grid.cell(grid.index(r, c))); + ++numcols; + } } - // append the left over vertical cells to the last _cell_ + // amend cursor position if cols have been appended + cur.idx() += startrow * (ncols() - oldncols); + // append rows for the left over vertical cells idx_type i = nargs() - 1; for (row_type r = numrows; r < grid.nrows(); ++r) { + row_type crow = startrow + r; + addRow(crow - 1); for (col_type c = 0; c < grid.ncols(); ++c) - cell(i).append(grid.cell(grid.index(r, c))); + cell(index(min(crow, nrows() - 1), min(c + startcol, ncols() - 1))).append(grid.cell(grid.index(r, c))); if (hline_enabled) - rowinfo_[r].lines_ += grid.rowinfo_[r].lines_; + rowinfo_[crow].lines += grid.rowinfo_[r].lines; else { - for (unsigned int l = 0; l < grid.rowinfo_[r].lines_; ++l) { + for (unsigned int l = 0; l < grid.rowinfo_[r].lines; ++l) { cell(i).insert(0, MathAtom(new InsetMathUnknown(from_ascii("\\hline")))); } @@ -1417,12 +1660,10 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) break; } - case LFUN_LINE_BEGIN_SELECT: case LFUN_LINE_BEGIN: - case LFUN_WORD_BACKWARD_SELECT: - case LFUN_WORD_BACKWARD: - case LFUN_WORD_LEFT_SELECT: - case LFUN_WORD_LEFT: + cur.screenUpdateFlags(Update::Decoration | Update::FitCursor); + // fall through + case LFUN_LINE_BEGIN_SELECT: cur.selHandle(act == LFUN_WORD_BACKWARD_SELECT || act == LFUN_WORD_LEFT_SELECT || act == LFUN_LINE_BEGIN_SELECT); @@ -1441,12 +1682,10 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) } break; - case LFUN_WORD_FORWARD_SELECT: - case LFUN_WORD_FORWARD: - case LFUN_WORD_RIGHT_SELECT: - case LFUN_WORD_RIGHT: - case LFUN_LINE_END_SELECT: case LFUN_LINE_END: + cur.screenUpdateFlags(Update::Decoration | Update::FitCursor); + // fall through + case LFUN_LINE_END_SELECT: cur.selHandle(act == LFUN_WORD_FORWARD_SELECT || act == LFUN_WORD_RIGHT_SELECT || act == LFUN_LINE_END_SELECT); @@ -1476,14 +1715,8 @@ bool InsetMathGrid::getStatus(Cursor & cur, FuncRequest const & cmd, FuncStatus & status) const { switch (cmd.action()) { - case LFUN_INSET_MODIFY: { - istringstream is(to_utf8(cmd.argument())); - string s; - is >> s; - if (s != "tabular") { - // We only now about table actions here. - break; - } + case LFUN_TABULAR_FEATURE: { + string s = cmd.getArg(0); if (&cur.inset() != this) { // Table actions requires that the cursor is _inside_ the // table. @@ -1491,7 +1724,6 @@ bool InsetMathGrid::getStatus(Cursor & cur, FuncRequest const & cmd, status.message(from_utf8(N_("Cursor not in table"))); return true; } - is >> s; if (nrows() <= 1 && (s == "delete-row" || s == "swap-row")) { status.setEnabled(false); status.message(from_utf8(N_("Only one row"))); @@ -1503,18 +1735,18 @@ bool InsetMathGrid::getStatus(Cursor & cur, FuncRequest const & cmd, status.message(from_utf8(N_("Only one column"))); return true; } - if ((rowinfo_[cur.row()].lines_ == 0 && + if ((rowinfo_[cur.row()].lines == 0 && s == "delete-hline-above") || - (rowinfo_[cur.row() + 1].lines_ == 0 && + (rowinfo_[cur.row() + 1].lines == 0 && s == "delete-hline-below")) { status.setEnabled(false); status.message(from_utf8(N_("No hline to delete"))); return true; } - if ((colinfo_[cur.col()].lines_ == 0 && + if ((colinfo_[cur.col()].lines == 0 && s == "delete-vline-left") || - (colinfo_[cur.col() + 1].lines_ == 0 && + (colinfo_[cur.col() + 1].lines == 0 && s == "delete-vline-right")) { status.setEnabled(false); status.message(from_utf8(N_("No vline to delete"))); @@ -1546,28 +1778,10 @@ bool InsetMathGrid::getStatus(Cursor & cur, FuncRequest const & cmd, } else { status.setEnabled(false); status.message(bformat( - from_utf8(N_("Unknown tabular feature '%1$s'")), lyx::from_ascii(s))); + from_utf8(N_("Unknown tabular feature '%1$s'")), + from_utf8(s))); } -#if 0 - // FIXME: What did this code do? - // Please check whether it is still needed! - // should be more precise - if (v_align_ == '\0') { - status.enable(true); - break; - } - if (cmd.argument().empty()) { - status.enable(false); - break; - } - if (!contains("tcb", cmd.argument()[0])) { - status.enable(false); - break; - } - status.setOnOff(cmd.argument()[0] == v_align_); - status.setEnabled(true); -#endif return true; } @@ -1587,4 +1801,75 @@ bool InsetMathGrid::getStatus(Cursor & cur, FuncRequest const & cmd, } +char InsetMathGrid::colAlign(HullType type, col_type col) const +{ + switch (type) { + case hullEqnArray: + return "rcl"[col % 3]; + + case hullMultline: + return 'c'; + case hullGather: + LASSERT(isBufferValid(), + LYXERR0("Buffer not set correctly. Please report!"); + return 'c';); + if (buffer().params().is_math_indent) + return 'l'; + else + return 'c'; + + case hullAlign: + case hullAlignAt: + case hullXAlignAt: + case hullXXAlignAt: + case hullFlAlign: + return "rl"[col & 1]; + + case hullUnknown: + case hullNone: + case hullSimple: + case hullEquation: + case hullRegexp: + return 'c'; + } + // avoid warning + return 'c'; +} + + +//static +int InsetMathGrid::colSpace(HullType type, col_type col) +{ + int alignInterSpace = 0; + switch (type) { + case hullUnknown: + case hullNone: + case hullSimple: + case hullEquation: + case hullMultline: + case hullGather: + case hullRegexp: + return 0; + + case hullEqnArray: + return 5; + + case hullAlign: + alignInterSpace = 20; + break; + case hullAlignAt: + alignInterSpace = 0; + break; + case hullXAlignAt: + alignInterSpace = 40; + break; + case hullXXAlignAt: + case hullFlAlign: + alignInterSpace = 60; + break; + } + return (col % 2) ? alignInterSpace : 0; +} + + } // namespace lyx