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 "MathExtern.h"
18 #include "MathFactory.h"
19 #include "MathStream.h"
20 #include "MathSupport.h"
23 #include "BufferParams.h"
24 #include "BufferView.h"
25 #include "CutAndPaste.h"
27 #include "FuncRequest.h"
28 #include "FuncStatus.h"
29 #include "LaTeXFeatures.h"
31 #include "MacroTable.h"
32 #include "output_xhtml.h"
34 #include "TextPainter.h"
35 #include "TocBackend.h"
37 #include "insets/InsetLabel.h"
38 #include "insets/InsetRef.h"
39 #include "insets/RenderPreview.h"
41 #include "graphics/PreviewImage.h"
43 #include "frontends/Painter.h"
45 #include "support/lassert.h"
46 #include "support/debug.h"
47 #include "support/gettext.h"
48 #include "support/lstrings.h"
53 using namespace lyx::support;
57 using cap::grabAndEraseSelection;
61 int getCols(HullType type)
78 // returns position of first relation operator in the array
79 // used for "intelligent splitting"
80 size_t firstRelOp(MathData const & ar)
82 for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
84 return it - ar.begin();
89 char const * star(bool numbered)
91 return numbered ? "" : "*";
95 } // end anon namespace
98 HullType hullType(docstring const & s)
100 if (s == "none") return hullNone;
101 if (s == "simple") return hullSimple;
102 if (s == "equation") return hullEquation;
103 if (s == "eqnarray") return hullEqnArray;
104 if (s == "align") return hullAlign;
105 if (s == "alignat") return hullAlignAt;
106 if (s == "xalignat") return hullXAlignAt;
107 if (s == "xxalignat") return hullXXAlignAt;
108 if (s == "multline") return hullMultline;
109 if (s == "gather") return hullGather;
110 if (s == "flalign") return hullFlAlign;
111 if (s == "regexp") return hullRegexp;
112 lyxerr << "unknown hull type '" << to_utf8(s) << "'" << endl;
117 docstring hullName(HullType type)
120 case hullNone: return from_ascii("none");
121 case hullSimple: return from_ascii("simple");
122 case hullEquation: return from_ascii("equation");
123 case hullEqnArray: return from_ascii("eqnarray");
124 case hullAlign: return from_ascii("align");
125 case hullAlignAt: return from_ascii("alignat");
126 case hullXAlignAt: return from_ascii("xalignat");
127 case hullXXAlignAt: return from_ascii("xxalignat");
128 case hullMultline: return from_ascii("multline");
129 case hullGather: return from_ascii("gather");
130 case hullFlAlign: return from_ascii("flalign");
131 case hullRegexp: return from_ascii("regexp");
133 lyxerr << "unknown hull type '" << type << "'" << endl;
134 return from_ascii("none");
138 static InsetLabel * dummy_pointer = 0;
140 InsetMathHull::InsetMathHull(Buffer * buf)
141 : InsetMathGrid(buf, 1, 1), type_(hullNone), nonum_(1, false),
142 label_(1, dummy_pointer), preview_(new RenderPreview(this))
144 //lyxerr << "sizeof InsetMath: " << sizeof(InsetMath) << endl;
145 //lyxerr << "sizeof MetricsInfo: " << sizeof(MetricsInfo) << endl;
146 //lyxerr << "sizeof InsetMathChar: " << sizeof(InsetMathChar) << endl;
147 //lyxerr << "sizeof FontInfo: " << sizeof(FontInfo) << endl;
154 InsetMathHull::InsetMathHull(Buffer * buf, HullType type)
155 : InsetMathGrid(buf, getCols(type), 1), type_(type), nonum_(1, false),
156 label_(1, dummy_pointer), preview_(new RenderPreview(this))
164 InsetMathHull::InsetMathHull(InsetMathHull const & other) : InsetMathGrid(other)
170 InsetMathHull::~InsetMathHull()
172 for (size_t i = 0; i < label_.size(); ++i)
177 Inset * InsetMathHull::clone() const
179 return new InsetMathHull(*this);
183 InsetMathHull & InsetMathHull::operator=(InsetMathHull const & other)
187 InsetMathGrid::operator=(other);
189 nonum_ = other.nonum_;
190 buffer_ = other.buffer_;
191 for (size_t i = 0; i < label_.size(); ++i)
193 label_ = other.label_;
194 for (size_t i = 0; i != label_.size(); ++i) {
196 label_[i] = new InsetLabel(*label_[i]);
198 preview_.reset(new RenderPreview(*other.preview_, this));
204 void InsetMathHull::setBuffer(Buffer & buffer)
206 InsetMathGrid::setBuffer(buffer);
208 for (size_t i = 0; i != label_.size(); ++i) {
210 label_[i]->setBuffer(buffer);
215 void InsetMathHull::updateLabels(ParIterator const & it, bool out)
218 //FIXME: buffer_ should be set at creation for this inset! Problem is
219 // This inset is created at too many places (see Parser::parse1() in
223 for (size_t i = 0; i != label_.size(); ++i) {
225 label_[i]->updateLabels(it, out);
230 void InsetMathHull::addToToc(DocIterator const & pit)
233 //FIXME: buffer_ should be set at creation for this inset! Problem is
234 // This inset is created at too many places (see Parser::parse1() in
239 Toc & toc = buffer().tocBackend().toc("equation");
241 for (row_type row = 0; row != nrows(); ++row) {
245 label_[row]->addToToc(pit);
246 toc.push_back(TocItem(pit, 0, nicelabel(row)));
251 Inset * InsetMathHull::editXY(Cursor & cur, int x, int y)
257 return InsetMathNest::editXY(cur, x, y);
261 InsetMath::mode_type InsetMathHull::currentMode() const
263 if (type_ == hullNone)
264 return UNDECIDED_MODE;
265 // definitely math mode ...
270 bool InsetMathHull::idxFirst(Cursor & cur) const
278 bool InsetMathHull::idxLast(Cursor & cur) const
280 cur.idx() = nargs() - 1;
281 cur.pos() = cur.lastpos();
286 char InsetMathHull::defaultColAlign(col_type col)
288 if (type_ == hullEqnArray)
290 if (type_ == hullGather)
292 if (type_ >= hullAlign)
293 return "rl"[col & 1];
298 int InsetMathHull::defaultColSpace(col_type col)
300 if (type_ == hullAlign || type_ == hullAlignAt)
302 if (type_ == hullXAlignAt)
303 return (col & 1) ? 20 : 0;
304 if (type_ == hullXXAlignAt || type_ == hullFlAlign)
305 return (col & 1) ? 40 : 0;
310 docstring InsetMathHull::standardFont() const
312 return from_ascii(type_ == hullNone ? "lyxnochange" : "mathnormal");
316 bool InsetMathHull::previewState(BufferView * bv) const
318 if (!editing(bv) && RenderPreview::status() == LyXRC::PREVIEW_ON) {
319 graphics::PreviewImage const * pimage =
320 preview_->getPreviewImage(bv->buffer());
321 return pimage && pimage->image();
327 void InsetMathHull::metrics(MetricsInfo & mi, Dimension & dim) const
329 if (previewState(mi.base.bv)) {
330 preview_->metrics(mi, dim);
331 // insert a one pixel gap in front of the formula
334 dim.des += displayMargin();
335 // Cache the inset dimension.
336 setDimCache(mi, dim);
340 FontSetChanger dummy1(mi.base, standardFont());
341 StyleChanger dummy2(mi.base, display() ? LM_ST_DISPLAY : LM_ST_TEXT);
343 // let the cells adjust themselves
344 InsetMathGrid::metrics(mi, dim);
347 dim.asc += displayMargin();
348 dim.des += displayMargin();
351 if (numberedType()) {
352 FontSetChanger dummy(mi.base, from_ascii("mathbf"));
354 for (row_type row = 0; row < nrows(); ++row)
355 l = max(l, mathed_string_width(mi.base.font, nicelabel(row)));
361 // make it at least as high as the current font
364 math_font_max_dim(mi.base.font, asc, des);
365 dim.asc = max(dim.asc, asc);
366 dim.des = max(dim.des, des);
367 // Cache the inset dimension.
368 // FIXME: This will overwrite InsetMathGrid dimension, is that OK?
369 setDimCache(mi, dim);
373 void InsetMathHull::drawBackground(PainterInfo & pi, int x, int y) const
375 Dimension const dim = dimension(*pi.base.bv);
376 pi.pain.fillRectangle(x + 1, y - dim.asc + 1, dim.wid - 2,
377 dim.asc + dim.des - 1, pi.backgroundColor(this));
381 void InsetMathHull::draw(PainterInfo & pi, int x, int y) const
383 use_preview_ = previewState(pi.base.bv);
386 // one pixel gap in front
387 preview_->draw(pi, x + 1, y);
388 setPosCache(pi, x, y);
392 FontSetChanger dummy1(pi.base, standardFont());
393 StyleChanger dummy2(pi.base, display() ? LM_ST_DISPLAY : LM_ST_TEXT);
394 InsetMathGrid::draw(pi, x + 1, y);
396 if (numberedType()) {
397 int const xx = x + colinfo_.back().offset_ + colinfo_.back().width_ + 20;
398 for (row_type row = 0; row < nrows(); ++row) {
399 int const yy = y + rowinfo_[row].offset_;
400 FontSetChanger dummy(pi.base, from_ascii("mathrm"));
401 docstring const nl = nicelabel(row);
405 setPosCache(pi, x, y);
409 void InsetMathHull::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
412 InsetMathGrid::metricsT(mi, dim);
415 WriteStream wi(os, false, true, WriteStream::wsDefault);
417 dim.wid = os.str().size();
424 void InsetMathHull::drawT(TextPainter & pain, int x, int y) const
427 InsetMathGrid::drawT(pain, x, y);
430 WriteStream wi(os, false, true, WriteStream::wsDefault);
432 pain.draw(x, y, os.str().c_str());
437 static docstring latexString(InsetMathHull const & inset)
440 // This has to be static, because a preview snippet or a math
441 // macro containing math in text mode (such as $\text{$\phi$}$ or
442 // \newcommand{\xxx}{\text{$\phi$}}) gets processed twice. The
443 // first time as a whole, and the second time only the inner math.
444 // In this last case inset.buffer() would be invalid.
445 static Encoding const * encoding = 0;
446 if (inset.isBufferValid())
447 encoding = &(inset.buffer().params().encoding());
448 WriteStream wi(ls, false, true, WriteStream::wsPreview, encoding);
454 void InsetMathHull::initUnicodeMath() const
456 // Trigger classification of the unicode symbols in this inset
457 docstring const dummy = latexString(*this);
461 void InsetMathHull::addPreview(DocIterator const & inset_pos,
462 graphics::PreviewLoader & /*ploader*/) const
464 if (RenderPreview::status() == LyXRC::PREVIEW_ON) {
465 preparePreview(inset_pos);
470 void InsetMathHull::preparePreview(DocIterator const & pos) const
472 Buffer const * buffer = pos.buffer();
474 // collect macros at this position
476 buffer->listMacroNames(macros);
477 MacroNameSet::iterator it = macros.begin();
478 MacroNameSet::iterator end = macros.end();
479 odocstringstream macro_preamble;
480 for (; it != end; ++it) {
481 MacroData const * data = buffer->getMacro(*it, pos, true);
483 data->write(macro_preamble, true);
484 macro_preamble << endl;
488 docstring const snippet = macro_preamble.str() + latexString(*this);
489 LYXERR(Debug::MACROS, "Preview snippet: " << snippet);
490 preview_->addPreview(snippet, *buffer);
494 void InsetMathHull::reloadPreview(DocIterator const & pos) const
497 preview_->startLoading(*pos.buffer());
501 bool InsetMathHull::notifyCursorLeaves(Cursor const & old, Cursor & cur)
503 if (RenderPreview::status() == LyXRC::PREVIEW_ON) {
505 cur.updateFlags(Update::Force);
511 docstring InsetMathHull::label(row_type row) const
513 LASSERT(row < nrows(), /**/);
514 if (InsetLabel * il = label_[row])
515 return il->screenLabel();
520 void InsetMathHull::label(row_type row, docstring const & label)
522 //lyxerr << "setting label '" << label << "' for row " << row << endl;
526 label_[row] = dummy_pointer;
527 // We need an update of the Buffer reference cache.
528 // This is achieved by updateLabels().
530 buffer().updateLabels();
533 label_[row]->updateCommand(label);
535 label_[row]->setParam("name", label);
539 InsetCommandParams p(LABEL_CODE);
541 label_[row] = new InsetLabel(buffer_, p);
543 label_[row]->setBuffer(buffer());
547 void InsetMathHull::numbered(row_type row, bool num)
550 if (nonum_[row] && label_[row]) {
554 // The buffer is set at the end of readInset.
555 // When parsing the inset, buffer_ is 0.
558 // We need an update of the Buffer reference cache.
559 // This is achieved by updateLabels().
560 buffer().updateLabels();
565 bool InsetMathHull::numbered(row_type row) const
571 bool InsetMathHull::ams() const
573 return type_ == hullAlign
574 || type_ == hullFlAlign
575 || type_ == hullMultline
576 || type_ == hullGather
577 || type_ == hullAlignAt
578 || type_ == hullXAlignAt
579 || type_ == hullXXAlignAt;
583 Inset::DisplayType InsetMathHull::display() const
585 if (type_ == hullSimple || type_ == hullNone || type_ == hullRegexp)
590 bool InsetMathHull::numberedType() const
592 if (type_ == hullNone)
594 if (type_ == hullSimple)
596 if (type_ == hullXXAlignAt)
598 if (type_ == hullRegexp)
600 for (row_type row = 0; row < nrows(); ++row)
607 void InsetMathHull::validate(LaTeXFeatures & features) const
610 features.require("amsmath");
612 // Validation is necessary only if not using AMS math.
613 // To be safe, we will always run mathedvalidate.
614 //if (features.amsstyle)
617 //features.binom = true;
619 InsetMathGrid::validate(features);
623 void InsetMathHull::header_write(WriteStream & os) const
625 bool n = numberedType();
639 os << "\\begin{equation" << star(n) << "}\n";
649 os << "\\begin{" << hullName(type_) << star(n) << "}\n";
654 os << "\\begin{" << hullName(type_) << star(n) << '}'
655 << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
659 os << "\\begin{" << hullName(type_) << '}'
660 << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
668 os << "\\begin{unknown" << star(n) << '}';
674 void InsetMathHull::footer_write(WriteStream & os) const
676 bool n = numberedType();
689 os << "\\end{equation" << star(n) << "}\n";
701 os << "\\end{" << hullName(type_) << star(n) << "}\n";
705 os << "\\end{" << hullName(type_) << "}\n";
713 os << "\\end{unknown" << star(n) << '}';
719 bool InsetMathHull::rowChangeOK() const
722 type_ == hullEqnArray || type_ == hullAlign ||
723 type_ == hullFlAlign || type_ == hullAlignAt ||
724 type_ == hullXAlignAt || type_ == hullXXAlignAt ||
725 type_ == hullGather || type_ == hullMultline;
729 bool InsetMathHull::colChangeOK() const
732 type_ == hullAlign || type_ == hullFlAlign ||type_ == hullAlignAt ||
733 type_ == hullXAlignAt || type_ == hullXXAlignAt;
737 void InsetMathHull::addRow(row_type row)
742 bool numbered = numberedType();
744 if (type_ == hullMultline) {
745 if (row + 1 == nrows()) {
752 nonum_.insert(nonum_.begin() + row + 1, !numbered);
753 label_.insert(label_.begin() + row + 1, dummy_pointer);
756 InsetMathGrid::addRow(row);
760 void InsetMathHull::swapRow(row_type row)
764 if (row + 1 == nrows())
766 // gcc implements the standard std::vector<bool> which is *not* a container:
767 // http://www.gotw.ca/publications/N1185.pdf
768 // As a results, it doesn't like this:
769 // swap(nonum_[row], nonum_[row + 1]);
770 // so we do it manually:
771 bool const b = nonum_[row];
772 nonum_[row] = nonum_[row + 1];
774 swap(label_[row], label_[row + 1]);
775 InsetMathGrid::swapRow(row);
779 void InsetMathHull::delRow(row_type row)
781 if (nrows() <= 1 || !rowChangeOK())
783 if (row + 1 == nrows() && type_ == hullMultline) {
784 bool const b = nonum_[row - 1];
785 nonum_[row - 1] = nonum_[row];
787 swap(label_[row - 1], label_[row]);
788 InsetMathGrid::delRow(row);
791 InsetMathGrid::delRow(row);
792 // The last dummy row has no number info nor a label.
793 // Test nrows() + 1 because we have already erased the row.
794 if (row == nrows() + 1)
796 nonum_.erase(nonum_.begin() + row);
798 label_.erase(label_.begin() + row);
802 void InsetMathHull::addCol(col_type col)
806 InsetMathGrid::addCol(col);
810 void InsetMathHull::delCol(col_type col)
812 if (ncols() <= 1 || !colChangeOK())
814 InsetMathGrid::delCol(col);
818 docstring InsetMathHull::nicelabel(row_type row) const
823 return from_ascii("(#)");
824 return '(' + label_[row]->screenLabel() + from_ascii(", #)");
828 void InsetMathHull::glueall()
831 for (idx_type i = 0; i < nargs(); ++i)
833 *this = InsetMathHull(buffer_, hullSimple);
839 void InsetMathHull::splitTo2Cols()
841 LASSERT(ncols() == 1, /**/);
842 InsetMathGrid::addCol(1);
843 for (row_type row = 0; row < nrows(); ++row) {
844 idx_type const i = 2 * row;
845 pos_type pos = firstRelOp(cell(i));
846 cell(i + 1) = MathData(buffer_, cell(i).begin() + pos, cell(i).end());
847 cell(i).erase(pos, cell(i).size());
852 void InsetMathHull::splitTo3Cols()
854 LASSERT(ncols() < 3, /**/);
857 InsetMathGrid::addCol(2);
858 for (row_type row = 0; row < nrows(); ++row) {
859 idx_type const i = 3 * row + 1;
860 if (cell(i).size()) {
861 cell(i + 1) = MathData(buffer_, cell(i).begin() + 1, cell(i).end());
862 cell(i).erase(1, cell(i).size());
868 void InsetMathHull::changeCols(col_type cols)
872 else if (ncols() < cols) {
878 while (ncols() < cols)
879 InsetMathGrid::addCol(ncols());
885 for (row_type row = 0; row < nrows(); ++row) {
886 idx_type const i = row * ncols();
887 for (col_type col = cols; col < ncols(); ++col) {
888 cell(i + cols - 1).append(cell(i + col));
892 while (ncols() > cols) {
893 InsetMathGrid::delCol(ncols() - 1);
898 HullType InsetMathHull::getType() const
904 void InsetMathHull::setType(HullType type)
911 void InsetMathHull::mutate(HullType newtype)
913 //lyxerr << "mutating from '" << type_ << "' to '" << newtype << "'" << endl;
915 // we try to move along the chain
916 // none <-> simple <-> equation <-> eqnarray -> *align* -> multline, gather -+
918 // +-------------------------------------+
919 // we use eqnarray as intermediate type for mutations that are not
920 // directly supported because it handles labels and numbering for
923 if (newtype == type_) {
927 else if (newtype < hullNone) {
932 else if (type_ == hullNone) {
938 else if (type_ == hullSimple) {
939 if (newtype == hullNone) {
943 setType(hullEquation);
949 else if (type_ == hullEquation) {
950 if (newtype < type_) {
954 } else if (newtype == hullEqnArray) {
955 // split it "nicely" on the first relop
957 setType(hullEqnArray);
958 } else if (newtype == hullMultline || newtype == hullGather) {
968 else if (type_ == hullEqnArray) {
969 if (newtype < type_) {
970 // set correct (no)numbering
972 for (row_type row = 0; row < nrows(); ++row) {
979 // set first non-empty label
980 for (row_type row = 0; row < nrows(); ++row) {
982 label_[0] = label_[row];
989 } else { // align & Co.
996 else if (type_ == hullAlign || type_ == hullAlignAt ||
997 type_ == hullXAlignAt || type_ == hullFlAlign) {
998 if (newtype < hullAlign) {
1000 setType(hullEqnArray);
1002 } else if (newtype == hullGather || newtype == hullMultline) {
1005 } else if (newtype == hullXXAlignAt) {
1006 for (row_type row = 0; row < nrows(); ++row)
1007 numbered(row, false);
1014 else if (type_ == hullXXAlignAt) {
1015 for (row_type row = 0; row < nrows(); ++row)
1016 numbered(row, false);
1017 if (newtype < hullAlign) {
1019 setType(hullEqnArray);
1021 } else if (newtype == hullGather || newtype == hullMultline) {
1029 else if (type_ == hullMultline || type_ == hullGather) {
1030 if (newtype == hullGather || newtype == hullMultline)
1032 else if (newtype == hullAlign || newtype == hullFlAlign ||
1033 newtype == hullAlignAt || newtype == hullXAlignAt) {
1036 } else if (newtype == hullXXAlignAt) {
1038 for (row_type row = 0; row < nrows(); ++row)
1039 numbered(row, false);
1043 setType(hullEqnArray);
1049 lyxerr << "mutation from '" << to_utf8(hullName(type_))
1050 << "' to '" << to_utf8(hullName(newtype))
1051 << "' not implemented" << endl;
1056 docstring InsetMathHull::eolString(row_type row, bool fragile) const
1059 if (numberedType()) {
1060 if (label_[row] && !nonum_[row])
1061 res += "\\label{" + label_[row]->getParam("name") + '}';
1062 if (nonum_[row] && (type_ != hullMultline))
1063 res += "\\nonumber ";
1065 return res + InsetMathGrid::eolString(row, fragile);
1069 void InsetMathHull::write(WriteStream & os) const
1071 ModeSpecifier specifier(os, MATH_MODE);
1073 InsetMathGrid::write(os);
1078 void InsetMathHull::normalize(NormalStream & os) const
1080 os << "[formula " << hullName(type_) << ' ';
1081 InsetMathGrid::normalize(os);
1086 void InsetMathHull::mathmlize(MathStream & os) const
1088 InsetMathGrid::mathmlize(os);
1092 void InsetMathHull::infoize(odocstream & os) const
1094 os << "Type: " << hullName(type_);
1098 void InsetMathHull::check() const
1100 LASSERT(nonum_.size() == nrows(), /**/);
1101 LASSERT(label_.size() == nrows(), /**/);
1105 void InsetMathHull::doExtern(Cursor & cur, FuncRequest & func)
1109 idocstringstream iss(func.argument());
1110 iss >> dlang >> extra;
1112 extra = from_ascii("noextra");
1113 string const lang = to_ascii(dlang);
1115 // FIXME: temporarily disabled
1116 //if (cur.selection()) {
1119 // lyxerr << "use selection: " << ar << endl;
1120 // insert(pipeThroughExtern(lang, extra, ar));
1125 eq.push_back(MathAtom(new InsetMathChar('=')));
1127 // go to first item in line
1128 cur.idx() -= cur.idx() % ncols();
1131 if (getType() == hullSimple) {
1132 size_type pos = cur.cell().find_last(eq);
1134 if (cur.inMathed() && cur.selection()) {
1135 asArray(grabAndEraseSelection(cur), ar);
1136 } else if (pos == cur.cell().size()) {
1138 lyxerr << "use whole cell: " << ar << endl;
1140 ar = MathData(buffer_, cur.cell().begin() + pos + 1, cur.cell().end());
1141 lyxerr << "use partial cell form pos: " << pos << endl;
1143 cur.cell().append(eq);
1144 cur.cell().append(pipeThroughExtern(lang, extra, ar));
1145 cur.pos() = cur.lastpos();
1149 if (getType() == hullEquation) {
1150 lyxerr << "use equation inset" << endl;
1151 mutate(hullEqnArray);
1152 MathData & ar = cur.cell();
1153 lyxerr << "use cell: " << ar << endl;
1157 cur.cell() = pipeThroughExtern(lang, extra, ar);
1158 // move to end of line
1159 cur.pos() = cur.lastpos();
1164 lyxerr << "use eqnarray" << endl;
1165 cur.idx() += 2 - cur.idx() % ncols();
1167 MathData ar = cur.cell();
1168 lyxerr << "use cell: " << ar << endl;
1169 // FIXME: temporarily disabled
1175 cur.cell() = pipeThroughExtern(lang, extra, ar);
1176 cur.pos() = cur.lastpos();
1181 void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd)
1183 //lyxerr << "action: " << cmd.action << endl;
1184 switch (cmd.action) {
1186 case LFUN_FINISHED_BACKWARD:
1187 case LFUN_FINISHED_FORWARD:
1188 case LFUN_FINISHED_RIGHT:
1189 case LFUN_FINISHED_LEFT:
1190 //lyxerr << "action: " << cmd.action << endl;
1191 InsetMathGrid::doDispatch(cur, cmd);
1195 case LFUN_BREAK_PARAGRAPH:
1196 // just swallow this
1199 case LFUN_NEWLINE_INSERT:
1200 // some magic for the common case
1201 if (type_ == hullSimple || type_ == hullEquation) {
1202 cur.recordUndoInset();
1204 cur.bv().buffer().params().use_amsmath == BufferParams::package_on;
1205 mutate(align ? hullAlign : hullEqnArray);
1206 cur.idx() = nrows() * ncols() - 1;
1207 cur.pos() = cur.lastpos();
1209 InsetMathGrid::doDispatch(cur, cmd);
1212 case LFUN_MATH_NUMBER_TOGGLE: {
1213 //lyxerr << "toggling all numbers" << endl;
1214 cur.recordUndoInset();
1215 bool old = numberedType();
1216 if (type_ == hullMultline)
1217 numbered(nrows() - 1, !old);
1219 for (row_type row = 0; row < nrows(); ++row)
1220 numbered(row, !old);
1222 cur.message(old ? _("No number") : _("Number"));
1226 case LFUN_MATH_NUMBER_LINE_TOGGLE: {
1227 cur.recordUndoInset();
1228 row_type r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1229 bool old = numbered(r);
1230 cur.message(old ? _("No number") : _("Number"));
1235 case LFUN_LABEL_INSERT: {
1236 cur.recordUndoInset();
1237 row_type r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1238 docstring old_label = label(r);
1239 docstring const default_label = from_ascii(
1240 (lyxrc.label_init_length >= 0) ? "eq:" : "");
1241 if (old_label.empty())
1242 old_label = default_label;
1244 InsetCommandParams p(LABEL_CODE);
1245 p["name"] = cmd.argument().empty() ? old_label : cmd.argument();
1246 string const data = InsetCommand::params2string("label", p);
1248 if (cmd.argument().empty())
1249 cur.bv().showDialog("label", data);
1251 FuncRequest fr(LFUN_INSET_INSERT, data);
1257 case LFUN_LABEL_COPY_AS_REF: {
1259 if (cmd.argument().empty() && &cur.inset() == this)
1260 // if there is no argument and we're inside math, we retrieve
1261 // the row number from the cursor position.
1262 row = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1264 // if there is an argument, find the corresponding label, else
1265 // check whether there is at least one label.
1266 for (row = 0; row != nrows(); ++row)
1267 if (!nonum_[row] && label_[row]
1268 && (cmd.argument().empty() || label(row) == cmd.argument()))
1275 InsetCommandParams p(REF_CODE, "ref");
1276 p["reference"] = label(row);
1277 cap::clearSelection();
1278 cap::copyInset(cur, new InsetRef(buffer_, p), label(row));
1282 case LFUN_WORD_DELETE_FORWARD:
1283 case LFUN_CHAR_DELETE_FORWARD:
1284 if (col(cur.idx()) + 1 == ncols()
1285 && cur.pos() == cur.lastpos()
1286 && !cur.selection()) {
1287 if (!label(row(cur.idx())).empty()) {
1288 cur.recordUndoInset();
1289 label(row(cur.idx()), docstring());
1290 } else if (numbered(row(cur.idx()))) {
1291 cur.recordUndoInset();
1292 numbered(row(cur.idx()), false);
1294 InsetMathGrid::doDispatch(cur, cmd);
1298 InsetMathGrid::doDispatch(cur, cmd);
1303 case LFUN_INSET_INSERT: {
1304 //lyxerr << "arg: " << to_utf8(cmd.argument()) << endl;
1305 // FIXME: this should be cleaned up to use InsetLabel methods directly.
1306 string const name = cmd.getArg(0);
1307 if (name == "label") {
1308 InsetCommandParams p(LABEL_CODE);
1309 InsetCommand::string2params(name, to_utf8(cmd.argument()), p);
1310 docstring str = p["name"];
1311 cur.recordUndoInset();
1312 row_type const r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1316 docstring old = label(r);
1319 // The label will take care of the reference update.
1323 // Newly created inset so initialize it.
1324 label_[r]->initView();
1329 InsetMathGrid::doDispatch(cur, cmd);
1333 case LFUN_MATH_EXTERN:
1334 cur.recordUndoInset();
1338 case LFUN_MATH_MUTATE: {
1339 cur.recordUndoInset();
1340 row_type row = cur.row();
1341 col_type col = cur.col();
1342 mutate(hullType(cmd.argument()));
1343 cur.idx() = row * ncols() + col;
1344 if (cur.idx() > cur.lastidx()) {
1345 cur.idx() = cur.lastidx();
1346 cur.pos() = cur.lastpos();
1348 if (cur.pos() > cur.lastpos())
1349 cur.pos() = cur.lastpos();
1351 // FIXME: find some more clever handling of the selection,
1352 // i.e. preserve it.
1353 cur.clearSelection();
1354 //cur.dispatched(FINISHED);
1358 case LFUN_MATH_DISPLAY: {
1359 cur.recordUndoInset();
1360 mutate(type_ == hullSimple ? hullEquation : hullSimple);
1362 cur.pos() = cur.lastpos();
1363 //cur.dispatched(FINISHED);
1368 InsetMathGrid::doDispatch(cur, cmd);
1374 bool InsetMathHull::getStatus(Cursor & cur, FuncRequest const & cmd,
1375 FuncStatus & status) const
1377 switch (cmd.action) {
1378 case LFUN_FINISHED_BACKWARD:
1379 case LFUN_FINISHED_FORWARD:
1380 case LFUN_FINISHED_RIGHT:
1381 case LFUN_FINISHED_LEFT:
1384 case LFUN_NEWLINE_INSERT:
1385 case LFUN_MATH_EXTERN:
1386 case LFUN_MATH_DISPLAY:
1388 status.setEnabled(true);
1391 case LFUN_MATH_MUTATE: {
1392 HullType ht = hullType(cmd.argument());
1393 status.setOnOff(type_ == ht);
1394 status.setEnabled(true);
1398 case LFUN_MATH_NUMBER_TOGGLE:
1399 // FIXME: what is the right test, this or the one of
1401 status.setEnabled(display());
1402 status.setOnOff(numberedType());
1405 case LFUN_MATH_NUMBER_LINE_TOGGLE: {
1406 // FIXME: what is the right test, this or the one of
1408 bool const enable = (type_ == hullMultline)
1409 ? (nrows() - 1 == cur.row())
1410 : display() != Inline && nrows() > 1;
1411 row_type const r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1412 status.setEnabled(enable);
1413 status.setOnOff(enable && numbered(r));
1417 case LFUN_LABEL_INSERT:
1418 status.setEnabled(type_ != hullSimple);
1421 case LFUN_LABEL_COPY_AS_REF: {
1422 bool enabled = false;
1424 if (cmd.argument().empty() && &cur.inset() == this) {
1425 // if there is no argument and we're inside math, we retrieve
1426 // the row number from the cursor position.
1427 row = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1428 enabled = numberedType() && label_[row] && !nonum_[row];
1430 // if there is an argument, find the corresponding label, else
1431 // check whether there is at least one label.
1432 for (row_type row = 0; row != nrows(); ++row) {
1433 if (!nonum_[row] && label_[row] &&
1434 (cmd.argument().empty() || label(row) == cmd.argument())) {
1440 status.setEnabled(enabled);
1444 case LFUN_INSET_INSERT:
1445 if (cmd.getArg(0) == "label") {
1446 status.setEnabled(type_ != hullSimple);
1449 return InsetMathGrid::getStatus(cur, cmd, status);
1451 case LFUN_TABULAR_FEATURE: {
1452 istringstream is(to_utf8(cmd.argument()));
1456 && (s == "append-row"
1457 || s == "delete-row"
1458 || s == "copy-row")) {
1459 status.message(bformat(
1460 from_utf8(N_("Can't change number of rows in '%1$s'")),
1462 status.setEnabled(false);
1466 && (s == "append-column"
1467 || s == "delete-column"
1468 || s == "copy-column")) {
1469 status.message(bformat(
1470 from_utf8(N_("Can't change number of columns in '%1$s'")),
1472 status.setEnabled(false);
1475 if ((type_ == hullSimple
1476 || type_ == hullEquation
1477 || type_ == hullNone) &&
1478 (s == "add-hline-above" || s == "add-hline-below")) {
1479 status.message(bformat(
1480 from_utf8(N_("Can't add horizontal grid lines in '%1$s'")),
1482 status.setEnabled(false);
1485 if (s == "add-vline-left" || s == "add-vline-right") {
1486 status.message(bformat(
1487 from_utf8(N_("Can't add vertical grid lines in '%1$s'")),
1489 status.setEnabled(false);
1492 if (s == "valign-top" || s == "valign-middle"
1493 || s == "valign-bottom" || s == "align-left"
1494 || s == "align-center" || s == "align-right") {
1495 status.setEnabled(false);
1498 return InsetMathGrid::getStatus(cur, cmd, status);
1502 return InsetMathGrid::getStatus(cur, cmd, status);
1505 // This cannot really happen, but inserted to shut-up gcc
1506 return InsetMathGrid::getStatus(cur, cmd, status);
1510 /////////////////////////////////////////////////////////////////////
1514 // simply scrap this function if you want
1515 void InsetMathHull::mutateToText()
1518 // translate to latex
1520 latex(os, false, false);
1521 string str = os.str();
1524 Text * lt = view_->cursor().innerText();
1525 string::const_iterator cit = str.begin();
1526 string::const_iterator end = str.end();
1527 for (; cit != end; ++cit)
1528 view_->getIntl()->getTransManager().TranslateAndInsert(*cit, lt);
1531 //dispatch(LFUN_ESCAPE);
1536 void InsetMathHull::handleFont(Cursor & cur, docstring const & arg,
1537 docstring const & font)
1539 // this whole function is a hack and won't work for incremental font
1542 if (cur.inset().asInsetMath()->name() == font)
1543 cur.handleFont(to_utf8(font));
1545 cur.handleNest(createInsetMath(font, cur.buffer()));
1551 void InsetMathHull::handleFont2(Cursor & cur, docstring const & arg)
1556 font.fromString(to_utf8(arg), b);
1557 if (font.fontInfo().color() != Color_inherit) {
1558 MathAtom at = MathAtom(new InsetMathColor(buffer_, true, font.fontInfo().color()));
1559 cur.handleNest(at, 0);
1564 void InsetMathHull::edit(Cursor & cur, bool front, EntryDirection entry_from)
1567 bool enter_front = (entry_from == Inset::ENTRY_DIRECTION_LEFT ||
1568 (entry_from == Inset::ENTRY_DIRECTION_IGNORE && front));
1569 enter_front ? idxFirst(cur) : idxLast(cur);
1570 // The inset formula dimension is not necessarily the same as the
1571 // one of the instant preview image, so we have to indicate to the
1572 // BufferView that a metrics update is needed.
1573 cur.updateFlags(Update::Force);
1577 void InsetMathHull::revealCodes(Cursor & cur) const
1579 if (!cur.inMathed())
1581 odocstringstream os;
1583 cur.message(os.str());
1585 // write something to the minibuffer
1586 // translate to latex
1590 string str = os.str();
1592 string::size_type pos = 0;
1594 for (string::iterator it = str.begin(); it != str.end(); ++it) {
1597 else if (*it == '\0') {
1599 pos = it - str.begin();
1605 res = res.substr(pos - 30);
1606 if (res.size() > 60)
1607 res = res.substr(0, 60);
1613 /////////////////////////////////////////////////////////////////////
1617 bool InsetMathHull::searchForward(BufferView * bv, string const & str,
1620 // FIXME: completely broken
1621 static InsetMathHull * lastformula = 0;
1622 static CursorBase current = DocIterator(ibegin(nucleus()));
1624 static string laststr;
1626 if (lastformula != this || laststr != str) {
1627 //lyxerr << "reset lastformula to " << this << endl;
1630 current = ibegin(nucleus());
1632 mathed_parse_cell(ar, str, Parse::NORMAL, &buffer());
1636 //lyxerr << "searching '" << str << "' in " << this << ar << endl;
1638 for (DocIterator it = current; it != iend(nucleus()); increment(it)) {
1639 CursorSlice & top = it.back();
1640 MathData const & a = top.asInsetMath()->cell(top.idx_);
1641 if (a.matchpart(ar, top.pos_)) {
1642 bv->cursor().setSelection(it, ar.size());
1644 top.pos_ += ar.size();
1650 //lyxerr << "not found!" << endl;
1657 void InsetMathHull::write(ostream & os) const
1659 odocstringstream oss;
1660 WriteStream wi(oss, false, false, WriteStream::wsDefault);
1663 os << to_utf8(oss.str());
1667 void InsetMathHull::read(Lexer & lex)
1670 mathed_parse_normal(buffer_, at, lex, Parse::TRACKMACRO);
1671 operator=(*at->asHullInset());
1675 bool InsetMathHull::readQuiet(Lexer & lex)
1678 bool success = mathed_parse_normal(buffer_, at, lex, Parse::QUIET);
1680 operator=(*at->asHullInset());
1685 int InsetMathHull::plaintext(odocstream & os, OutputParams const &) const
1687 if (0 && display()) {
1691 TextPainter tpain(dim.width(), dim.height());
1692 drawT(tpain, 0, dim.ascent());
1694 // reset metrics cache to "real" values
1696 return tpain.textheight();
1698 odocstringstream oss;
1699 Encoding const * const enc = encodings.fromLyXName("utf8");
1700 WriteStream wi(oss, false, true, WriteStream::wsDefault, enc);
1702 if (type_ == hullRegexp)
1706 docstring const str = oss.str();
1713 int InsetMathHull::docbook(odocstream & os, OutputParams const & runparams) const
1718 if (getType() == hullSimple)
1719 name = from_ascii("inlineequation");
1721 name = from_ascii("informalequation");
1723 docstring bname = name;
1724 if (!label(0).empty())
1725 bname += " id='" + sgml::cleanID(buffer(), runparams, label(0)) + "'";
1727 ++ms.tab(); ms.cr(); ms.os() << '<' << bname << '>';
1729 odocstringstream ls;
1730 if (runparams.flavor == OutputParams::XML) {
1731 ms << MTag("alt role='tex' ");
1732 // Workaround for db2latex: db2latex always includes equations with
1733 // \ensuremath{} or \begin{display}\end{display}
1734 // so we strip LyX' math environment
1735 WriteStream wi(ls, false, false, WriteStream::wsDefault, runparams.encoding);
1736 InsetMathGrid::write(wi);
1737 ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&"), "<", "<"));
1742 InsetMathGrid::mathmlize(ms);
1745 ms << MTag("alt role='tex'");
1746 res = latex(ls, runparams);
1747 ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&"), "<", "<"));
1751 ms << from_ascii("<graphic fileref=\"eqn/");
1752 if (!label(0).empty())
1753 ms << sgml::cleanID(buffer(), runparams, label(0));
1755 ms << sgml::uniqueID(from_ascii("anon"));
1757 if (runparams.flavor == OutputParams::XML)
1758 ms << from_ascii("\"/>");
1760 ms << from_ascii("\">");
1762 ms.cr(); --ms.tab(); ms.os() << "</" << name << '>';
1764 return ms.line() + res;
1768 docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const &) const
1770 if (getType() == hullSimple)
1771 xs << StartTag("math", "xmlns=\"http://www.w3.org/1998/Math/MathML\"", true);
1773 xs << StartTag("math",
1774 "display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"", true);
1775 MathStream ms(xs.os());
1776 InsetMathGrid::mathmlize(ms);
1777 xs << EndTag("math");
1782 void InsetMathHull::tocString(odocstream & os) const
1784 plaintext(os, OutputParams(0));
1788 docstring InsetMathHull::contextMenu(BufferView const &, int, int) const
1790 return from_ascii("context-math");