X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2FInsetMathGrid.cpp;h=67300403907d947f43f1ca71e2b75323bf2192e6;hb=31cd421bcd26647cb7edbb694b05473271fe9153;hp=c918243325646214de0fba70b4a659e4328227a0;hpb=f835e2a6f94584efe8ee63ed73cdc45bcd7bb05c;p=lyx.git diff --git a/src/mathed/InsetMathGrid.cpp b/src/mathed/InsetMathGrid.cpp index c918243325..6730040390 100644 --- a/src/mathed/InsetMathGrid.cpp +++ b/src/mathed/InsetMathGrid.cpp @@ -13,27 +13,29 @@ #include "InsetMathGrid.h" +#include "InsetMathUnknown.h" #include "MathData.h" #include "MathParser.h" #include "MathStream.h" #include "MetricsInfo.h" #include "Buffer.h" +#include "BufferParams.h" #include "BufferView.h" -#include "CutAndPaste.h" -#include "FuncStatus.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 @@ -42,6 +44,7 @@ using namespace std; using namespace lyx::support; + namespace lyx { static docstring verboseHLine(int n) @@ -66,9 +69,9 @@ static int extractInt(istream & is) static void resetGrid(InsetMathGrid & grid) { while (grid.ncols() > 1) - grid.delCol(grid.ncols()); + grid.delCol(grid.ncols() - 1); while (grid.nrows() > 1) - grid.delRow(grid.nrows()); + grid.delRow(grid.nrows() - 1); grid.cell(0).erase(0, grid.cell(0).size()); grid.setDefaults(); } @@ -79,7 +82,7 @@ static void resetGrid(InsetMathGrid & grid) InsetMathGrid::CellInfo::CellInfo() - : dummy_(false) + : multi_(CELL_NORMAL) {} @@ -88,16 +91,15 @@ InsetMathGrid::CellInfo::CellInfo() InsetMathGrid::RowInfo::RowInfo() - : lines_(0), skip_(0), allow_newpage_(true) + : descent_(0), ascent_(0), offset_(0), 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 crskip_.inPixels(mi.base); } @@ -106,7 +108,7 @@ int InsetMathGrid::RowInfo::skipPixels(MetricsInfo const & mi) const InsetMathGrid::ColInfo::ColInfo() - : align_('c'), lines_(0) + : align_('c'), width_(0), offset_(0), lines_(0), skip_(0) {} @@ -172,6 +174,24 @@ 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; +} + + +bool InsetMathGrid::interpretString(Cursor & cur, docstring const & str) +{ + if (str == "\\hline") { + FuncRequest fr = FuncRequest(LFUN_TABULAR_FEATURE, "add-hline-above"); + FuncStatus status; + if (getStatus(cur, fr, status)) { + if (status.enabled()) { + rowinfo_[cur.row()].lines_++; + return true; + } + } + } + return InsetMathNest::interpretString(cur, str); } @@ -342,6 +362,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; @@ -357,7 +394,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; @@ -366,22 +408,24 @@ 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_[0].ascent_ += hlinesep() * rowinfo_[0].lines_; rowinfo_[nrows()].ascent_ = 0; rowinfo_[nrows()].descent_ = 0; // compute vertical offsets rowinfo_[0].offset_ = 0; for (row_type row = 1; row <= nrows(); ++row) { - rowinfo_[row].offset_ = - rowinfo_[row - 1].offset_ + + rowinfo_[row].offset_ = + rowinfo_[row - 1].offset_ + rowinfo_[row - 1].descent_ + rowinfo_[row - 1].skipPixels(mi) + rowsep() + @@ -405,41 +449,92 @@ 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 > 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::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; // 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_ + + displayColSpace(col - 1) + colsep() + 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_ - + border(); - dim.asc = - rowinfo_[0].offset_ - + rowinfo_[0].ascent_ - + hlinesep() * rowinfo_[0].lines_ - + border(); + dim.wid = colinfo_[ncols() - 1].offset_ + + colinfo_[ncols() - 1].width_ + + vlinesep() * colinfo_[ncols()].lines_ + + border(); - dim.des = rowinfo_[nrows() - 1].offset_ - + rowinfo_[nrows() - 1].descent_ - + hlinesep() * rowinfo_[nrows()].lines_ - + border(); + dim.asc = - rowinfo_[0].offset_ + + rowinfo_[0].ascent_ + + hlinesep() * rowinfo_[0].lines_ + + border(); + + dim.des = rowinfo_[nrows() - 1].offset_ + + rowinfo_[nrows() - 1].descent_ + + hlinesep() * rowinfo_[nrows()].lines_ + + border() + 1; /* @@ -492,71 +587,98 @@ 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(row_type row, unsigned int line) const { - Dimension const dim = dimension(*pi.base.bv); - BufferView const & bv = *pi.base.bv; + return rowinfo_[row].offset_ + - 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(idx)); + + row_type r = row(idx); + int const yy1 = y + hLineVOffset(r, 0); + int const yy2 = y + hLineVOffset(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(r, i); + pi.pain.line(xx1, yy, xx2, yy, Color_foreground); } - drawMarkers2(pi, x, 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; } - //rowinfo_[0].ascent_ += hlinesep() * rowinfo_[0].lines_; rowinfo_[nrows()].ascent_ = 0; rowinfo_[nrows()].descent_ = 0; @@ -593,7 +715,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; } @@ -605,7 +729,7 @@ void InsetMathGrid::metricsT(TextMetricsInfo const & mi, Dimension & dim) const colinfo_[col].offset_ = colinfo_[col - 1].offset_ + colinfo_[col - 1].width_ + - colinfo_[col - 1].skip_ + + displayColSpace(col - 1) + 1 ; //colsep() + //colinfo_[col].lines_ * vlinesep(); } @@ -631,7 +755,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)); } @@ -639,11 +764,13 @@ 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); } -docstring InsetMathGrid::eolString(row_type row, bool fragile) const +docstring InsetMathGrid::eolString(row_type row, bool fragile, + bool /*latex*/, bool last_eoln) const { docstring eol; @@ -655,13 +782,13 @@ docstring InsetMathGrid::eolString(row_type row, bool fragile) const // 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()) + if (eol.empty() && row + 1 == nrows() && (nrows() == 1 || !last_eoln)) return docstring(); return (fragile ? "\\protect\\\\" : "\\\\") + eol; @@ -686,17 +813,6 @@ void InsetMathGrid::addRow(row_type row) } -void InsetMathGrid::appendRow() -{ - rowinfo_.push_back(RowInfo()); - //cells_.insert(cells_.end(), ncols(), MathData()); - for (col_type col = 0; col < ncols(); ++col) { - cells_.push_back(cells_type::value_type()); - cellinfo_.push_back(CellInfo()); - } -} - - void InsetMathGrid::delRow(row_type row) { if (nrows() == 1) @@ -795,14 +911,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 = colinfo_[c].align_; + 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; } @@ -813,6 +931,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_ + - displayColSpace(c2) + - colsep() + - colinfo_[c2].lines_ * vlinesep(); + } + case CELL_PART_OF_MULTICOLUMN: + return 0; + } + return 0; +} + + bool InsetMathGrid::idxUpDown(Cursor & cur, bool up) const { if (up) { @@ -824,6 +963,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; } @@ -835,6 +979,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; } @@ -846,6 +995,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; } @@ -863,6 +1019,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; } @@ -880,6 +1041,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; } @@ -897,7 +1063,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 @@ -930,9 +1096,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(); } } @@ -962,14 +1134,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(ncellcols(i)) << ' ' + << cell(i) << ']'; + break; + case CELL_PART_OF_MULTICOLUMN: + break; + } + } os << ']'; } os << ']'; @@ -984,20 +1168,29 @@ void InsetMathGrid::mathmlize(MathStream & os) const char const * const celltag = havetable ? "mtd" : "mrow"; for (row_type row = 0; row < nrows(); ++row) { if (havetable) - os << MTag("mtr");; + 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");; + os << ETag("mtr"); } if (havetable) os << ETag("mtable"); } +// FIXME XHTML +// We need to do something about alignment here. void InsetMathGrid::htmlize(HtmlStream & os, string attrib) const { bool const havetable = nrows() > 1 || ncols() > 1; @@ -1005,15 +1198,22 @@ void InsetMathGrid::htmlize(HtmlStream & os, string attrib) const os << cell(index(0, 0)); return; } - os << MTag("table", "class='" + attrib + "'"); + os << MTag("table", attrib); for (row_type row = 0; row < nrows(); ++row) { - os << MTag("tr");; + 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");; + os << ETag("tr"); } os << ETag("table"); } @@ -1021,7 +1221,21 @@ void InsetMathGrid::htmlize(HtmlStream & os, string attrib) const void InsetMathGrid::htmlize(HtmlStream & os) const { - htmlize(os, "mathtable"); + htmlize(os, "class='mathtable'"); +} + + +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); } @@ -1042,19 +1256,47 @@ void InsetMathGrid::write(WriteStream & os, // unless there are vertical lines col_type lastcol = 0; bool emptyline = true; - for (col_type col = beg_col; col < end_col; ++col) - if (!cell(index(row, col)).empty() - || colinfo_[col + 1].lines_) { + bool last_eoln = true; + for (col_type col = beg_col; col < end_col; ++col) { + idx_type const idx = index(row, col); + bool const empty_cell = cell(idx).empty(); + if (!empty_cell || cellinfo_[idx].multi_ != CELL_NORMAL) + last_eoln = false; + 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 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()); + eol = eolString(row, os.fragile(), os.latex(), last_eoln); os << eol; // append newline only if line wasn't completely empty // and the formula is not written on a single line @@ -1113,21 +1355,46 @@ 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; Parse::flags parseflg = Parse::QUIET | Parse::USETEXT; - switch (cmd.action) { + FuncCode const act = cmd.action(); + switch (act) { // insert file functions - case LFUN_LINE_DELETE: + case LFUN_LINE_DELETE_FORWARD: cur.recordUndoInset(); //autocorrect_ = false; //macroModeClose(); @@ -1150,7 +1417,7 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) case LFUN_CELL_BACKWARD: // See below. - cur.setSelection(false); + cur.selection(false); if (!idxPrev(cur)) { cmd = FuncRequest(LFUN_FINISHED_BACKWARD); cur.undispatched(); @@ -1160,7 +1427,7 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) case LFUN_CELL_FORWARD: // Can't handle selection by additional 'shift' as this is // hard bound to LFUN_CELL_BACKWARD - cur.setSelection(false); + cur.selection(false); if (!idxNext(cur)) { cmd = FuncRequest(LFUN_FINISHED_FORWARD); cur.undispatched(); @@ -1178,27 +1445,23 @@ 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(); - + cur.forceBufferUpdate(); //mathcursor->normalize(); //cmd = FuncRequest(LFUN_FINISHED_BACKWARD); 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") @@ -1287,7 +1550,7 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) 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); } } @@ -1296,7 +1559,7 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) 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); } } @@ -1313,32 +1576,46 @@ 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, buffer().params().documentClassPtr()); } 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_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_; + else { + for (unsigned int l = 0; l < grid.rowinfo_[0].lines_; ++l) { + cur.cell().insert(0, + MathAtom(new InsetMathUnknown(from_ascii("\\hline")))); + cur.pos()++; + } + } } else { // multiple cells cur.recordUndoInset(); @@ -1351,6 +1628,15 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) idx_type i = index(r + cur.row(), c + col(cur.idx())); cell(i).insert(0, grid.cell(grid.index(r, c))); } + if (hline_enabled) + 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); + 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) @@ -1358,28 +1644,34 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) } // append the left over vertical cells to the last _cell_ idx_type i = nargs() - 1; - for (row_type r = numrows; r < grid.nrows(); ++r) + for (row_type r = numrows; r < grid.nrows(); ++r) { for (col_type c = 0; c < grid.ncols(); ++c) cell(i).append(grid.cell(grid.index(r, c))); + if (hline_enabled) + rowinfo_[r].lines_ += grid.rowinfo_[r].lines_; + else { + for (unsigned int l = 0; l < grid.rowinfo_[r].lines_; ++l) { + cell(i).insert(0, + MathAtom(new InsetMathUnknown(from_ascii("\\hline")))); + } + } + } } cur.clearSelection(); // bug 393 - // FIXME audit setBuffer/updateBuffer calls + // FIXME audit setBuffer calls cur.inset().setBuffer(*buffer_); - // FIXME audit setBuffer/updateBuffer calls - cur.buffer()->updateBuffer(); + cur.forceBufferUpdate(); cur.finishUndo(); 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.selHandle(cmd.action == LFUN_WORD_BACKWARD_SELECT || - cmd.action == LFUN_WORD_LEFT_SELECT || - cmd.action == LFUN_LINE_BEGIN_SELECT); + 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); cur.macroModeClose(); if (cur.pos() != 0) { cur.pos() = 0; @@ -1395,15 +1687,13 @@ 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.selHandle(cmd.action == LFUN_WORD_FORWARD_SELECT || - cmd.action == LFUN_WORD_RIGHT_SELECT || - cmd.action == LFUN_LINE_END_SELECT); + 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); cur.macroModeClose(); cur.clearTargetX(); if (cur.pos() != cur.lastpos()) { @@ -1429,15 +1719,9 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd) 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; - } + switch (cmd.action()) { + case LFUN_TABULAR_FEATURE: { + string s = cmd.getArg(0); if (&cur.inset() != this) { // Table actions requires that the cursor is _inside_ the // table. @@ -1445,7 +1729,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"))); @@ -1500,7 +1783,8 @@ 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 @@ -1526,7 +1810,7 @@ bool InsetMathGrid::getStatus(Cursor & cur, FuncRequest const & cmd, } case LFUN_CELL_SPLIT: - status.setEnabled(true); + status.setEnabled(cur.idx() != cur.lastidx()); return true; case LFUN_CELL_BACKWARD: @@ -1541,4 +1825,69 @@ bool InsetMathGrid::getStatus(Cursor & cur, FuncRequest const & cmd, } +// static +char InsetMathGrid::colAlign(HullType type, col_type col) +{ + switch (type) { + case hullEqnArray: + return "rcl"[col % 3]; + + case hullMultline: + case hullGather: + 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