2 * \file InsetMathHull.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
13 #include "InsetMathHull.h"
15 #include "InsetMathChar.h"
16 #include "InsetMathColor.h"
17 #include "InsetMathFrac.h"
18 #include "InsetMathNest.h"
19 #include "InsetMathScript.h"
20 #include "MathExtern.h"
21 #include "MathFactory.h"
22 #include "MathStream.h"
23 #include "MathSupport.h"
26 #include "BufferParams.h"
27 #include "BufferView.h"
30 #include "CutAndPaste.h"
33 #include "FuncRequest.h"
34 #include "FuncStatus.h"
36 #include "LaTeXFeatures.h"
38 #include "MacroTable.h"
39 #include "InsetMathMacro.h"
40 #include "InsetMathMacroTemplate.h"
41 #include "MetricsInfo.h"
42 #include "Paragraph.h"
43 #include "ParIterator.h"
46 #include "TextClass.h"
47 #include "TextPainter.h"
48 #include "TocBackend.h"
50 #include "insets/InsetLabel.h"
51 #include "insets/InsetRef.h"
52 #include "insets/RenderPreview.h"
54 #include "graphics/PreviewImage.h"
55 #include "graphics/PreviewLoader.h"
57 #include "frontends/alert.h"
58 #include "frontends/FontMetrics.h"
59 #include "frontends/Painter.h"
61 #include "support/convert.h"
62 #include "support/debug.h"
63 #include "support/gettext.h"
64 #include "support/filetools.h"
65 #include "support/lassert.h"
66 #include "support/lstrings.h"
67 #include "support/Changer.h"
72 using namespace lyx::support;
76 using cap::grabAndEraseSelection;
77 using cap::reduceSelectionToOneCell;
81 int getCols(HullType type)
106 // returns position of first relation operator in the array
107 // used for "intelligent splitting"
108 size_t firstRelOp(MathData const & ar)
110 for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
111 if ((*it)->mathClass() == MC_REL)
112 return it - ar.begin();
117 char const * star(bool numbered)
119 return numbered ? "" : "*";
123 // writes a preamble for underlined or struck out math display
124 void writeMathdisplayPreamble(TeXMathStream & os)
126 if (os.strikeoutMath())
129 if (os.ulemCmd() == TeXMathStream::UNDERLINE)
130 os << "\\raisebox{-\\belowdisplayshortskip}{"
131 "\\parbox[b]{\\linewidth}{";
132 else if (os.ulemCmd() == TeXMathStream::STRIKEOUT)
133 os << "\\parbox{\\linewidth}{";
137 // writes a postamble for underlined or struck out math display
138 void writeMathdisplayPostamble(TeXMathStream & os)
140 if (os.strikeoutMath())
143 if (os.ulemCmd() == TeXMathStream::UNDERLINE)
145 else if (os.ulemCmd() == TeXMathStream::STRIKEOUT)
152 static InsetLabel * dummy_pointer = 0;
154 InsetMathHull::InsetMathHull(Buffer * buf)
155 : InsetMathGrid(buf, 1, 1), type_(hullNone), numbered_(1, NONUMBER),
156 numbers_(1, empty_docstring()), label_(1, dummy_pointer),
157 preview_(new RenderPreview(this))
159 //lyxerr << "sizeof InsetMath: " << sizeof(InsetMath) << endl;
160 //lyxerr << "sizeof MetricsInfo: " << sizeof(MetricsInfo) << endl;
161 //lyxerr << "sizeof InsetMathChar: " << sizeof(InsetMathChar) << endl;
162 //lyxerr << "sizeof FontInfo: " << sizeof(FontInfo) << endl;
169 InsetMathHull::InsetMathHull(Buffer * buf, HullType type)
170 : InsetMathGrid(buf, getCols(type), 1), type_(type), numbered_(1, NONUMBER),
171 numbers_(1, empty_docstring()), label_(1, dummy_pointer),
172 preview_(new RenderPreview(this))
180 InsetMathHull::InsetMathHull(InsetMathHull const & other) : InsetMathGrid(other)
186 InsetMathHull::~InsetMathHull()
188 for (auto & i : label_)
193 Inset * InsetMathHull::clone() const
195 return new InsetMathHull(*this);
199 InsetMathHull & InsetMathHull::operator=(InsetMathHull const & other)
203 InsetMathGrid::operator=(other);
205 numbered_ = other.numbered_;
206 numbers_ = other.numbers_;
207 buffer_ = other.buffer_;
208 for (auto & i : label_)
210 label_ = other.label_;
211 for (size_t i = 0; i != label_.size(); ++i) {
213 label_[i] = new InsetLabel(*label_[i]);
215 preview_.reset(new RenderPreview(*other.preview_, this));
221 void InsetMathHull::setBuffer(Buffer & buffer)
223 InsetMathGrid::setBuffer(buffer);
225 for (size_t i = 0; i != label_.size(); ++i) {
227 label_[i]->setBuffer(buffer);
232 // FIXME This should really be controlled by the TOC level, or
233 // something of the sort.
235 const char * counters_to_save[] = {"section", "chapter"};
236 unsigned int const numcnts = sizeof(counters_to_save)/sizeof(char *);
240 void InsetMathHull::updateBuffer(ParIterator const & it, UpdateType utype, bool const deleted)
243 //FIXME: buffer_ should be set at creation for this inset! Problem is
244 // This inset is created at too many places (see Parser::parse1() in
249 // if any of the equations are numbered, then we want to save the values
250 // of some of the counters.
252 BufferParams const & bp = buffer_->params();
253 string const & lang = it->getParLanguage(bp)->code();
255 buffer_->masterBuffer()->params().documentClass().counters();
257 // right now, we only need to do this at export time
258 if (utype == OutputUpdate) {
259 for (size_t i = 0; i < numcnts; ++i) {
260 docstring const cnt = from_ascii(counters_to_save[i]);
261 if (cnts.hasCounter(cnt))
262 counter_map[cnt] = cnts.value(cnt);
266 // this has to be done separately
267 docstring const eqstr = from_ascii("equation");
268 if (cnts.hasCounter(eqstr)) {
269 if (utype == OutputUpdate)
270 counter_map[eqstr] = cnts.value(eqstr);
271 for (size_t i = 0; i != label_.size(); ++i) {
273 Paragraph const & par = it.paragraph();
274 if (!par.isDeleted(it.pos())) {
275 cnts.step(eqstr, utype);
276 numbers_[i] = cnts.theCounter(eqstr, lang);
278 numbers_[i] = from_ascii("#");
280 numbers_[i] = empty_docstring();
286 for (size_t i = 0; i != label_.size(); ++i) {
288 label_[i]->updateBuffer(it, utype, deleted);
291 InsetMathGrid::updateBuffer(it, utype, deleted);
295 void InsetMathHull::addToToc(DocIterator const & pit, bool output_active,
296 UpdateType utype, TocBackend & backend) const
299 //FIXME: buffer_ should be set at creation for this inset! Problem is
300 // This inset is created at too many places (see Parser::parse1() in
305 TocBuilder & b = backend.builder("equation");
306 // compute first and last item
307 row_type first = nrows();
308 for (row_type row = 0; row != nrows(); ++row)
313 if (first == nrows())
316 row_type last = nrows() - 1;
317 for (; last != 0; --last)
320 // add equation numbers
321 b.pushItem(pit, docstring(), output_active);
323 b.argumentItem(bformat(from_ascii("(%1$s-%2$s)"),
324 numbers_[first], numbers_[last]));
325 for (row_type row = 0; row != nrows(); ++row) {
329 label_[row]->addToToc(pit, output_active, utype, backend);
330 docstring label = nicelabel(row);
332 // this is the only equation
333 b.argumentItem(label);
335 // insert as sub-items
336 b.pushItem(pit, label, output_active);
344 Inset * InsetMathHull::editXY(Cursor & cur, int x, int y)
346 if (previewState(&cur.bv())) {
350 return InsetMathNest::editXY(cur, x, y);
354 InsetMath::mode_type InsetMathHull::currentMode() const
358 return UNDECIDED_MODE;
360 // definitely math mode ...
380 // FIXME: InsetMathGrid should be changed to let the real column alignment be
381 // given by a virtual method like displayColAlign, because the values produced
382 // by defaultColAlign can be invalidated by lfuns such as add-column. For the
383 // moment the values produced by defaultColAlign are not used, notably because
384 // alignment is not implemented in the LyXHTML output.
385 char InsetMathHull::defaultColAlign(col_type col)
387 return colAlign(type_, col);
391 char InsetMathHull::displayColAlign(idx_type idx) const
395 row_type const r = row(idx);
398 if (r == nrows() - 1)
409 return colAlign(type_, col(idx));
413 return InsetMathGrid::displayColAlign(idx);
417 int InsetMathHull::displayColSpace(col_type col) const
419 return colSpace(type_, col);
423 // FIXME: same comment as for defaultColAlign applies.
424 int InsetMathHull::defaultColSpace(col_type col)
426 return colSpace(type_, col);
430 string InsetMathHull::standardFont() const
436 return "lyxnochange";
443 ColorCode InsetMathHull::standardColor() const
448 return Color_foreground;
456 bool InsetMathHull::previewState(const BufferView *const bv) const
458 if (!editing(bv) && RenderPreview::previewMath()
459 && type_ != hullRegexp)
461 graphics::PreviewImage const * pimage =
462 preview_->getPreviewImage(bv->buffer());
463 return pimage && pimage->image();
470 const int ERROR_FRAME_WIDTH = 2;
472 bool previewTooSmall(Dimension const & dim)
474 return dim.width() <= 10 && dim.height() <= 10;
479 void InsetMathHull::metrics(MetricsInfo & mi, Dimension & dim) const
481 /* Compute \(above|below)displayskip
482 true value in LaTeX is 10pt plus 2pt minus 5pt (in normal size at 10pt)
483 But 12 pixels is what we are used to.
485 int const bottom_display_margin = 12;
486 int top_display_margin = bottom_display_margin;
487 // at start of paragraph, add an empty line
489 top_display_margin += theFontMetrics(mi.base.font).maxHeight() + 2;
491 if (previewState(mi.base.bv)) {
492 preview_->metrics(mi, dim);
493 if (previewTooSmall(dim)) {
494 // preview image is too small
495 dim.wid += 2 * ERROR_FRAME_WIDTH;
496 dim.asc += 2 * ERROR_FRAME_WIDTH;
498 // insert a gap in front of the formula
499 // value was hardcoded to 1 pixel
500 dim.wid += mi.base.bv->zoomedPixels(1) ;
502 dim.asc += top_display_margin;
503 dim.des += bottom_display_margin;
510 Changer dummy1 = mi.base.changeFontSet(standardFont());
511 Changer dummy2 = mi.base.font.changeStyle(display() ? DISPLAY_STYLE
514 // let the cells adjust themselves
515 InsetMathGrid::metrics(mi, dim);
518 // Check whether the numbering interferes with the equations
519 if (numberedType()) {
520 BufferParams::MathNumber const math_number = buffer().params().getMathNumber();
521 int extra_offset = 0;
522 for (row_type row = 0; row < nrows(); ++row) {
523 rowinfo(row).offset[mi.base.bv] += extra_offset;
526 docstring const nl = nicelabel(row);
528 mathed_string_dim(mi.base.font, nl, dimnl);
529 int const ind = indent(*mi.base.bv);
530 int const x = ind ? ind : (mi.base.textwidth - dim.wid) / 2;
531 // for some reason metrics does not trigger at the
532 // same point as draw, and therefore we use >= instead of >
533 if ((math_number == BufferParams::LEFT && dimnl.wid >= x)
534 || (math_number == BufferParams::RIGHT
535 && dimnl.wid >= mi.base.textwidth - x - dim.wid)) {
536 extra_offset += dimnl.height();
539 dim.des += extra_offset;
544 dim.asc += top_display_margin;
545 dim.des += bottom_display_margin;
548 // reserve some space for marker.
553 ColorCode InsetMathHull::backgroundColor(PainterInfo const & pi) const
555 BufferView const * const bv = pi.base.bv;
556 if (previewState(bv)) {
557 Dimension const dim = dimension(*pi.base.bv);
558 if (previewTooSmall(dim))
560 return graphics::PreviewLoader::backgroundColor();
566 void InsetMathHull::drawMarkers(PainterInfo & pi, int x, int y) const
568 ColorCode pen_color = mouseHovered(pi.base.bv) || editing(pi.base.bv)?
569 Color_mathframe : Color_mathcorners;
570 // If the corners have the same color as the background, do not paint them.
571 if (lcolor.getX11HexName(Color_mathbg) == lcolor.getX11HexName(pen_color))
574 Inset::drawMarkers(pi, x, y);
575 Dimension const dim = dimension(*pi.base.bv);
576 int const t = x + dim.width() - 1;
577 int const a = y - dim.ascent();
578 pi.pain.line(x, a + 3, x, a, pen_color);
579 pi.pain.line(t, a + 3, t, a, pen_color);
580 pi.pain.line(x, a, x + 3, a, pen_color);
581 pi.pain.line(t - 3, a, t, a, pen_color);
585 void InsetMathHull::drawBackground(PainterInfo & pi, int x, int y) const
587 Dimension const dim = dimension(*pi.base.bv);
588 if (previewTooSmall(dim)) {
589 pi.pain.fillRectangle(x, y - 2 * ERROR_FRAME_WIDTH,
590 dim.wid, dim.asc + dim.des, backgroundColor(pi));
593 // If there are numbers, the margins around the (displayed)
594 // equation have to be cleared.
596 pi.pain.fillRectangle(pi.leftx, y - dim.asc,
597 pi.rightx - pi.leftx, dim.height(), pi.background_color);
598 pi.pain.fillRectangle(x + 1, y - dim.asc + 1, dim.wid - 2,
599 dim.asc + dim.des - 1, pi.backgroundColor(this));
603 void InsetMathHull::draw(PainterInfo & pi, int x, int y) const
605 BufferView const * const bv = pi.base.bv;
606 Dimension const dim = dimension(*bv);
608 if (type_ == hullRegexp)
609 pi.pain.rectangle(x + 2, y - dim.ascent() + 1,
610 dim.width() - 3, dim.height() - 2, Color_regexpframe);
612 if (previewState(bv)) {
613 // Do not draw change tracking cue if taken care of by RowPainter
615 Changer dummy = !canPaintChange(*bv) ? changeVar(pi.change, Change())
617 if (previewTooSmall(dim)) {
618 // we have an extra frame
619 preview_->draw(pi, x + ERROR_FRAME_WIDTH, y);
621 // one pixel gap in front
622 // value was hardcoded to 1 pixel
623 int const gap = pi.base.bv->zoomedPixels(1) ;
624 preview_->draw(pi, x + gap, y);
629 // First draw the numbers
630 ColorCode color = pi.selected && lyxrc.use_system_colors
631 ? Color_selectiontext : standardColor();
632 bool const really_change_color = pi.base.font.color() == Color_none;
633 Changer dummy0 = really_change_color ? pi.base.font.changeColor(color)
635 if (numberedType()) {
636 BufferParams::MathNumber const math_number = buffer().params().getMathNumber();
637 for (row_type row = 0; row < nrows(); ++row) {
638 int yy = y + rowinfo(row).offset[bv];
639 docstring const nl = nicelabel(row);
641 mathed_string_dim(pi.base.font, nl, dimnl);
642 if (math_number == BufferParams::LEFT) {
643 if (dimnl.wid > x - pi.leftx)
644 yy += rowinfo(row).descent + dimnl.asc;
645 pi.draw(pi.leftx, yy, nl);
647 if (dimnl.wid > pi.rightx - x - dim.wid)
648 yy += rowinfo(row).descent + dimnl.asc;
649 pi.draw(pi.rightx - dimnl.wid, yy, nl);
654 // Then the equations
655 Changer dummy1 = pi.base.changeFontSet(standardFont());
656 Changer dummy2 = pi.base.font.changeStyle(display() ? DISPLAY_STYLE
658 InsetMathGrid::draw(pi, x + 1, y);
659 drawMarkers(pi, x, y);
661 // drawing change line
662 if (canPaintChange(*bv)) {
664 int const display_margin = display() ? pi.base.inPixels(Length(12, Length::PT)) : 0;
665 pi.change.paintCue(pi, x + 1, y + 1 - dim.asc + display_margin,
666 x + dim.wid, y + dim.des - display_margin);
671 void InsetMathHull::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
674 InsetMathGrid::metricsT(mi, dim);
677 otexrowstream ots(os);
678 TeXMathStream wi(ots, false, true, TeXMathStream::wsDefault);
680 dim.wid = os.str().size();
687 void InsetMathHull::drawT(TextPainter & pain, int x, int y) const
690 InsetMathGrid::drawT(pain, x, y);
693 otexrowstream ots(os);
694 TeXMathStream wi(ots, false, true, TeXMathStream::wsDefault);
696 pain.draw(x, y, os.str().c_str());
701 static docstring latexString(InsetMathHull const & inset)
704 // This has to be static, because a preview snippet or a math
705 // macro containing math in text mode (such as $\text{$\phi$}$ or
706 // \newcommand{\xxx}{\text{$\phi$}}) gets processed twice. The
707 // first time as a whole, and the second time only the inner math.
708 // In this last case inset.buffer() would be invalid.
709 static Encoding const * encoding = 0;
710 if (inset.isBufferValid())
711 encoding = &(inset.buffer().params().encoding());
712 otexrowstream ots(ls);
713 TeXMathStream wi(ots, false, true, TeXMathStream::wsPreview, encoding);
719 void InsetMathHull::initUnicodeMath() const
721 // Trigger classification of the unicode symbols in this inset
722 docstring const dummy = latexString(*this);
726 void InsetMathHull::addPreview(DocIterator const & inset_pos,
727 graphics::PreviewLoader & /*ploader*/) const
729 if (RenderPreview::previewMath()) {
730 preparePreview(inset_pos);
735 void InsetMathHull::usedMacros(MathData const & md, DocIterator const & pos,
736 MacroNameSet & macros, MacroNameSet & defs) const
738 MacroNameSet::iterator const end = macros.end();
740 for (size_t i = 0; i < md.size(); ++i) {
741 InsetMathMacro const * mi = md[i].nucleus()->asMacro();
742 InsetMathMacroTemplate const * mt = md[i].nucleus()->asMacroTemplate();
743 InsetMathScript const * si = md[i].nucleus()->asScriptInset();
744 InsetMathFracBase const * fi = md[i].nucleus()->asFracBaseInset();
745 InsetMathGrid const * gi = md[i].nucleus()->asGridInset();
746 InsetMathNest const * ni = md[i].nucleus()->asNestInset();
748 // Look for macros in the arguments of this macro.
749 for (idx_type idx = 0; idx < mi->nargs(); ++idx)
750 usedMacros(mi->cell(idx), pos, macros, defs);
751 // Make sure this is a macro defined in the document
752 // (as we also spot the macros in the symbols file)
753 // or that we have not already accounted for it.
754 docstring const name = mi->name();
755 if (macros.find(name) == end)
758 // Look for macros in the definition of this macro.
759 MathData ar(pos.buffer());
760 MacroData const * data =
761 pos.buffer()->getMacro(name, pos, true);
763 odocstringstream macro_def;
764 data->write(macro_def, true);
766 defs.insert(macro_def.str());
767 asArray(data->definition(), ar);
769 usedMacros(ar, pos, macros, defs);
771 MathData ar(pos.buffer());
772 asArray(mt->definition(), ar);
773 usedMacros(ar, pos, macros, defs);
775 if (!si->nuc().empty())
776 usedMacros(si->nuc(), pos, macros, defs);
778 usedMacros(si->down(), pos, macros, defs);
780 usedMacros(si->up(), pos, macros, defs);
781 } else if (fi || gi) {
782 idx_type nidx = fi ? fi->nargs() : gi->nargs();
783 for (idx_type idx = 0; idx < nidx; ++idx)
784 usedMacros(fi ? fi->cell(idx) : gi->cell(idx),
787 usedMacros(ni->cell(0), pos, macros, defs);
793 void InsetMathHull::preparePreview(DocIterator const & pos,
794 bool forexport) const
796 // there is no need to do all the macro stuff if we're not
797 // actually going to generate the preview.
798 if (!RenderPreview::previewMath() && !forexport)
801 Buffer const * buffer = pos.buffer();
803 // collect macros at this position
805 buffer->listMacroNames(macros);
807 // collect definitions only for the macros used in this inset
809 for (idx_type idx = 0; idx < nargs(); ++idx)
810 usedMacros(cell(idx), pos, macros, defs);
812 docstring macro_preamble;
813 for (auto const & defvar : defs)
814 macro_preamble.append(defvar);
816 // set the font series and size for this snippet
817 DocIterator dit = pos.getInnerText();
818 Paragraph const & par = dit.paragraph();
819 Font font = par.getFontSettings(buffer->params(), dit.pos());
820 font.fontInfo().realize(par.layout().font);
821 string const lsize = font.latexSize();
824 if (font.fontInfo().series() == BOLD_SERIES) {
825 setfont += from_ascii("\\textbf{");
828 if (lsize != "normalsize" && !prefixIs(lsize, "error"))
829 setfont += from_ascii("\\" + lsize + '\n');
832 if (forexport && haveNumbers()) {
833 docstring eqstr = from_ascii("equation");
834 CounterMap::const_iterator it = counter_map.find(eqstr);
835 if (it != counter_map.end()) {
836 int num = it->second;
838 setcnt += from_ascii("\\setcounter{") + eqstr + '}' +
839 '{' + convert<docstring>(num) + '}' + '\n';
841 for (size_t i = 0; i != numcnts; ++i) {
842 docstring cnt = from_ascii(counters_to_save[i]);
843 it = counter_map.find(cnt);
844 if (it == counter_map.end())
846 int num = it->second;
848 setcnt += from_ascii("\\setcounter{") + cnt + '}' +
849 '{' + convert<docstring>(num) + '}';
852 docstring const snippet = macro_preamble + setfont + setcnt
853 + latexString(*this) + endfont;
854 LYXERR(Debug::MACROS, "Preview snippet: " << snippet);
855 preview_->addPreview(snippet, *buffer, forexport);
859 void InsetMathHull::reloadPreview(DocIterator const & pos) const
862 preview_->startLoading(*pos.buffer());
866 void InsetMathHull::loadPreview(DocIterator const & pos) const
868 bool const forexport = true;
869 preparePreview(pos, forexport);
870 preview_->startLoading(*pos.buffer(), forexport);
874 bool InsetMathHull::notifyCursorLeaves(Cursor const & old, Cursor & cur)
876 if (RenderPreview::previewMath()) {
878 cur.screenUpdateFlags(Update::Force);
884 docstring InsetMathHull::label(row_type row) const
886 LASSERT(row < nrows(), return docstring());
887 if (InsetLabel * il = label_[row])
888 return il->screenLabel();
893 void InsetMathHull::label(row_type row, docstring const & label)
895 //lyxerr << "setting label '" << label << "' for row " << row << endl;
899 label_[row] = dummy_pointer;
902 label_[row]->updateLabelAndRefs(label);
904 label_[row]->setParam("name", label);
908 InsetCommandParams p(LABEL_CODE);
910 label_[row] = new InsetLabel(buffer_, p);
912 label_[row]->setBuffer(buffer());
916 void InsetMathHull::numbered(row_type row, Numbered num)
918 numbered_[row] = num;
919 if (!numbered(row) && label_[row]) {
926 bool InsetMathHull::numbered(row_type row) const
928 return numbered_[row] == NUMBER;
932 bool InsetMathHull::ams() const
952 for (auto const & row : numbered_)
959 bool InsetMathHull::outerDisplay() const
982 Inset::RowFlags InsetMathHull::rowFlags() const
999 if (buffer().params().is_math_indent)
1000 return Display | AlignLeft;
1009 int InsetMathHull::indent(BufferView const & bv) const
1011 // FIXME: set this in the textclass. This value is what the article class uses.
1012 static Length default_indent(2.5, Length::EM);
1013 if (display() && buffer().params().is_math_indent) {
1014 Length const & len = buffer().params().getMathIndent();
1016 return bv.inPixels(default_indent);
1018 return bv.inPixels(len);
1020 return Inset::indent(bv);
1024 bool InsetMathHull::numberedType() const
1043 for (row_type row = 0; row < nrows(); ++row)
1050 void InsetMathHull::validate(LaTeXFeatures & features) const
1052 if (features.runparams().isLaTeX()) {
1054 features.require("amsmath");
1056 if (type_ == hullRegexp) {
1057 features.require("color");
1058 docstring frcol = from_utf8(lcolor.getLaTeXName(Color_regexpframe));
1059 docstring bgcol = from_ascii("white");
1060 features.addPreambleSnippet(
1061 "\\newcommand{\\regexp}[1]{\\fcolorbox{"
1063 + bgcol + "}{\\ensuremath{\\mathtt{#1}}}}");
1064 features.addPreambleSnippet(
1065 from_ascii("\\newcommand{\\endregexp}{}"));
1066 } else if (outerDisplay() && features.inDeletedInset()) {
1067 features.require("tikz");
1068 features.require("ct-tikz-object-sout");
1071 // Validation is necessary only if not using AMS math.
1072 // To be safe, we will always run mathedvalidate.
1073 //if (features.amsstyle)
1076 //features.binom = true;
1077 } else if (features.runparams().math_flavor == OutputParams::MathAsHTML) {
1078 // it would be better to do this elsewhere, but we can't validate in
1079 // InsetMathMatrix and we have no way, outside MathExtern, to know if
1080 // we even have any matrices.
1081 features.addCSSSnippet(
1082 "table.matrix{display: inline-block; vertical-align: middle; text-align:center;}\n"
1083 "table.matrix td{padding: 0.25px;}\n"
1084 "td.ldelim{width: 0.5ex; border: thin solid black; border-right: none;}\n"
1085 "td.rdelim{width: 0.5ex; border: thin solid black; border-left: none;}");
1087 InsetMathGrid::validate(features);
1091 CtObject InsetMathHull::getCtObject(OutputParams const & runparams) const
1093 CtObject res = CtObject::Normal;
1109 case hullMultline: {
1110 if (runparams.inulemcmd
1111 && (!runparams.local_font || runparams.local_font->fontInfo().strikeout() != FONT_ON))
1112 res = CtObject::UDisplayObject;
1114 res = CtObject::DisplayObject;
1122 void InsetMathHull::header_write(TeXMathStream & os) const
1124 bool n = numberedType();
1135 if (cell(0).empty())
1140 writeMathdisplayPreamble(os);
1144 os << "\\begin{equation" << star(n) << "}\n";
1154 writeMathdisplayPreamble(os);
1157 os << "\\begin{" << hullName(type_) << star(n) << "}\n";
1164 os << "\\begin{" << hullName(type_) << star(n) << '}'
1165 << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
1171 os << "\\begin{" << hullName(type_) << '}'
1172 << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
1182 os << "\\begin{unknown" << star(n) << "}\n";
1188 void InsetMathHull::footer_write(TeXMathStream & os) const
1190 bool n = numberedType();
1207 os << "\\end{equation" << star(n) << "}\n";
1210 writeMathdisplayPostamble(os);
1220 os << "\\end{" << hullName(type_) << star(n) << "}\n";
1221 writeMathdisplayPostamble(os);
1228 os << "\\end{" << hullName(type_) << star(n) << "}\n";
1234 os << "\\end{" << hullName(type_) << "}\n";
1238 // Only used as a heuristic to find the regexp termination, when searching in ignore-format mode
1239 os << "\\endregexp{}}";
1245 os << "\\end{unknown" << star(n) << "}\n";
1251 bool InsetMathHull::allowsTabularFeatures() const
1274 bool InsetMathHull::rowChangeOK() const
1277 type_ == hullEqnArray || type_ == hullAlign ||
1278 type_ == hullFlAlign || type_ == hullAlignAt ||
1279 type_ == hullXAlignAt || type_ == hullXXAlignAt ||
1280 type_ == hullGather || type_ == hullMultline;
1284 bool InsetMathHull::colChangeOK() const
1287 type_ == hullAlign || type_ == hullFlAlign ||type_ == hullAlignAt ||
1288 type_ == hullXAlignAt || type_ == hullXXAlignAt;
1292 void InsetMathHull::addRow(row_type row)
1297 bool numbered = numberedType();
1298 // Move the number and raw pointer, do not call label() (bug 7511)
1299 InsetLabel * label = dummy_pointer;
1300 docstring number = empty_docstring();
1301 if (type_ == hullMultline) {
1302 if (row + 1 == nrows()) {
1303 numbered_[row] = NONUMBER;
1304 swap(label, label_[row]);
1305 swap(number, numbers_[row]);
1310 numbered_.insert(numbered_.begin() + row + 1, numbered ? NUMBER : NONUMBER);
1311 numbers_.insert(numbers_.begin() + row + 1, number);
1312 label_.insert(label_.begin() + row + 1, label);
1313 InsetMathGrid::addRow(row);
1317 void InsetMathHull::swapRow(row_type row)
1321 if (row + 1 == nrows())
1323 swap(numbered_[row], numbered_[row + 1]);
1324 swap(numbers_[row], numbers_[row + 1]);
1325 swap(label_[row], label_[row + 1]);
1326 InsetMathGrid::swapRow(row);
1330 void InsetMathHull::delRow(row_type row)
1332 if (nrows() <= 1 || !rowChangeOK())
1334 if (row + 1 == nrows() && type_ == hullMultline) {
1335 swap(numbered_[row - 1], numbered_[row]);
1336 swap(numbers_[row - 1], numbers_[row]);
1337 swap(label_[row - 1], label_[row]);
1338 InsetMathGrid::delRow(row);
1341 InsetMathGrid::delRow(row);
1342 // The last dummy row has no number info nor a label.
1343 // Test nrows() + 1 because we have already erased the row.
1344 if (row == nrows() + 1)
1346 numbered_.erase(numbered_.begin() + row);
1347 numbers_.erase(numbers_.begin() + row);
1349 label_.erase(label_.begin() + row);
1353 void InsetMathHull::addCol(col_type col)
1357 InsetMathGrid::addCol(col);
1361 void InsetMathHull::delCol(col_type col)
1363 if (ncols() <= 1 || !colChangeOK())
1365 InsetMathGrid::delCol(col);
1369 docstring InsetMathHull::nicelabel(row_type row) const
1373 docstring const & val = numbers_[row];
1375 return '(' + val + ')';
1376 return '(' + val + ',' + label_[row]->screenLabel() + ')';
1380 void InsetMathHull::glueall(HullType type)
1383 for (idx_type i = 0; i < nargs(); ++i)
1385 InsetLabel * label = 0;
1386 if (type == hullEquation) {
1387 // preserve first non-empty label
1388 for (row_type row = 0; row < nrows(); ++row) {
1390 label = label_[row];
1396 *this = InsetMathHull(buffer_, hullSimple);
1403 void InsetMathHull::splitTo2Cols()
1405 LASSERT(ncols() == 1, return);
1406 InsetMathGrid::addCol(1);
1407 for (row_type row = 0; row < nrows(); ++row) {
1408 idx_type const i = 2 * row;
1409 pos_type pos = firstRelOp(cell(i));
1410 cell(i + 1) = MathData(buffer_, cell(i).begin() + pos, cell(i).end());
1411 cell(i).erase(pos, cell(i).size());
1416 void InsetMathHull::splitTo3Cols()
1418 LASSERT(ncols() < 3, return);
1421 InsetMathGrid::addCol(2);
1422 for (row_type row = 0; row < nrows(); ++row) {
1423 idx_type const i = 3 * row + 1;
1424 if (!cell(i).empty()) {
1425 cell(i + 1) = MathData(buffer_, cell(i).begin() + 1, cell(i).end());
1426 cell(i).erase(1, cell(i).size());
1432 void InsetMathHull::changeCols(col_type cols)
1434 if (ncols() == cols)
1436 else if (ncols() < cols) {
1442 while (ncols() < cols)
1443 InsetMathGrid::addCol(ncols());
1449 for (row_type row = 0; row < nrows(); ++row) {
1450 idx_type const i = row * ncols();
1451 for (col_type col = cols; col < ncols(); ++col) {
1452 cell(i + cols - 1).append(cell(i + col));
1456 while (ncols() > cols) {
1457 InsetMathGrid::delCol(ncols() - 1);
1462 HullType InsetMathHull::getType() const
1468 void InsetMathHull::setType(HullType type)
1475 bool InsetMathHull::isMutable(HullType type)
1499 void InsetMathHull::mutate(HullType newtype)
1501 //lyxerr << "mutating from '" << type_ << "' to '" << newtype << "'" << endl;
1503 if (newtype == type_)
1506 // This guards the algorithm below it, which is designed with certain types
1508 if (!isMutable(newtype) || !isMutable(type_)) {
1509 lyxerr << "mutation from '" << to_utf8(hullName(type_))
1510 << "' to '" << to_utf8(hullName(newtype))
1511 << "' not implemented" << endl;
1515 // we try to move along the chain
1516 // none <-> simple <-> equation <-> eqnarray -> *align* -> multline, gather -+
1518 // +-------------------------------------+
1519 // we use eqnarray as intermediate type for mutations that are not
1520 // directly supported because it handles labels and numbering for
1525 setType(hullSimple);
1531 if (newtype == hullNone) {
1535 setType(hullEquation);
1536 numbered(0, label_[0] != nullptr);
1545 setType(hullSimple);
1550 // split it "nicely" on the first relop
1552 setType(hullEqnArray);
1560 // split it "nicely"
1595 setType(hullEqnArray);
1604 for (row_type row = 0; row < nrows(); ++row)
1605 numbered(row, false);
1615 for (row_type row = 0; row < nrows(); ++row)
1616 numbered(row, false);
1623 setType(hullEqnArray);
1653 for (row_type row = 0; row < nrows(); ++row)
1654 numbered(row, false);
1658 // first we mutate to EqnArray
1660 setType(hullEqnArray);
1667 // we passed the guard so we should not be here
1668 LYXERR0("Mutation not implemented, but should have been.");
1669 LASSERT(false, return);
1675 docstring InsetMathHull::eolString(row_type row, bool fragile, bool latex,
1676 bool last_eoln) const
1679 if (numberedType()) {
1680 if (label_[row] && numbered(row)) {
1681 docstring const name =
1682 latex ? escape(label_[row]->getParam("name"))
1683 : label_[row]->getParam("name");
1684 res += "\\label{" + name + '}';
1686 if (type_ != hullMultline) {
1687 if (numbered_[row] == NONUMBER)
1688 res += "\\nonumber ";
1689 else if (numbered_[row] == NOTAG)
1693 // Never add \\ on the last empty line of eqnarray and friends
1695 return res + InsetMathGrid::eolString(row, fragile, latex, last_eoln);
1698 void InsetMathHull::write(TeXMathStream & os) const
1700 ModeSpecifier specifier(os, MATH_MODE);
1702 InsetMathGrid::write(os);
1707 void InsetMathHull::normalize(NormalStream & os) const
1709 os << "[formula " << hullName(type_) << ' ';
1710 InsetMathGrid::normalize(os);
1715 void InsetMathHull::infoize(odocstream & os) const
1717 os << bformat(_("Type: %1$s"), hullName(type_));
1721 void InsetMathHull::check() const
1723 LATTEST(numbered_.size() == nrows());
1724 LATTEST(numbers_.size() == nrows());
1725 LATTEST(label_.size() == nrows());
1729 void InsetMathHull::doExtern(Cursor & cur, FuncRequest & func)
1731 //FIXME: sort out whether we want std::string or docstring for those
1732 string const lang = func.getArg(0);
1733 docstring extra = from_utf8(func.getArg(1));
1735 extra = from_ascii("noextra");
1737 // replace selection with result of computation
1738 if (reduceSelectionToOneCell(cur)) {
1740 asArray(grabAndEraseSelection(cur), ar);
1741 lyxerr << "use selection: " << ar << endl;
1742 cur.insert(pipeThroughExtern(lang, extra, ar));
1746 // only inline, display or eqnarray math is allowed
1747 switch (getType()) {
1753 frontend::Alert::warning(_("Bad math environment"),
1754 _("Computation cannot be performed for AMS "
1755 "math environments.\nChange the math "
1756 "formula type and try again."));
1761 eq.push_back(MathAtom(new InsetMathChar('=')));
1763 // go to first item in line
1764 cur.idx() -= cur.idx() % ncols();
1767 if (getType() == hullSimple) {
1768 size_type pos = cur.cell().find_last(eq);
1770 if (pos == cur.cell().size()) {
1772 lyxerr << "use whole cell: " << ar << endl;
1774 ar = MathData(buffer_, cur.cell().begin() + pos + 1, cur.cell().end());
1775 lyxerr << "use partial cell form pos: " << pos << endl;
1777 cur.cell().append(eq);
1778 cur.cell().append(pipeThroughExtern(lang, extra, ar));
1779 cur.pos() = cur.lastpos();
1783 if (getType() == hullEquation) {
1784 lyxerr << "use equation inset" << endl;
1785 mutate(hullEqnArray);
1786 MathData & ar = cur.cell();
1787 lyxerr << "use cell: " << ar << endl;
1791 cur.cell() = pipeThroughExtern(lang, extra, ar);
1792 // move to end of line
1793 cur.pos() = cur.lastpos();
1798 lyxerr << "use eqnarray" << endl;
1799 cur.idx() += 2 - cur.idx() % ncols();
1801 MathData ar = cur.cell();
1802 lyxerr << "use cell: " << ar << endl;
1803 // FIXME: temporarily disabled
1809 cur.cell() = pipeThroughExtern(lang, extra, ar);
1810 cur.pos() = cur.lastpos();
1815 void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd)
1817 //lyxerr << "action: " << cmd.action() << endl;
1818 switch (cmd.action()) {
1820 case LFUN_FINISHED_BACKWARD:
1821 case LFUN_FINISHED_FORWARD:
1822 case LFUN_FINISHED_RIGHT:
1823 case LFUN_FINISHED_LEFT:
1824 //lyxerr << "action: " << cmd.action() << endl;
1825 InsetMathGrid::doDispatch(cur, cmd);
1828 case LFUN_PARAGRAPH_BREAK:
1829 // just swallow this
1832 case LFUN_NEWLINE_INSERT:
1833 // some magic for the common case
1834 if (type_ == hullSimple || type_ == hullEquation) {
1835 cur.recordUndoInset();
1837 cur.bv().buffer().params().use_package("amsmath") != BufferParams::package_off;
1838 mutate(align ? hullAlign : hullEqnArray);
1839 // mutate() may change labels and such.
1840 cur.forceBufferUpdate();
1841 cur.idx() = nrows() * ncols() - 1;
1842 cur.pos() = cur.lastpos();
1844 InsetMathGrid::doDispatch(cur, cmd);
1847 case LFUN_MATH_NUMBER_TOGGLE: {
1848 //lyxerr << "toggling all numbers" << endl;
1849 cur.recordUndoInset();
1850 bool old = numberedType();
1851 if (type_ == hullMultline)
1852 numbered(nrows() - 1, !old);
1854 for (row_type row = 0; row < nrows(); ++row)
1855 numbered(row, !old);
1857 cur.message(old ? _("No number") : _("Number"));
1858 cur.forceBufferUpdate();
1862 case LFUN_MATH_NUMBER_LINE_TOGGLE: {
1863 cur.recordUndoInset();
1864 row_type r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1865 bool old = numbered(r);
1866 cur.message(old ? _("No number") : _("Number"));
1868 cur.forceBufferUpdate();
1872 case LFUN_LABEL_INSERT: {
1873 row_type r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1874 docstring old_label = label(r);
1875 docstring const default_label = from_ascii("eq:");
1876 if (old_label.empty())
1877 old_label = default_label;
1879 InsetCommandParams p(LABEL_CODE);
1880 p["name"] = cmd.argument().empty() ? old_label : cmd.argument();
1881 string const data = InsetCommand::params2string(p);
1883 if (cmd.argument().empty())
1884 cur.bv().showDialog("label", data);
1886 FuncRequest fr(LFUN_INSET_INSERT, data);
1892 case LFUN_LABEL_COPY_AS_REFERENCE: {
1894 if (cmd.argument().empty() && &cur.inset() == this)
1895 // if there is no argument and we're inside math, we retrieve
1896 // the row number from the cursor position.
1897 row = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1899 // if there is an argument, find the corresponding label, else
1900 // check whether there is at least one label.
1901 for (row = 0; row != nrows(); ++row)
1902 if (numbered(row) && label_[row]
1903 && (cmd.argument().empty() || label(row) == cmd.argument()))
1910 InsetCommandParams p(REF_CODE, "ref");
1911 p["reference"] = label(row);
1912 cap::clearSelection();
1913 cap::copyInset(cur, new InsetRef(buffer_, p), label(row));
1917 case LFUN_WORD_DELETE_FORWARD:
1918 case LFUN_CHAR_DELETE_FORWARD:
1919 if (col(cur.idx()) + 1 == ncols()
1920 && cur.pos() == cur.lastpos()
1921 && !cur.selection()) {
1922 if (!label(row(cur.idx())).empty()) {
1923 cur.recordUndoInset();
1924 label(row(cur.idx()), docstring());
1925 } else if (numbered(row(cur.idx()))) {
1926 cur.recordUndoInset();
1927 numbered(row(cur.idx()), false);
1928 cur.forceBufferUpdate();
1930 InsetMathGrid::doDispatch(cur, cmd);
1934 InsetMathGrid::doDispatch(cur, cmd);
1939 case LFUN_INSET_INSERT: {
1940 //lyxerr << "arg: " << to_utf8(cmd.argument()) << endl;
1941 // FIXME: this should be cleaned up to use InsetLabel methods directly.
1942 string const name = cmd.getArg(0);
1943 if (name == "label") {
1944 InsetCommandParams p(LABEL_CODE);
1945 InsetCommand::string2params(to_utf8(cmd.argument()), p);
1946 docstring str = p["name"];
1947 cur.recordUndoInset();
1948 row_type const r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1952 docstring old = label(r);
1955 // The label will take care of the reference update.
1959 // Newly created inset so initialize it.
1960 label_[r]->initView();
1963 cur.forceBufferUpdate();
1966 InsetMathGrid::doDispatch(cur, cmd);
1970 case LFUN_MATH_EXTERN:
1971 cur.recordUndoInset();
1975 case LFUN_MATH_MUTATE: {
1976 cur.recordUndoInset();
1977 row_type row = cur.row();
1978 col_type col = cur.col();
1979 mutate(hullType(cmd.argument()));
1980 cur.idx() = row * ncols() + col;
1981 if (cur.idx() > cur.lastidx()) {
1982 cur.idx() = cur.lastidx();
1983 cur.pos() = cur.lastpos();
1985 if (cur.pos() > cur.lastpos())
1986 cur.pos() = cur.lastpos();
1988 cur.forceBufferUpdate();
1989 // FIXME: find some more clever handling of the selection,
1990 // i.e. preserve it.
1991 cur.clearSelection();
1992 //cur.dispatched(FINISHED);
1996 case LFUN_MATH_DISPLAY: {
1997 cur.recordUndoInset();
1998 mutate(type_ == hullSimple ? hullEquation : hullSimple);
1999 // if the cursor is in a cell that got merged, move it to
2000 // start of the hull inset.
2001 if (cur.idx() > 0) {
2005 if (cur.pos() > cur.lastpos())
2006 cur.pos() = cur.lastpos();
2011 case LFUN_TABULAR_FEATURE:
2012 if (!allowsTabularFeatures())
2015 InsetMathGrid::doDispatch(cur, cmd);
2019 InsetMathGrid::doDispatch(cur, cmd);
2027 bool allowDisplayMath(Cursor const & cur)
2029 LATTEST(cur.depth() > 1);
2030 Cursor tmpcur = cur;
2033 FuncRequest cmd(LFUN_MATH_DISPLAY);
2034 return tmpcur.getStatus(cmd, status) && status.enabled();
2040 bool InsetMathHull::getStatus(Cursor & cur, FuncRequest const & cmd,
2041 FuncStatus & status) const
2043 switch (cmd.action()) {
2044 case LFUN_FINISHED_BACKWARD:
2045 case LFUN_FINISHED_FORWARD:
2046 case LFUN_FINISHED_RIGHT:
2047 case LFUN_FINISHED_LEFT:
2050 case LFUN_NEWLINE_INSERT:
2051 case LFUN_MATH_EXTERN:
2053 status.setEnabled(true);
2056 // we never allow this in math, and we want to bind enter
2057 // to another actions in command-alternatives
2058 case LFUN_PARAGRAPH_BREAK:
2059 status.setEnabled(false);
2061 case LFUN_MATH_MUTATE: {
2062 HullType const ht = hullType(cmd.argument());
2063 status.setOnOff(type_ == ht);
2064 status.setEnabled(isMutable(ht) && isMutable(type_));
2066 if (ht != hullSimple && status.enabled())
2067 status.setEnabled(allowDisplayMath(cur));
2070 case LFUN_MATH_DISPLAY: {
2071 status.setEnabled(display() || allowDisplayMath(cur));
2072 status.setOnOff(display());
2076 case LFUN_MATH_NUMBER_TOGGLE:
2077 // FIXME: what is the right test, this or the one of
2079 status.setEnabled(display());
2080 status.setOnOff(numberedType());
2083 case LFUN_MATH_NUMBER_LINE_TOGGLE: {
2084 // FIXME: what is the right test, this or the one of
2086 bool const enable = (type_ == hullMultline)
2087 ? (nrows() - 1 == cur.row())
2089 row_type const r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
2090 status.setEnabled(enable);
2091 status.setOnOff(enable && numbered(r));
2095 case LFUN_LABEL_INSERT:
2096 status.setEnabled(type_ != hullSimple);
2099 case LFUN_LABEL_COPY_AS_REFERENCE: {
2100 bool enabled = false;
2101 if (cmd.argument().empty() && &cur.inset() == this) {
2102 // if there is no argument and we're inside math, we retrieve
2103 // the row number from the cursor position.
2104 row_type row = (type_ == hullMultline) ? nrows() - 1 : cur.row();
2105 enabled = numberedType() && label_[row] && numbered(row);
2107 // if there is an argument, find the corresponding label, else
2108 // check whether there is at least one label.
2109 for (row_type row = 0; row != nrows(); ++row) {
2110 if (numbered(row) && label_[row] &&
2111 (cmd.argument().empty() || label(row) == cmd.argument())) {
2117 status.setEnabled(enabled);
2121 case LFUN_INSET_INSERT:
2122 if (cmd.getArg(0) == "label") {
2123 status.setEnabled(type_ != hullSimple);
2126 return InsetMathGrid::getStatus(cur, cmd, status);
2128 case LFUN_TABULAR_FEATURE: {
2129 if (!allowsTabularFeatures())
2131 string s = cmd.getArg(0);
2133 && (s == "append-row"
2134 || s == "delete-row"
2135 || s == "copy-row")) {
2136 status.message(bformat(
2137 from_utf8(N_("Can't change number of rows in '%1$s'")),
2139 status.setEnabled(false);
2143 && (s == "append-column"
2144 || s == "delete-column"
2145 || s == "copy-column")) {
2146 status.message(bformat(
2147 from_utf8(N_("Can't change number of columns in '%1$s'")),
2149 status.setEnabled(false);
2152 if (s == "add-vline-left" || s == "add-vline-right") {
2153 status.message(bformat(
2154 from_utf8(N_("Can't add vertical grid lines in '%1$s'")),
2156 status.setEnabled(false);
2159 if (s == "valign-top" || s == "valign-middle"
2160 || s == "valign-bottom" || s == "align-left"
2161 || s == "align-center" || s == "align-right") {
2162 status.setEnabled(false);
2165 return InsetMathGrid::getStatus(cur, cmd, status);
2169 return InsetMathGrid::getStatus(cur, cmd, status);
2174 int InsetMathHull::leftMargin() const
2176 return (getType() == hullSimple) ? 0 : InsetMathGrid::leftMargin();
2180 int InsetMathHull::rightMargin() const
2182 return (getType() == hullSimple) ? 0 : InsetMathGrid::rightMargin();
2186 int InsetMathHull::border() const
2188 return (getType() == hullSimple) ? 0 : InsetMathGrid::border();
2192 /////////////////////////////////////////////////////////////////////
2196 // simply scrap this function if you want
2197 void InsetMathHull::mutateToText()
2200 // translate to latex
2202 latex(os, false, false);
2203 string str = os.str();
2206 Text * lt = view_->cursor().innerText();
2207 string::const_iterator cit = str.begin();
2208 string::const_iterator end = str.end();
2209 for (; cit != end; ++cit)
2210 view_->getIntl()->getTransManager().TranslateAndInsert(*cit, lt);
2213 //dispatch(LFUN_ESCAPE);
2218 void InsetMathHull::handleFont(Cursor & cur, docstring const & arg,
2219 docstring const & font)
2221 // this whole function is a hack and won't work for incremental font
2224 if (cur.inset().asInsetMath()->name() == font)
2225 cur.handleFont(to_utf8(font));
2227 cur.handleNest(createInsetMath(font, cur.buffer()));
2233 void InsetMathHull::handleFont2(Cursor & cur, docstring const & arg)
2238 font.fromString(to_utf8(arg), b);
2239 if (font.fontInfo().color() != Color_inherit) {
2240 MathAtom at = MathAtom(new InsetMathColor(buffer_, true, font.fontInfo().color()));
2246 void InsetMathHull::edit(Cursor & cur, bool front, EntryDirection entry_from)
2248 InsetMathNest::edit(cur, front, entry_from);
2249 // The inset formula dimension is not necessarily the same as the
2250 // one of the instant preview image, so we have to indicate to the
2251 // BufferView that a metrics update is needed.
2252 cur.screenUpdateFlags(Update::Force);
2256 /////////////////////////////////////////////////////////////////////
2260 bool InsetMathHull::searchForward(BufferView * bv, string const & str,
2263 // FIXME: completely broken
2264 static InsetMathHull * lastformula = 0;
2265 static CursorBase current = DocIterator(ibegin(nucleus()));
2267 static string laststr;
2269 if (lastformula != this || laststr != str) {
2270 //lyxerr << "reset lastformula to " << this << endl;
2273 current = ibegin(nucleus());
2275 mathed_parse_cell(ar, str, Parse::NORMAL, &buffer());
2279 //lyxerr << "searching '" << str << "' in " << this << ar << endl;
2281 for (DocIterator it = current; it != iend(nucleus()); increment(it)) {
2282 CursorSlice & top = it.back();
2283 MathData const & a = top.asInsetMath()->cell(top.idx_);
2284 if (a.matchpart(ar, top.pos_)) {
2285 bv->cursor().setSelection(it, ar.size());
2287 top.pos_ += ar.size();
2293 //lyxerr << "not found!" << endl;
2300 void InsetMathHull::write(ostream & os) const
2302 odocstringstream oss;
2303 otexrowstream ots(oss);
2304 TeXMathStream wi(ots, false, false, TeXMathStream::wsDefault);
2307 os << to_utf8(oss.str());
2311 void InsetMathHull::read(Lexer & lex)
2314 mathed_parse_normal(buffer_, at, lex, Parse::TRACKMACRO);
2315 operator=(*at->asHullInset());
2319 bool InsetMathHull::readQuiet(Lexer & lex)
2322 bool success = mathed_parse_normal(buffer_, at, lex, Parse::QUIET);
2324 operator=(*at->asHullInset());
2329 int InsetMathHull::plaintext(odocstringstream & os,
2330 OutputParams const & op, size_t max_length) const
2332 // Try enabling this now that there is a flag as requested at #2275.
2333 if (buffer().isExporting() && display()) {
2337 TextPainter tpain(dim.width(), dim.height());
2338 drawT(tpain, 0, dim.ascent());
2340 // reset metrics cache to "real" values
2342 return tpain.textheight();
2345 odocstringstream oss;
2346 otexrowstream ots(oss);
2347 Encoding const * const enc = encodings.fromLyXName("utf8");
2348 TeXMathStream wi(ots, false, true, TeXMathStream::wsDefault, enc);
2351 if (type_ == hullRegexp)
2354 for (row_type r = 0; r < nrows(); ++r) {
2355 for (col_type c = 0; c < ncols(); ++c)
2356 wi << (c == 0 ? "" : "\t") << cell(index(r, c));
2357 // if it's for the TOC, we write just the first line
2358 // and do not include the newline.
2359 if (op.for_toc || op.for_tooltip || oss.str().size() >= max_length)
2361 if (r < nrows() - 1)
2365 docstring const str = oss.str();
2371 void InsetMathHull::docbook(XMLStream & xs, OutputParams const & runparams) const
2373 // Choose the tag around the MathML equation.
2376 if (getType() == hullSimple)
2377 name = from_ascii("inlineequation");
2379 doCR = true; // This is a block equation, always have <informalequation> on its own line.
2380 name = from_ascii("informalequation");
2383 // DocBook also has <equation>, but it comes with a title.
2384 // TODO: recognise \tag from amsmath? This would allow having <equation> with a proper title.
2387 for (row_type i = 0; i < nrows(); ++i) {
2388 if (!label(i).empty()) {
2389 attr = "xml:id=\"" + xml::cleanID(label(i)) + "\"";
2395 if (!xs.isLastTagCR())
2398 xs << xml::StartTag(name, attr);
2401 // With DocBook 5, MathML must be within its own namespace; defined in Buffer.cpp::writeDocBookSource as "m".
2402 // Output everything in a separate stream so that this does not interfere with the standard flow of DocBook tags.
2403 odocstringstream osmath;
2404 MathMLStream ms(osmath, "m", true);
2406 // Output the MathML subtree.
2407 odocstringstream ls;
2410 // TeX transcription. Avoid MTag/ETag so that there are no extraneous spaces.
2411 ms << "<" << from_ascii("alt") << " role='tex'" << ">";
2412 // Workaround for db2latex: db2latex always includes equations with
2413 // \ensuremath{} or \begin{display}\end{display}
2414 // so we strip LyX' math environment
2415 TeXMathStream wi(ols, false, false, TeXMathStream::wsDefault, runparams.encoding);
2416 InsetMathGrid::write(wi);
2417 ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&"), "<", "<"));
2418 ms << "</" << from_ascii("alt") << ">";
2420 // Actual transformation of the formula into MathML. This translation may fail (for example, due to custom macros).
2421 // The new output stream is required to deal with the errors: first write completely the formula into this
2422 // temporary stream; then, if it is possible without error, then copy it back to the "real" stream. Otherwise,
2423 // some incomplete tags might be put into the real stream.
2425 // First, generate the MathML expression.
2426 odocstringstream ostmp;
2427 MathMLStream mstmp(ostmp, ms.xmlns(), ms.xmlMode());
2428 InsetMathGrid::mathmlize(mstmp);
2430 // Then, output it (but only if the generation can be done without errors!).
2433 osmath << ostmp.str(); // osmath is not a XMLStream, so no need for XMLStream::ESCAPE_NONE.
2435 } catch (MathExportException const &) {
2437 osmath << "<mathphrase>MathML export failed. Please report this as a bug.</mathphrase>";
2440 // Output the complete formula to the DocBook stream.
2441 xs << XMLStream::ESCAPE_NONE << osmath.str();
2443 xs << xml::EndTag(name);
2449 bool InsetMathHull::haveNumbers() const
2451 bool havenumbers = false;
2452 // inline formulas are never numbered (bug 7351 part 3)
2453 if (getType() == hullSimple)
2455 for (size_t i = 0; i != numbered_.size(); ++i) {
2466 // We need to do something about alignment here.
2468 // This duplicates code from InsetMathGrid, but
2469 // we need access here to number information,
2470 // and we simply do not have that in InsetMathGrid.
2471 void InsetMathHull::htmlize(HtmlStream & os) const
2473 bool const havenumbers = haveNumbers();
2474 bool const havetable = havenumbers || nrows() > 1 || ncols() > 1;
2477 os << cell(index(0, 0));
2481 os << MTag("table", "class='mathtable'");
2482 for (row_type row = 0; row < nrows(); ++row) {
2484 for (col_type col = 0; col < ncols(); ++col) {
2486 os << cell(index(row, col));
2491 docstring const & num = numbers_[row];
2493 os << '(' << num << ')';
2498 os << ETag("table");
2502 // this duplicates code from InsetMathGrid, but
2503 // we need access here to number information,
2504 // and we simply do not have that in InsetMathGrid.
2505 void InsetMathHull::mathmlize(MathMLStream & ms) const
2507 bool const havenumbers = haveNumbers();
2508 bool const havetable = havenumbers || nrows() > 1 || ncols() > 1;
2511 ms << MTag("mtable");
2512 char const * const celltag = havetable ? "mtd" : "mrow";
2513 // FIXME There does not seem to be wide support at the moment
2514 // for mlabeledtr, so we have to use just mtr for now.
2515 // char const * const rowtag = havenumbers ? "mlabeledtr" : "mtr";
2516 char const * const rowtag = "mtr";
2517 for (row_type row = 0; row < nrows(); ++row) {
2520 for (col_type col = 0; col < ncols(); ++col) {
2522 << cell(index(row, col))
2528 docstring const & num = numbers_[row];
2530 ms << '(' << num << ')';
2537 ms << ETag("mtable");
2541 void InsetMathHull::mathAsLatex(TeXMathStream & os) const
2543 MathEnsurer ensurer(os, false);
2544 bool havenumbers = haveNumbers();
2545 bool const havetable = havenumbers || nrows() > 1 || ncols() > 1;
2548 os << cell(index(0, 0));
2552 os << "<table class='mathtable'>";
2553 for (row_type row = 0; row < nrows(); ++row) {
2555 for (col_type col = 0; col < ncols(); ++col) {
2556 os << "<td class='math'>";
2557 os << cell(index(row, col));
2562 docstring const & num = numbers_[row];
2564 os << '(' << num << ')';
2573 docstring InsetMathHull::xhtml(XMLStream & xs, OutputParams const & op) const
2575 BufferParams::MathOutput const mathtype =
2576 buffer().masterBuffer()->params().html_math_output;
2578 bool success = false;
2580 // we output all the labels just at the beginning of the equation.
2581 // this should be fine.
2582 for (size_t i = 0; i != label_.size(); ++i) {
2583 InsetLabel const * const il = label_[i];
2589 // FIXME Eventually we would like to do this inset by inset.
2590 if (mathtype == BufferParams::MathML) {
2591 odocstringstream os;
2592 MathMLStream ms(os);
2596 } catch (MathExportException const &) {}
2598 if (getType() == hullSimple)
2599 xs << xml::StartTag("math",
2600 "xmlns=\"http://www.w3.org/1998/Math/MathML\"", true);
2602 xs << xml::StartTag("math",
2603 "display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"", true);
2604 xs << XMLStream::ESCAPE_NONE
2606 << xml::EndTag("math");
2608 } else if (mathtype == BufferParams::HTML) {
2609 odocstringstream os;
2614 } catch (MathExportException const &) {}
2616 string const tag = (getType() == hullSimple) ? "span" : "div";
2617 xs << xml::StartTag(tag, "class='formula'", true)
2618 << XMLStream::ESCAPE_NONE
2620 << xml::EndTag(tag);
2624 // what we actually want is this:
2626 // ((mathtype == BufferParams::MathML || mathtype == BufferParams::HTML)
2628 // || mathtype == BufferParams::Images
2630 // but what follows is equivalent, since we'll enter only if either (a) we
2631 // tried and failed with MathML or HTML or (b) didn't try yet at all but
2632 // aren't doing LaTeX.
2634 // so this is for Images.
2635 if (!success && mathtype != BufferParams::LaTeX) {
2636 graphics::PreviewImage const * pimage = 0;
2638 loadPreview(docit_);
2639 pimage = preview_->getPreviewImage(buffer());
2640 // FIXME Do we always have png?
2643 if (pimage || op.dryrun) {
2644 string const filename = pimage ? pimage->filename().onlyFileName()
2645 : "previewimage.png";
2647 // if we are not in the master buffer, then we need to see that the
2648 // generated image is copied there; otherwise, preview fails.
2649 Buffer const * mbuf = buffer().masterBuffer();
2650 if (mbuf != &buffer()) {
2651 string mbtmp = mbuf->temppath();
2652 FileName const mbufimg(support::addName(mbtmp, filename));
2653 pimage->filename().copyTo(mbufimg);
2655 // add the file to the list of files to be exported
2656 op.exportdata->addExternalFile("xhtml", pimage->filename());
2659 string const tag = (getType() == hullSimple) ? "span" : "div";
2661 << xml::StartTag(tag, "style = \"text-align: center;\"")
2662 << xml::CompTag("img", "src=\"" + filename + "\" alt=\"Mathematical Equation\"")
2669 // so we'll pass this test if we've failed everything else, or
2670 // if mathtype was LaTeX, since we won't have entered any of the
2672 if (!success /* || mathtype != BufferParams::LaTeX */) {
2673 // Unfortunately, we cannot use latexString() because we do not want
2674 // $...$ or whatever.
2675 odocstringstream ls;
2676 otexrowstream ots(ls);
2677 TeXMathStream wi(ots, false, true, TeXMathStream::wsPreview);
2678 ModeSpecifier specifier(wi, MATH_MODE);
2680 docstring const latex = ls.str();
2682 // class='math' allows for use of jsMath
2683 // http://www.math.union.edu/~dpvc/jsMath/
2685 // probably should allow for some kind of customization here
2686 string const tag = (getType() == hullSimple) ? "span" : "div";
2687 xs << xml::StartTag(tag, "class='math'")
2696 void InsetMathHull::toString(odocstream & os) const
2698 odocstringstream ods;
2699 plaintext(ods, OutputParams(0));
2704 void InsetMathHull::forOutliner(docstring & os, size_t const, bool const) const
2706 odocstringstream ods;
2709 // FIXME: this results in spilling TeX into the LyXHTML output since the
2710 // outliner is used to generate the LyXHTML list of figures/etc.
2716 string InsetMathHull::contextMenuName() const
2718 return "context-math";
2722 void InsetMathHull::recordLocation(DocIterator const & di)
2728 bool InsetMathHull::canPaintChange(BufferView const &) const
2730 // We let RowPainter do it seamlessly for inline insets