X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2FInsetMathHull.cpp;h=537d6670c5ffa0d090ffb0b2acfd5c372a001f83;hb=4ed0312c51704780af1c452d3a82a84171b3725a;hp=724c11574ea08860a7ccef2b34a51a28f10948a3;hpb=b954f478e31e640f292f865a5c41c65565e5c02a;p=lyx.git diff --git a/src/mathed/InsetMathHull.cpp b/src/mathed/InsetMathHull.cpp index 724c11574e..537d6670c5 100644 --- a/src/mathed/InsetMathHull.cpp +++ b/src/mathed/InsetMathHull.cpp @@ -11,7 +11,6 @@ #include #include "InsetMathHull.h" - #include "InsetMathChar.h" #include "InsetMathColor.h" #include "InsetMathFrac.h" @@ -26,6 +25,7 @@ #include "BufferParams.h" #include "BufferView.h" #include "ColorSet.h" +#include "Cursor.h" #include "CutAndPaste.h" #include "Encoding.h" #include "Exporter.h" @@ -38,10 +38,9 @@ #include "InsetMathMacro.h" #include "InsetMathMacroTemplate.h" #include "MetricsInfo.h" -#include "output_xhtml.h" #include "Paragraph.h" #include "ParIterator.h" -#include "sgml.h" +#include "xml.h" #include "TexRow.h" #include "TextClass.h" #include "TextPainter.h" @@ -51,11 +50,11 @@ #include "insets/InsetRef.h" #include "insets/RenderPreview.h" -#include "graphics/GraphicsImage.h" #include "graphics/PreviewImage.h" #include "graphics/PreviewLoader.h" #include "frontends/alert.h" +#include "frontends/FontMetrics.h" #include "frontends/Painter.h" #include "support/convert.h" @@ -64,7 +63,7 @@ #include "support/filetools.h" #include "support/lassert.h" #include "support/lstrings.h" -#include "support/RefChanger.h" +#include "support/Changer.h" #include @@ -121,84 +120,38 @@ namespace { // writes a preamble for underlined or struck out math display - void writeMathdisplayPreamble(WriteStream & os) + void writeMathdisplayPreamble(TeXMathStream & os) { - if (os.strikeoutMath()) { - if (os.ulemCmd() == WriteStream::UNDERLINE) - os << "\\raisebox{-\\belowdisplayshortskip}{" - "\\lyxmathsout{\\parbox[b]{\\linewidth}{"; - else - os << "\\lyxmathsout{\\parbox{\\linewidth}{"; - } else if (os.ulemCmd() == WriteStream::UNDERLINE) + if (os.strikeoutMath()) + return; + + if (os.ulemCmd() == TeXMathStream::UNDERLINE) os << "\\raisebox{-\\belowdisplayshortskip}{" "\\parbox[b]{\\linewidth}{"; - else if (os.ulemCmd() == WriteStream::STRIKEOUT) + else if (os.ulemCmd() == TeXMathStream::STRIKEOUT) os << "\\parbox{\\linewidth}{"; } // writes a postamble for underlined or struck out math display - void writeMathdisplayPostamble(WriteStream & os) + void writeMathdisplayPostamble(TeXMathStream & os) { - if (os.strikeoutMath()) { - if (os.ulemCmd() == WriteStream::UNDERLINE) - os << "}"; - os << "}}\\\\\n"; - } else if (os.ulemCmd() == WriteStream::UNDERLINE) + if (os.strikeoutMath()) + return; + + if (os.ulemCmd() == TeXMathStream::UNDERLINE) os << "}}\\\\\n"; - else if (os.ulemCmd() == WriteStream::STRIKEOUT) + else if (os.ulemCmd() == TeXMathStream::STRIKEOUT) os << "}\\\\\n"; } - } // namespace -HullType hullType(docstring const & s) -{ - if (s == "none") return hullNone; - if (s == "simple") return hullSimple; - if (s == "equation") return hullEquation; - if (s == "eqnarray") return hullEqnArray; - if (s == "align") return hullAlign; - if (s == "alignat") return hullAlignAt; - if (s == "xalignat") return hullXAlignAt; - if (s == "xxalignat") return hullXXAlignAt; - if (s == "multline") return hullMultline; - if (s == "gather") return hullGather; - if (s == "flalign") return hullFlAlign; - if (s == "regexp") return hullRegexp; - lyxerr << "unknown hull type '" << to_utf8(s) << "'" << endl; - return hullUnknown; -} - - -docstring hullName(HullType type) -{ - switch (type) { - case hullNone: return from_ascii("none"); - case hullSimple: return from_ascii("simple"); - case hullEquation: return from_ascii("equation"); - case hullEqnArray: return from_ascii("eqnarray"); - case hullAlign: return from_ascii("align"); - case hullAlignAt: return from_ascii("alignat"); - case hullXAlignAt: return from_ascii("xalignat"); - case hullXXAlignAt: return from_ascii("xxalignat"); - case hullMultline: return from_ascii("multline"); - case hullGather: return from_ascii("gather"); - case hullFlAlign: return from_ascii("flalign"); - case hullRegexp: return from_ascii("regexp"); - case hullUnknown: - lyxerr << "unknown hull type" << endl; - break; - } - return from_ascii("none"); -} - -static InsetLabel * dummy_pointer = 0; +static InsetLabel * dummy_pointer = nullptr; InsetMathHull::InsetMathHull(Buffer * buf) - : InsetMathGrid(buf, 1, 1), type_(hullNone), numbered_(1, NUMBER), + : InsetMathGrid(buf, 1, 1), type_(hullNone), numbered_(1, NONUMBER), numbers_(1, empty_docstring()), label_(1, dummy_pointer), preview_(new RenderPreview(this)) { @@ -213,7 +166,7 @@ InsetMathHull::InsetMathHull(Buffer * buf) InsetMathHull::InsetMathHull(Buffer * buf, HullType type) - : InsetMathGrid(buf, getCols(type), 1), type_(type), numbered_(1, NUMBER), + : InsetMathGrid(buf, getCols(type), 1), type_(type), numbered_(1, NONUMBER), numbers_(1, empty_docstring()), label_(1, dummy_pointer), preview_(new RenderPreview(this)) { @@ -231,8 +184,8 @@ InsetMathHull::InsetMathHull(InsetMathHull const & other) : InsetMathGrid(other) InsetMathHull::~InsetMathHull() { - for (size_t i = 0; i < label_.size(); ++i) - delete label_[i]; + for (auto & i : label_) + delete i; } @@ -251,8 +204,8 @@ InsetMathHull & InsetMathHull::operator=(InsetMathHull const & other) numbered_ = other.numbered_; numbers_ = other.numbers_; buffer_ = other.buffer_; - for (size_t i = 0; i < label_.size(); ++i) - delete label_[i]; + for (auto & i : label_) + delete i; label_ = other.label_; for (size_t i = 0; i != label_.size(); ++i) { if (label_[i]) @@ -275,15 +228,7 @@ void InsetMathHull::setBuffer(Buffer & buffer) } -// FIXME This should really be controlled by the TOC level, or -// something of the sort. -namespace { - const char * counters_to_save[] = {"section", "chapter"}; - unsigned int const numcnts = sizeof(counters_to_save)/sizeof(char *); -} // namespace - - -void InsetMathHull::updateBuffer(ParIterator const & it, UpdateType utype) +void InsetMathHull::updateBuffer(ParIterator const & it, UpdateType utype, bool const deleted) { if (!buffer_) { //FIXME: buffer_ should be set at creation for this inset! Problem is @@ -300,26 +245,26 @@ void InsetMathHull::updateBuffer(ParIterator const & it, UpdateType utype) Counters & cnts = buffer_->masterBuffer()->params().documentClass().counters(); - // right now, we only need to do this at export time - if (utype == OutputUpdate) { - for (size_t i = 0; i < numcnts; ++i) { - docstring const cnt = from_ascii(counters_to_save[i]); - if (cnts.hasCounter(cnt)) - counter_map[cnt] = cnts.value(cnt); - } - } - // this has to be done separately docstring const eqstr = from_ascii("equation"); if (cnts.hasCounter(eqstr)) { - if (utype == OutputUpdate) - counter_map[eqstr] = cnts.value(eqstr); for (size_t i = 0; i != label_.size(); ++i) { + docstring const oldnumber = numbers_[i]; if (numbered(i)) { - cnts.step(eqstr, utype); - numbers_[i] = cnts.theCounter(eqstr, lang); + Paragraph const & par = it.paragraph(); + if (!par.isDeleted(it.pos())) { + cnts.step(eqstr, utype); + numbers_[i] = cnts.theCounter(eqstr, lang); + } else + numbers_[i] = from_ascii("#"); } else numbers_[i] = empty_docstring(); + // If the numbering has changed, trigger a new preview + if (oldnumber != numbers_[i] && RenderPreview::previewMath()) { + // Do we need to remove it first? + //preview_->removePreview(*buffer_); + preparePreview(it); + } } } } @@ -327,10 +272,10 @@ void InsetMathHull::updateBuffer(ParIterator const & it, UpdateType utype) // now the labels for (size_t i = 0; i != label_.size(); ++i) { if (label_[i]) - label_[i]->updateBuffer(it, utype); + label_[i]->updateBuffer(it, utype, deleted); } // pass down - InsetMathGrid::updateBuffer(it, utype); + InsetMathGrid::updateBuffer(it, utype, deleted); } @@ -419,22 +364,6 @@ InsetMath::mode_type InsetMathHull::currentMode() const } -bool InsetMathHull::idxFirst(Cursor & cur) const -{ - cur.idx() = 0; - cur.pos() = 0; - return true; -} - - -bool InsetMathHull::idxLast(Cursor & cur) const -{ - cur.idx() = nargs() - 1; - cur.pos() = cur.lastpos(); - return true; -} - - // FIXME: InsetMathGrid should be changed to let the real column alignment be // given by a virtual method like displayColAlign, because the values produced // by defaultColAlign can be invalidated by lfuns such as add-column. For the @@ -525,11 +454,27 @@ bool InsetMathHull::previewState(const BufferView *const bv) const namespace { -static const int ERROR_FRAME_WIDTH = 2; +const int ERROR_FRAME_WIDTH = 2; + +bool previewTooSmall(Dimension const & dim) +{ + return dim.width() <= 10 && dim.height() <= 10; +} } + void InsetMathHull::metrics(MetricsInfo & mi, Dimension & dim) const { + /* Compute \(above|below)displayskip + true value in LaTeX is 10pt plus 2pt minus 5pt (in normal size at 10pt) + But we use a fixed number of pixels and scale them with zoom. + */ + int const bottom_display_margin = mi.base.bv->zoomedPixels(6); + int top_display_margin = bottom_display_margin; + // at start of paragraph, add an empty line + if (mi.vmode) + top_display_margin += theFontMetrics(mi.base.font).maxHeight() + 2; + if (previewState(mi.base.bv)) { preview_->metrics(mi, dim); if (previewTooSmall(dim)) { @@ -537,52 +482,58 @@ void InsetMathHull::metrics(MetricsInfo & mi, Dimension & dim) const dim.wid += 2 * ERROR_FRAME_WIDTH; dim.asc += 2 * ERROR_FRAME_WIDTH; } else { - // insert a one pixel gap in front of the formula - dim.wid += 1; - if (display()) - dim.des += displayMargin(); + // insert a gap in front of the formula + // value was hardcoded to 1 pixel + dim.wid += mi.base.bv->zoomedPixels(1) ; + if (display()) { + dim.asc += top_display_margin; + dim.des += bottom_display_margin; + } } return; } - Changer dummy1 = mi.base.changeFontSet(standardFont()); - Changer dummy2 = mi.base.font.changeStyle(display() ? LM_ST_DISPLAY - : LM_ST_TEXT); - - // let the cells adjust themselves - InsetMathGrid::metrics(mi, dim); + { + Changer dummy1 = mi.base.changeFontSet(standardFont()); + Changer dummy2 = mi.base.font.changeStyle(display() ? DISPLAY_STYLE + : TEXT_STYLE); - if (display()) { - dim.asc += displayMargin(); - dim.des += displayMargin(); + // let the cells adjust themselves + InsetMathGrid::metrics(mi, dim); } + // Check whether the numbering interferes with the equations if (numberedType()) { - Changer dummy = mi.base.changeFontSet("mathrm"); - int l = 0; - for (row_type row = 0; row < nrows(); ++row) - l = max(l, mathed_string_width(mi.base.font, nicelabel(row))); + BufferParams::MathNumber const math_number = buffer().params().getMathNumber(); + int extra_offset = 0; + for (row_type row = 0; row < nrows(); ++row) { + rowinfo(row).offset[mi.base.bv] += extra_offset; + docstring const nl = nicelabel(row); + if (nl.empty()) + continue; + Dimension dimnl; + mathed_string_dim(mi.base.font, nl, dimnl); + int const ind = indent(*mi.base.bv); + int const x = ind ? ind : (mi.base.textwidth - dim.wid) / 2; + // for some reason metrics does not trigger at the + // same point as draw, and therefore we use >= instead of > + if ((math_number == BufferParams::LEFT && dimnl.wid >= x) + || (math_number == BufferParams::RIGHT + && dimnl.wid >= mi.base.textwidth - x - dim.wid)) { + extra_offset += dimnl.height(); + } + } + dim.des += extra_offset; + } + - if (l) - // Value was hardcoded to 30 pixels - dim.wid += Length(0.3, Length::IN).inPixels(mi.base) + l; + if (display()) { + dim.asc += top_display_margin; + dim.des += bottom_display_margin; } // reserve some space for marker. dim.wid += 2; - - // make it at least as high as the current font - int asc = 0; - int des = 0; - math_font_max_dim(mi.base.font, asc, des); - dim.asc = max(dim.asc, asc); - dim.des = max(dim.des, des); -} - - -bool InsetMathHull::previewTooSmall(Dimension const & dim) const -{ - return dim.width() <= 10 && dim.height() <= 10; } @@ -599,6 +550,25 @@ ColorCode InsetMathHull::backgroundColor(PainterInfo const & pi) const } +void InsetMathHull::drawMarkers(PainterInfo & pi, int x, int y) const +{ + ColorCode pen_color = mouseHovered(pi.base.bv) || editing(pi.base.bv)? + Color_mathframe : Color_mathcorners; + // If the corners have the same color as the background, do not paint them. + if (lcolor.getX11HexName(Color_mathbg) == lcolor.getX11HexName(pen_color)) + return; + + Inset::drawMarkers(pi, x, y); + Dimension const dim = dimension(*pi.base.bv); + int const t = x + dim.width() - 1; + int const a = y - dim.ascent(); + pi.pain.line(x, a + 3, x, a, pen_color); + pi.pain.line(t, a + 3, t, a, pen_color); + pi.pain.line(x, a, x + 3, a, pen_color); + pi.pain.line(t - 3, a, t, a, pen_color); +} + + void InsetMathHull::drawBackground(PainterInfo & pi, int x, int y) const { Dimension const dim = dimension(*pi.base.bv); @@ -607,8 +577,9 @@ void InsetMathHull::drawBackground(PainterInfo & pi, int x, int y) const dim.wid, dim.asc + dim.des, backgroundColor(pi)); return; } + pi.pain.fillRectangle(x + 1, y - dim.asc + 1, dim.wid - 2, - dim.asc + dim.des - 1, pi.backgroundColor(this)); + dim.height() - 1, pi.backgroundColor(this)); } @@ -624,61 +595,73 @@ void InsetMathHull::draw(PainterInfo & pi, int x, int y) const if (previewState(bv)) { // Do not draw change tracking cue if taken care of by RowPainter // already. - Changer dummy = !canPaintChange(*bv) ? make_change(pi.change_, Change()) - : Changer(); + Changer dummy = !canPaintChange(*bv) ? changeVar(pi.change, Change()) + : noChange(); if (previewTooSmall(dim)) { // we have an extra frame preview_->draw(pi, x + ERROR_FRAME_WIDTH, y); } else { // one pixel gap in front - preview_->draw(pi, x + 1, y); + // value was hardcoded to 1 pixel + int const gap = pi.base.bv->zoomedPixels(1) ; + preview_->draw(pi, x + gap, y); } return; } - ColorCode color = pi.selected && lyxrc.use_system_colors - ? Color_selectiontext : standardColor(); - bool const really_change_color = pi.base.font.color() == Color_none; - Changer dummy0 = really_change_color ? pi.base.font.changeColor(color) - : Changer(); - Changer dummy1 = pi.base.changeFontSet(standardFont()); - Changer dummy2 = pi.base.font.changeStyle(display() ? LM_ST_DISPLAY - : LM_ST_TEXT); - - int xmath = x; - BufferParams::MathNumber const math_number = buffer().params().getMathNumber(); - if (numberedType() && math_number == BufferParams::LEFT) { - Changer dummy = pi.base.changeFontSet("mathrm"); - int l = 0; - for (row_type row = 0; row < nrows(); ++row) - l = max(l, mathed_string_width(pi.base.font, nicelabel(row))); - - if (l) - // Value was hardcoded to 30 pixels - xmath += Length(0.3, Length::IN).inPixels(pi.base) + l; - } - - InsetMathGrid::draw(pi, xmath + 1, y); - drawMarkers2(pi, x, y); - + // First draw the numbers if (numberedType()) { - Changer dummy = pi.base.changeFontSet("mathrm"); + BufferParams::MathNumber const math_number = buffer().params().getMathNumber(); for (row_type row = 0; row < nrows(); ++row) { - int const yy = y + rowinfo_[row].offset_; + int yy = y + rowinfo(row).offset[bv]; docstring const nl = nicelabel(row); - if (math_number == BufferParams::LEFT) - pi.draw(x, yy, nl); - else { - int l = mathed_string_width(pi.base.font, nl); - pi.draw(x + dim.wid - l, yy, nl); + Dimension dimnl; + mathed_string_dim(pi.base.font, nl, dimnl); + if (math_number == BufferParams::LEFT) { + ColorCode const col = pi.selected_left + ? Color_selectiontext + : pi.base.font.color(); + Changer dummy0 = pi.base.font.changeColor(col); + if (dimnl.wid > x - pi.leftx) + yy += rowinfo(row).descent + dimnl.asc; + pi.pain.fillRectangle(pi.leftx, yy - dimnl.asc, + dimnl.width(), dimnl.height(), + pi.selected_left ? Color_selection : pi.background_color); + pi.draw(pi.leftx, yy, nl); + } else { + ColorCode const col = pi.selected_right + ? Color_selectiontext + : pi.base.font.color(); + Changer dummy0 = pi.base.font.changeColor(col); + if (dimnl.wid > pi.rightx - x - dim.wid) + yy += rowinfo(row).descent + dimnl.asc; + pi.pain.fillRectangle(pi.rightx - dimnl.wid, yy - dimnl.asc, + dimnl.width(), dimnl.height(), + pi.selected_right ? Color_selection : pi.background_color); + pi.draw(pi.rightx - dimnl.wid, yy, nl); } } } + // Then the equations + ColorCode color = pi.selected && lyxrc.use_system_colors + ? Color_selectiontext : standardColor(); + bool const really_change_color = pi.base.font.color() == Color_none; + Changer dummy0 = really_change_color ? pi.base.font.changeColor(color) + : noChange(); + Changer dummy1 = pi.base.changeFontSet(standardFont()); + Changer dummy2 = pi.base.font.changeStyle(display() ? DISPLAY_STYLE + : TEXT_STYLE); + InsetMathGrid::draw(pi, x + 1, y); + drawMarkers(pi, x, y); + // drawing change line - if (canPaintChange(*bv)) - pi.change_.paintCue(pi, x + 1, y + 1 - dim.asc, - x + dim.wid, y + dim.des); + if (canPaintChange(*bv)) { + // like in metrics() + int const display_margin = display() ? pi.base.inPixels(Length(12, Length::PT)) : 0; + pi.change.paintCue(pi, x + 1, y + 1 - dim.asc + display_margin, + x + dim.wid, y + dim.des - display_margin); + } } @@ -689,7 +672,7 @@ void InsetMathHull::metricsT(TextMetricsInfo const & mi, Dimension & dim) const } else { odocstringstream os; otexrowstream ots(os); - WriteStream wi(ots, false, true, WriteStream::wsDefault); + TeXMathStream wi(ots, false, true, TeXMathStream::wsDefault); write(wi); dim.wid = os.str().size(); dim.asc = 1; @@ -705,7 +688,7 @@ void InsetMathHull::drawT(TextPainter & pain, int x, int y) const } else { odocstringstream os; otexrowstream ots(os); - WriteStream wi(ots, false, true, WriteStream::wsDefault); + TeXMathStream wi(ots, false, true, TeXMathStream::wsDefault); write(wi); pain.draw(x, y, os.str().c_str()); } @@ -720,11 +703,11 @@ static docstring latexString(InsetMathHull const & inset) // \newcommand{\xxx}{\text{$\phi$}}) gets processed twice. The // first time as a whole, and the second time only the inner math. // In this last case inset.buffer() would be invalid. - static Encoding const * encoding = 0; + static Encoding const * encoding = nullptr; if (inset.isBufferValid()) encoding = &(inset.buffer().params().encoding()); otexrowstream ots(ls); - WriteStream wi(ots, false, true, WriteStream::wsPreview, encoding); + TeXMathStream wi(ots, false, true, TeXMathStream::wsPreview, encoding); inset.write(wi); return ls.str(); } @@ -824,8 +807,8 @@ void InsetMathHull::preparePreview(DocIterator const & pos, usedMacros(cell(idx), pos, macros, defs); docstring macro_preamble; - for (auto const & defit : defs) - macro_preamble.append(defit); + for (auto const & defvar : defs) + macro_preamble.append(defvar); // set the font series and size for this snippet DocIterator dit = pos.getInnerText(); @@ -842,29 +825,7 @@ void InsetMathHull::preparePreview(DocIterator const & pos, if (lsize != "normalsize" && !prefixIs(lsize, "error")) setfont += from_ascii("\\" + lsize + '\n'); - docstring setcnt; - if (forexport && haveNumbers()) { - docstring eqstr = from_ascii("equation"); - CounterMap::const_iterator it = counter_map.find(eqstr); - if (it != counter_map.end()) { - int num = it->second; - if (num >= 0) - setcnt += from_ascii("\\setcounter{") + eqstr + '}' + - '{' + convert(num) + '}' + '\n'; - } - for (size_t i = 0; i != numcnts; ++i) { - docstring cnt = from_ascii(counters_to_save[i]); - it = counter_map.find(cnt); - if (it == counter_map.end()) - continue; - int num = it->second; - if (num > 0) - setcnt += from_ascii("\\setcounter{") + cnt + '}' + - '{' + convert(num) + '}'; - } - } - docstring const snippet = macro_preamble + setfont + setcnt - + latexString(*this) + endfont; + docstring const snippet = macro_preamble + setfont + latexString(*this) + endfont; LYXERR(Debug::MACROS, "Preview snippet: " << snippet); preview_->addPreview(snippet, *buffer, forexport); } @@ -930,10 +891,6 @@ void InsetMathHull::label(row_type row, docstring const & label) void InsetMathHull::numbered(row_type row, Numbered num) { numbered_[row] = num; - if (!numbered(row) && label_[row]) { - delete label_[row]; - label_[row] = 0; - } } @@ -963,8 +920,8 @@ bool InsetMathHull::ams() const case hullEqnArray: break; } - for (size_t row = 0; row < numbered_.size(); ++row) - if (numbered_[row] == NOTAG) + for (auto const & row : numbered_) + if (row == NOTAG) return true; return false; } @@ -993,7 +950,7 @@ bool InsetMathHull::outerDisplay() const } -Inset::DisplayType InsetMathHull::display() const +int InsetMathHull::rowFlags() const { switch (type_) { case hullUnknown: @@ -1011,12 +968,12 @@ Inset::DisplayType InsetMathHull::display() const case hullMultline: case hullGather: if (buffer().params().is_math_indent) - return AlignLeft; + return Display | AlignLeft; else - return AlignCenter; + return Display; } // avoid warning - return AlignCenter; + return Display; } @@ -1024,7 +981,7 @@ int InsetMathHull::indent(BufferView const & bv) const { // FIXME: set this in the textclass. This value is what the article class uses. static Length default_indent(2.5, Length::EM); - if (display() != Inline && buffer().params().is_math_indent) { + if (display() && buffer().params().is_math_indent) { Length const & len = buffer().params().getMathIndent(); if (len.empty()) return bv.inPixels(default_indent); @@ -1077,9 +1034,9 @@ void InsetMathHull::validate(LaTeXFeatures & features) const + bgcol + "}{\\ensuremath{\\mathtt{#1}}}}"); features.addPreambleSnippet( from_ascii("\\newcommand{\\endregexp}{}")); - } else if (outerDisplay() && features.inDeletedInset() - && !features.mustProvide("ct-dvipost")) { - features.require("ct-tikz-math-sout"); + } else if (outerDisplay() && features.inDeletedInset()) { + features.require("tikz"); + features.require("ct-tikz-object-sout"); } // Validation is necessary only if not using AMS math. @@ -1102,7 +1059,38 @@ void InsetMathHull::validate(LaTeXFeatures & features) const } -void InsetMathHull::header_write(WriteStream & os) const +CtObject InsetMathHull::getCtObject(OutputParams const & runparams) const +{ + CtObject res = CtObject::Normal; + switch(type_) { + case hullNone: + case hullSimple: + case hullAlignAt: + case hullXAlignAt: + case hullXXAlignAt: + case hullRegexp: + case hullUnknown: + break; + + case hullEquation: + case hullEqnArray: + case hullAlign: + case hullFlAlign: + case hullGather: + case hullMultline: { + if (runparams.inulemcmd + && (!runparams.local_font || runparams.local_font->fontInfo().strikeout() != FONT_ON)) + res = CtObject::UDisplayObject; + else + res = CtObject::DisplayObject; + break; + } + } + return res; +} + + +void InsetMathHull::header_write(TeXMathStream & os) const { bool n = numberedType(); @@ -1168,7 +1156,7 @@ void InsetMathHull::header_write(WriteStream & os) const } -void InsetMathHull::footer_write(WriteStream & os) const +void InsetMathHull::footer_write(TeXMathStream & os) const { bool n = numberedType(); @@ -1351,8 +1339,11 @@ void InsetMathHull::delCol(col_type col) docstring InsetMathHull::nicelabel(row_type row) const { - if (!numbered(row)) - return docstring(); + if (!numbered(row)) { + if (!label_[row]) + return docstring(); + return '[' + label_[row]->screenLabel() + ']'; + } docstring const & val = numbers_[row]; if (!label_[row]) return '(' + val + ')'; @@ -1365,7 +1356,7 @@ void InsetMathHull::glueall(HullType type) MathData ar; for (idx_type i = 0; i < nargs(); ++i) ar.append(cell(i)); - InsetLabel * label = 0; + InsetLabel * label = nullptr; if (type == hullEquation) { // preserve first non-empty label for (row_type row = 0; row < nrows(); ++row) { @@ -1516,7 +1507,7 @@ void InsetMathHull::mutate(HullType newtype) numbered(0, false); } else { setType(hullEquation); - numbered(0, label_[0] ? true : false); + numbered(0, label_[0] != nullptr); mutate(newtype); } break; @@ -1655,30 +1646,33 @@ void InsetMathHull::mutate(HullType newtype) } -docstring InsetMathHull::eolString(row_type row, bool fragile, bool latex, - bool last_eoln) const +void InsetMathHull::eol(TeXMathStream & os, row_type row, bool fragile, bool latex, + bool last_eoln) const { - docstring res; if (numberedType()) { - if (label_[row] && numbered(row)) { + if (label_[row]) { docstring const name = latex ? escape(label_[row]->getParam("name")) : label_[row]->getParam("name"); - res += "\\label{" + name + '}'; + os << "\\label{" + name + '}'; } if (type_ != hullMultline) { if (numbered_[row] == NONUMBER) - res += "\\nonumber "; + os << "\\nonumber "; else if (numbered_[row] == NOTAG) - res += "\\notag "; + os<< "\\notag "; + } + if (os.output() == TeXMathStream::wsPreview && !numbers_[row].empty()) { + os << "\\global\\def\\theequation{" << numbers_[row] << "}\n"; } + } // Never add \\ on the last empty line of eqnarray and friends last_eoln = false; - return res + InsetMathGrid::eolString(row, fragile, latex, last_eoln); + InsetMathGrid::eol(os, row, fragile, latex, last_eoln); } -void InsetMathHull::write(WriteStream & os) const +void InsetMathHull::write(TeXMathStream & os) const { ModeSpecifier specifier(os, MATH_MODE); header_write(os); @@ -1711,13 +1705,11 @@ void InsetMathHull::check() const void InsetMathHull::doExtern(Cursor & cur, FuncRequest & func) { - docstring dlang; - docstring extra; - idocstringstream iss(func.argument()); - iss >> dlang >> extra; + //FIXME: sort out whether we want std::string or docstring for those + string const lang = func.getArg(0); + docstring extra = from_utf8(func.getArg(1)); if (extra.empty()) extra = from_ascii("noextra"); - string const lang = to_ascii(dlang); // replace selection with result of computation if (reduceSelectionToOneCell(cur)) { @@ -1833,11 +1825,22 @@ void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd) //lyxerr << "toggling all numbers" << endl; cur.recordUndoInset(); bool old = numberedType(); - if (type_ == hullMultline) - numbered(nrows() - 1, !old); - else - for (row_type row = 0; row < nrows(); ++row) + if (type_ == hullMultline) { + row_type row = nrows() - 1; + numbered(row, !old); + if (old && label_[row]) { + delete label_[row]; + label_[row] = 0; + } + } else { + for (row_type row = 0; row < nrows(); ++row) { numbered(row, !old); + if (old && label_[row]) { + delete label_[row]; + label_[row] = 0; + } + } + } cur.message(old ? _("No number") : _("Number")); cur.forceBufferUpdate(); @@ -1850,6 +1853,10 @@ void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd) bool old = numbered(r); cur.message(old ? _("No number") : _("Number")); numbered(r, !old); + if (old && label_[r]) { + delete label_[r]; + label_[r] = 0; + } cur.forceBufferUpdate(); break; } @@ -1884,7 +1891,7 @@ void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd) // if there is an argument, find the corresponding label, else // check whether there is at least one label. for (row = 0; row != nrows(); ++row) - if (numbered(row) && label_[row] + if (label_[row] && (cmd.argument().empty() || label(row) == cmd.argument())) break; } @@ -2053,15 +2060,15 @@ bool InsetMathHull::getStatus(Cursor & cur, FuncRequest const & cmd, return true; } case LFUN_MATH_DISPLAY: { - status.setEnabled(display() != Inline || allowDisplayMath(cur)); - status.setOnOff(display() != Inline); + status.setEnabled(display() || allowDisplayMath(cur)); + status.setOnOff(display()); return true; } case LFUN_MATH_NUMBER_TOGGLE: // FIXME: what is the right test, this or the one of // LABEL_INSERT? - status.setEnabled(display() != Inline); + status.setEnabled(display()); status.setOnOff(numberedType()); return true; @@ -2070,7 +2077,7 @@ bool InsetMathHull::getStatus(Cursor & cur, FuncRequest const & cmd, // LABEL_INSERT? bool const enable = (type_ == hullMultline) ? (nrows() - 1 == cur.row()) - : display() != Inline; + : display(); row_type const r = (type_ == hullMultline) ? nrows() - 1 : cur.row(); status.setEnabled(enable); status.setOnOff(enable && numbered(r)); @@ -2087,12 +2094,12 @@ bool InsetMathHull::getStatus(Cursor & cur, FuncRequest const & cmd, // if there is no argument and we're inside math, we retrieve // the row number from the cursor position. row_type row = (type_ == hullMultline) ? nrows() - 1 : cur.row(); - enabled = numberedType() && label_[row] && numbered(row); + enabled = numberedType() && label_[row]; } else { // if there is an argument, find the corresponding label, else // check whether there is at least one label. for (row_type row = 0; row != nrows(); ++row) { - if (numbered(row) && label_[row] && + if (label_[row] && (cmd.argument().empty() || label(row) == cmd.argument())) { enabled = true; break; @@ -2223,7 +2230,7 @@ void InsetMathHull::handleFont2(Cursor & cur, docstring const & arg) font.fromString(to_utf8(arg), b); if (font.fontInfo().color() != Color_inherit) { MathAtom at = MathAtom(new InsetMathColor(buffer_, true, font.fontInfo().color())); - cur.handleNest(at, 0); + cur.handleNest(at); } } @@ -2289,7 +2296,7 @@ void InsetMathHull::write(ostream & os) const { odocstringstream oss; otexrowstream ots(oss); - WriteStream wi(ots, false, false, WriteStream::wsDefault); + TeXMathStream wi(ots, false, false, TeXMathStream::wsDefault); oss << "Formula "; write(wi); os << to_utf8(oss.str()); @@ -2333,12 +2340,19 @@ int InsetMathHull::plaintext(odocstringstream & os, odocstringstream oss; otexrowstream ots(oss); Encoding const * const enc = encodings.fromLyXName("utf8"); - WriteStream wi(ots, false, true, WriteStream::wsDefault, enc); + TeXMathStream::OutputType ot; + if (op.find_effective()) + ot = TeXMathStream::wsSearchAdv; + else + ot = TeXMathStream::wsDefault; // Fix Bug #6139 - if (type_ == hullRegexp) + if (type_ == hullRegexp) { + TeXMathStream wi(ots, false, true, ot, enc); write(wi); + } else { + TeXMathStream wi(ots, false, true, ot, enc); for (row_type r = 0; r < nrows(); ++r) { for (col_type c = 0; c < ncols(); ++c) wi << (c == 0 ? "" : "\t") << cell(index(r, c)); @@ -2356,60 +2370,105 @@ int InsetMathHull::plaintext(odocstringstream & os, } -int InsetMathHull::docbook(odocstream & os, OutputParams const & runparams) const -{ - MathStream ms(os); - int res = 0; +void InsetMathHull::docbook(XMLStream & xs, OutputParams const & runparams) const { + // Choose the tag around the MathML equation. docstring name; + bool doCR = false; if (getType() == hullSimple) name = from_ascii("inlineequation"); - else + else { + doCR = true; // This is a block equation, always have on its own line. name = from_ascii("informalequation"); + } - docstring bname = name; - if (!label(0).empty()) - bname += " id='" + sgml::cleanID(buffer(), runparams, label(0)) + "'"; + // DocBook also has , but it comes with a title. + // TODO: recognise \tag from amsmath? This would allow having with a proper title. - ++ms.tab(); ms.cr(); ms.os() << '<' << bname << '>'; + docstring attr; - odocstringstream ls; - otexstream ols(ls); - if (runparams.flavor == OutputParams::XML) { - ms << MTag("alt role='tex' "); - // Workaround for db2latex: db2latex always includes equations with - // \ensuremath{} or \begin{display}\end{display} - // so we strip LyX' math environment - WriteStream wi(ols, false, false, WriteStream::wsDefault, runparams.encoding); - InsetMathGrid::write(wi); - ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&"), "<", "<")); - ms << ETag("alt"); - ms << MTag("math"); - ms << ETag("alt"); - ms << MTag("math"); - InsetMathGrid::mathmlize(ms); - ms << ETag("math"); - } else { - ms << MTag("alt role='tex'"); - latex(ols, runparams); - res = ols.texrow().rows(); - ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&"), "<", "<")); - ms << ETag("alt"); + bool mathmlNamespaceInline = buffer().params().docbook_mathml_prefix == BufferParams::NoPrefix; + if (mathmlNamespaceInline) + attr += "xmlns=\"http://www.w3.org/1998/Math/MathML\""; + + for (row_type i = 0; i < nrows(); ++i) { + if (!label(i).empty()) { + if (!attr.empty()) + attr += " "; + + attr += "xml:id=\"" + xml::cleanID(label(i)) + "\""; + break; + } } - ms << from_ascii(""); - else - ms << from_ascii("\">"); + xs << xml::StartTag(name, attr); + xs << xml::CR(); + + // With DocBook 5, MathML must be within its own namespace (defined in Buffer.cpp::writeDocBookSource, except when + // it should be inlined). + // Output everything in a separate stream so that this does not interfere with the standard flow of DocBook tags. + std::string mathmlNamespacePrefix; + if (!mathmlNamespaceInline) { + if (buffer().params().docbook_mathml_prefix == BufferParams::MPrefix) + mathmlNamespacePrefix = "m"; + else if (buffer().params().docbook_mathml_prefix == BufferParams::MMLPrefix) + mathmlNamespacePrefix = "mml"; + } + + odocstringstream osmath; + MathMLStream ms(osmath, mathmlNamespacePrefix); + + // Output the MathML subtree. + // TeX transcription. Avoid MTag/ETag so that there are no extraneous spaces. + ms << "<" << from_ascii("alt") << " role='tex'" << ">"; + // Workaround for db2latex: db2latex always includes equations with + // \ensuremath{} or \begin{display}\end{display} + // so we strip LyX' math environment + odocstringstream ls; + otexstream ols(ls); + TeXMathStream wi(ols, false, false, TeXMathStream::wsDefault, runparams.encoding); + InsetMathGrid::write(wi); + ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&"), "<", "<")); + ms << ""; + + // Actual transformation of the formula into MathML. This translation may fail (for example, due to custom macros). + // The new output stream is required to deal with the errors: first write completely the formula into this + // temporary stream; then, if it is possible without error, then copy it back to the "real" stream. Otherwise, + // some incomplete tags might be put into the real stream. + try { + // First, generate the MathML expression. If there is an error in the generation, this block is not fully + // executed, and the formula is not output to the DocBook stream. + odocstringstream ostmp; + MathMLStream mstmp(ostmp, ms.xmlns()); + mathmlize(mstmp); + + // Choose the display style for the formula, to be output as an attribute near the formula root. + std::string mathmlAttr; + if (getType() == hullSimple) + mathmlAttr = "display=\"inline\""; + else + mathmlAttr = "display=\"block\""; - ms.cr(); --ms.tab(); ms.os() << "'; + // Then, output the formula. + ms << MTag("math", mathmlAttr); + ms.cr(); + osmath << ostmp.str(); // osmath is not a XMLStream, so no need for XMLStream::ESCAPE_NONE. + ms << ETag("math"); + } catch (MathExportException const &) { + ms.cr(); + osmath << "MathML export failed. Please report this as a bug to the LyX developers: " + "https://www.lyx.org/trac."; + } - return ms.line() + res; + // Output the complete formula to the DocBook stream. + xs << XMLStream::ESCAPE_NONE << osmath.str(); + xs << xml::CR(); + xs << xml::EndTag(name); + if (doCR) + xs << xml::CR(); } @@ -2469,46 +2528,61 @@ void InsetMathHull::htmlize(HtmlStream & os) const // this duplicates code from InsetMathGrid, but // we need access here to number information, // and we simply do not have that in InsetMathGrid. -void InsetMathHull::mathmlize(MathStream & os) const +void InsetMathHull::mathmlize(MathMLStream & ms) const { - bool const havenumbers = haveNumbers(); - bool const havetable = havenumbers || nrows() > 1 || ncols() > 1; + bool const havetable = haveNumbers() || nrows() > 1 || ncols() > 1; + + // Simplest case: single row, single column, no numbering. + if (!havetable) { + ms << cell(index(0, 0)); + return; + } + + // More complex case: wrap elements in a table. + if (getType() == hullSimple) { + ms << MTag("mtable"); + } else if (getType() >= hullAlign && getType() <= hullXXAlignAt) { + // hullAlign, hullAlignAt, hullXAlignAt, hullXXAlignAt + string alignment; + for (col_type col = 0; col < ncols(); ++col) { + alignment += (col % 2) ? "left " : "right "; + } + ms << MTag("mtable", "displaystyle='true' columnalign='" + alignment + "'"); + } else { + ms << MTag("mtable", "displaystyle='true'"); + } - if (havetable) - os << MTag("mtable"); - char const * const celltag = havetable ? "mtd" : "mrow"; - // FIXME There does not seem to be wide support at the moment - // for mlabeledtr, so we have to use just mtr for now. - // char const * const rowtag = havenumbers ? "mlabeledtr" : "mtr"; - char const * const rowtag = "mtr"; for (row_type row = 0; row < nrows(); ++row) { - if (havetable) - os << MTag(rowtag); + // No Wed browser supports mlabeledtr in 2023, even though it has been introduced in + // MathML 2 (2003). Moreover, it is not supported in MathML 4 Core. + ms << MTag("mtr"); + for (col_type col = 0; col < ncols(); ++col) { - os << MTag(celltag) + ms << MTag("mtd") << cell(index(row, col)) - << ETag(celltag); + << ETag("mtd"); } + // fleqn? - if (havenumbers) { - os << MTag("mtd"); + if (haveNumbers()) { + ms << MTag("mtd"); docstring const & num = numbers_[row]; if (!num.empty()) - os << '(' << num << ')'; - os << ETag("mtd"); + ms << MTagInline("mtext") << '(' << num << ')' << ETagInline("mtext"); + ms << ETag("mtd"); } - if (havetable) - os << ETag(rowtag); + + ms << ETag("mtr"); } - if (havetable) - os << ETag("mtable"); + + ms << ETag("mtable"); } -void InsetMathHull::mathAsLatex(WriteStream & os) const +void InsetMathHull::mathAsLatex(TeXMathStream & os) const { MathEnsurer ensurer(os, false); - bool havenumbers = haveNumbers(); + bool const havenumbers = haveNumbers(); bool const havetable = havenumbers || nrows() > 1 || ncols() > 1; if (!havetable) { @@ -2529,7 +2603,7 @@ void InsetMathHull::mathAsLatex(WriteStream & os) const docstring const & num = numbers_[row]; if (!num.empty()) os << '(' << num << ')'; - os << ""; + os << ""; } os << ""; } @@ -2537,7 +2611,7 @@ void InsetMathHull::mathAsLatex(WriteStream & os) const } -docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const & op) const +docstring InsetMathHull::xhtml(XMLStream & xs, OutputParams const & op) const { BufferParams::MathOutput const mathtype = buffer().masterBuffer()->params().html_math_output; @@ -2556,22 +2630,29 @@ docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const & op) const // FIXME Eventually we would like to do this inset by inset. if (mathtype == BufferParams::MathML) { odocstringstream os; - MathStream ms(os); + MathMLStream ms(os); try { mathmlize(ms); success = true; } catch (MathExportException const &) {} if (success) { + string const name_space_declaration = "xmlns='http://www.w3.org/1998/Math/MathML'"; if (getType() == hullSimple) - xs << html::StartTag("math", - "xmlns=\"http://www.w3.org/1998/Math/MathML\"", true); - else - xs << html::StartTag("math", - "display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"", true); - xs << XHTMLStream::ESCAPE_NONE - << os.str() - << html::EndTag("math"); + xs << xml::StartTag("math", name_space_declaration, true); + else { + if (!xs.isLastTagCR()) + xs << xml::CR(); + xs << xml::StartTag("math", name_space_declaration + " display='block'", true); + } + + xs << XMLStream::ESCAPE_NONE + << os.str(); + + if (!xs.isLastTagCR()) + xs << xml::CR(); + xs << xml::EndTag("math") << xml::CR(); } + // In case of failure, generate an image. } else if (mathtype == BufferParams::HTML) { odocstringstream os; HtmlStream ms(os); @@ -2581,11 +2662,12 @@ docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const & op) const } catch (MathExportException const &) {} if (success) { string const tag = (getType() == hullSimple) ? "span" : "div"; - xs << html::StartTag(tag, "class='formula'", true) - << XHTMLStream::ESCAPE_NONE + xs << xml::StartTag(tag, "class='formula'", true) + << XMLStream::ESCAPE_NONE << os.str() - << html::EndTag(tag); + << xml::EndTag(tag); } + // In case of failure, generate an image. } // what we actually want is this: @@ -2600,7 +2682,7 @@ docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const & op) const // // so this is for Images. if (!success && mathtype != BufferParams::LaTeX) { - graphics::PreviewImage const * pimage = 0; + graphics::PreviewImage const * pimage = nullptr; if (!op.dryrun) { loadPreview(docit_); pimage = preview_->getPreviewImage(buffer()); @@ -2624,11 +2706,11 @@ docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const & op) const } string const tag = (getType() == hullSimple) ? "span" : "div"; - xs << html::CR() - << html::StartTag(tag, "style = \"text-align: center;\"") - << html::CompTag("img", "src=\"" + filename + "\" alt=\"Mathematical Equation\"") - << html::EndTag(tag) - << html::CR(); + xs << xml::CR() + << xml::StartTag(tag, "style = \"text-align: center;\"") + << xml::CompTag("img", "src=\"" + filename + "\" alt=\"Mathematical Equation\"") + << xml::EndTag(tag) + << xml::CR(); success = true; } } @@ -2641,7 +2723,7 @@ docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const & op) const // $...$ or whatever. odocstringstream ls; otexrowstream ots(ls); - WriteStream wi(ots, false, true, WriteStream::wsPreview); + TeXMathStream wi(ots, false, true, TeXMathStream::wsPreview); ModeSpecifier specifier(wi, MATH_MODE); mathAsLatex(wi); docstring const latex = ls.str(); @@ -2651,10 +2733,10 @@ docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const & op) const // FIXME XHTML // probably should allow for some kind of customization here string const tag = (getType() == hullSimple) ? "span" : "div"; - xs << html::StartTag(tag, "class='math'") + xs << xml::StartTag(tag, "class='math'") << latex - << html::EndTag(tag) - << html::CR(); + << xml::EndTag(tag) + << xml::CR(); } return docstring(); } @@ -2663,7 +2745,7 @@ docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const & op) const void InsetMathHull::toString(odocstream & os) const { odocstringstream ods; - plaintext(ods, OutputParams(0)); + plaintext(ods, OutputParams(nullptr)); os << ods.str(); } @@ -2671,7 +2753,7 @@ void InsetMathHull::toString(odocstream & os) const void InsetMathHull::forOutliner(docstring & os, size_t const, bool const) const { odocstringstream ods; - OutputParams op(0); + OutputParams op(nullptr); op.for_toc = true; // FIXME: this results in spilling TeX into the LyXHTML output since the // outliner is used to generate the LyXHTML list of figures/etc. @@ -2695,7 +2777,7 @@ void InsetMathHull::recordLocation(DocIterator const & di) bool InsetMathHull::canPaintChange(BufferView const &) const { // We let RowPainter do it seamlessly for inline insets - return display() != Inline; + return display(); }