1 /**
2  * \file InsetMathHull.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10 
11 #include <config.h>
12 
13 #include "InsetMathHull.h"
14 
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"
24 
25 #include "Buffer.h"
26 #include "BufferParams.h"
27 #include "BufferView.h"
28 #include "ColorSet.h"
29 #include "CutAndPaste.h"
30 #include "Encoding.h"
31 #include "Exporter.h"
32 #include "FuncRequest.h"
33 #include "FuncStatus.h"
34 #include "Language.h"
35 #include "LaTeXFeatures.h"
36 #include "LyXRC.h"
37 #include "MacroTable.h"
38 #include "InsetMathMacro.h"
39 #include "InsetMathMacroTemplate.h"
40 #include "MetricsInfo.h"
41 #include "output_xhtml.h"
42 #include "Paragraph.h"
43 #include "ParIterator.h"
44 #include "sgml.h"
45 #include "TexRow.h"
46 #include "TextClass.h"
47 #include "TextPainter.h"
48 #include "TocBackend.h"
49 
50 #include "insets/InsetLabel.h"
51 #include "insets/InsetRef.h"
52 #include "insets/RenderPreview.h"
53 
54 #include "graphics/GraphicsImage.h"
55 #include "graphics/PreviewImage.h"
56 #include "graphics/PreviewLoader.h"
57 
58 #include "frontends/alert.h"
59 #include "frontends/Painter.h"
60 
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/RefChanger.h"
68 
69 #include <sstream>
70 
71 using namespace std;
72 using namespace lyx::support;
73 
74 namespace lyx {
75 
76 using cap::grabAndEraseSelection;
77 using cap::reduceSelectionToOneCell;
78 
79 namespace {
80 
getCols(HullType type)81 	int getCols(HullType type)
82 	{
83 		switch (type) {
84 		case hullEqnArray:
85 			return 3;
86 		case hullAlign:
87 		case hullFlAlign:
88 		case hullAlignAt:
89 		case hullXAlignAt:
90 		case hullXXAlignAt:
91 			return 2;
92 		case hullUnknown:
93 		case hullNone:
94 		case hullSimple:
95 		case hullEquation:
96 		case hullMultline:
97 		case hullGather:
98 		case hullRegexp:
99 			return 1;
100 		}
101 		// avoid warning
102 		return 0;
103 	}
104 
105 
106 	// returns position of first relation operator in the array
107 	// used for "intelligent splitting"
firstRelOp(MathData const & ar)108 	size_t firstRelOp(MathData const & ar)
109 	{
110 		for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
111 			if ((*it)->mathClass() == MC_REL)
112 				return it - ar.begin();
113 		return ar.size();
114 	}
115 
116 
star(bool numbered)117 	char const * star(bool numbered)
118 	{
119 		return numbered ? "" : "*";
120 	}
121 
122 
123 	// writes a preamble for underlined or struck out math display
writeMathdisplayPreamble(WriteStream & os)124 	void writeMathdisplayPreamble(WriteStream & os)
125 	{
126 		if (os.strikeoutMath()) {
127 			if (os.ulemCmd() == WriteStream::UNDERLINE)
128 				os << "\\raisebox{-\\belowdisplayshortskip}{"
129 				      "\\lyxmathsout{\\parbox[b]{\\linewidth}{";
130 			else
131 				os << "\\lyxmathsout{\\parbox{\\linewidth}{";
132 		} else if (os.ulemCmd() == WriteStream::UNDERLINE)
133 			os << "\\raisebox{-\\belowdisplayshortskip}{"
134 			      "\\parbox[b]{\\linewidth}{";
135 		else if (os.ulemCmd() == WriteStream::STRIKEOUT)
136 			os << "\\parbox{\\linewidth}{";
137 	}
138 
139 
140 	// writes a postamble for underlined or struck out math display
writeMathdisplayPostamble(WriteStream & os)141 	void writeMathdisplayPostamble(WriteStream & os)
142 	{
143 		if (os.strikeoutMath()) {
144 			if (os.ulemCmd() == WriteStream::UNDERLINE)
145 				os << "}";
146 			os << "}}\\\\\n";
147 		} else if (os.ulemCmd() == WriteStream::UNDERLINE)
148 			os << "}}\\\\\n";
149 		else if (os.ulemCmd() == WriteStream::STRIKEOUT)
150 			os << "}\\\\\n";
151 	}
152 
153 
154 } // namespace
155 
156 
hullType(docstring const & s)157 HullType hullType(docstring const & s)
158 {
159 	if (s == "none")      return hullNone;
160 	if (s == "simple")    return hullSimple;
161 	if (s == "equation")  return hullEquation;
162 	if (s == "eqnarray")  return hullEqnArray;
163 	if (s == "align")     return hullAlign;
164 	if (s == "alignat")   return hullAlignAt;
165 	if (s == "xalignat")  return hullXAlignAt;
166 	if (s == "xxalignat") return hullXXAlignAt;
167 	if (s == "multline")  return hullMultline;
168 	if (s == "gather")    return hullGather;
169 	if (s == "flalign")   return hullFlAlign;
170 	if (s == "regexp")    return hullRegexp;
171 	lyxerr << "unknown hull type '" << to_utf8(s) << "'" << endl;
172 	return hullUnknown;
173 }
174 
175 
hullName(HullType type)176 docstring hullName(HullType type)
177 {
178 	switch (type) {
179 	case hullNone:       return from_ascii("none");
180 	case hullSimple:     return from_ascii("simple");
181 	case hullEquation:   return from_ascii("equation");
182 	case hullEqnArray:   return from_ascii("eqnarray");
183 	case hullAlign:      return from_ascii("align");
184 	case hullAlignAt:    return from_ascii("alignat");
185 	case hullXAlignAt:   return from_ascii("xalignat");
186 	case hullXXAlignAt:  return from_ascii("xxalignat");
187 	case hullMultline:   return from_ascii("multline");
188 	case hullGather:     return from_ascii("gather");
189 	case hullFlAlign:    return from_ascii("flalign");
190 	case hullRegexp:     return from_ascii("regexp");
191 	case hullUnknown:
192 		lyxerr << "unknown hull type" << endl;
193 		break;
194 	}
195 	return from_ascii("none");
196 }
197 
198 static InsetLabel * dummy_pointer = 0;
199 
InsetMathHull(Buffer * buf)200 InsetMathHull::InsetMathHull(Buffer * buf)
201 	: InsetMathGrid(buf, 1, 1), type_(hullNone), numbered_(1, NONUMBER),
202 	  numbers_(1, empty_docstring()), label_(1, dummy_pointer),
203 	  preview_(new RenderPreview(this))
204 {
205 	//lyxerr << "sizeof InsetMath: " << sizeof(InsetMath) << endl;
206 	//lyxerr << "sizeof MetricsInfo: " << sizeof(MetricsInfo) << endl;
207 	//lyxerr << "sizeof InsetMathChar: " << sizeof(InsetMathChar) << endl;
208 	//lyxerr << "sizeof FontInfo: " << sizeof(FontInfo) << endl;
209 	buffer_ = buf;
210 	initMath();
211 	setDefaults();
212 }
213 
214 
InsetMathHull(Buffer * buf,HullType type)215 InsetMathHull::InsetMathHull(Buffer * buf, HullType type)
216 	: InsetMathGrid(buf, getCols(type), 1), type_(type), numbered_(1, NONUMBER),
217 	  numbers_(1, empty_docstring()), label_(1, dummy_pointer),
218 	  preview_(new RenderPreview(this))
219 {
220 	buffer_ = buf;
221 	initMath();
222 	setDefaults();
223 }
224 
225 
InsetMathHull(InsetMathHull const & other)226 InsetMathHull::InsetMathHull(InsetMathHull const & other) : InsetMathGrid(other)
227 {
228 	operator=(other);
229 }
230 
231 
~InsetMathHull()232 InsetMathHull::~InsetMathHull()
233 {
234 	for (size_t i = 0; i < label_.size(); ++i)
235 		delete label_[i];
236 }
237 
238 
clone() const239 Inset * InsetMathHull::clone() const
240 {
241 	return new InsetMathHull(*this);
242 }
243 
244 
operator =(InsetMathHull const & other)245 InsetMathHull & InsetMathHull::operator=(InsetMathHull const & other)
246 {
247 	if (this == &other)
248 		return *this;
249 	InsetMathGrid::operator=(other);
250 	type_  = other.type_;
251 	numbered_ = other.numbered_;
252 	numbers_ = other.numbers_;
253 	buffer_ = other.buffer_;
254 	for (size_t i = 0; i < label_.size(); ++i)
255 		delete label_[i];
256 	label_ = other.label_;
257 	for (size_t i = 0; i != label_.size(); ++i) {
258 		if (label_[i])
259 			label_[i] = new InsetLabel(*label_[i]);
260 	}
261 	preview_.reset(new RenderPreview(*other.preview_, this));
262 
263 	return *this;
264 }
265 
266 
setBuffer(Buffer & buffer)267 void InsetMathHull::setBuffer(Buffer & buffer)
268 {
269 	InsetMathGrid::setBuffer(buffer);
270 
271 	for (size_t i = 0; i != label_.size(); ++i) {
272 		if (label_[i])
273 			label_[i]->setBuffer(buffer);
274 	}
275 }
276 
277 
278 // FIXME This should really be controlled by the TOC level, or
279 // something of the sort.
280 namespace {
281 	const char * counters_to_save[] = {"section", "chapter"};
282 	unsigned int const numcnts = sizeof(counters_to_save)/sizeof(char *);
283 } // namespace
284 
285 
updateBuffer(ParIterator const & it,UpdateType utype)286 void InsetMathHull::updateBuffer(ParIterator const & it, UpdateType utype)
287 {
288 	if (!buffer_) {
289 		//FIXME: buffer_ should be set at creation for this inset! Problem is
290 		// This inset is created at too many places (see Parser::parse1() in
291 		// MathParser.cpp).
292 		return;
293 	}
294 
295 	// if any of the equations are numbered, then we want to save the values
296 	// of some of the counters.
297 	if (haveNumbers()) {
298 		BufferParams const & bp = buffer_->params();
299 		string const & lang = it->getParLanguage(bp)->code();
300 		Counters & cnts =
301 			buffer_->masterBuffer()->params().documentClass().counters();
302 
303 		// right now, we only need to do this at export time
304 		if (utype == OutputUpdate) {
305 			for (size_t i = 0; i < numcnts; ++i) {
306 				docstring const cnt = from_ascii(counters_to_save[i]);
307 				if (cnts.hasCounter(cnt))
308 					counter_map[cnt] = cnts.value(cnt);
309 			}
310 		}
311 
312 		// this has to be done separately
313 		docstring const eqstr = from_ascii("equation");
314 		if (cnts.hasCounter(eqstr)) {
315 			if (utype == OutputUpdate)
316 				counter_map[eqstr] = cnts.value(eqstr);
317 			for (size_t i = 0; i != label_.size(); ++i) {
318 				if (numbered(i)) {
319 					Paragraph const & par = it.paragraph();
320 					if (!par.isDeleted(it.pos())) {
321 						cnts.step(eqstr, utype);
322 						numbers_[i] = cnts.theCounter(eqstr, lang);
323 					} else
324 						numbers_[i] = from_ascii("#");
325 				} else
326 					numbers_[i] = empty_docstring();
327 			}
328 		}
329 	}
330 
331 	// now the labels
332 	for (size_t i = 0; i != label_.size(); ++i) {
333 		if (label_[i])
334 			label_[i]->updateBuffer(it, utype);
335 	}
336 	// pass down
337 	InsetMathGrid::updateBuffer(it, utype);
338 }
339 
340 
addToToc(DocIterator const & pit,bool output_active,UpdateType utype,TocBackend & backend) const341 void InsetMathHull::addToToc(DocIterator const & pit, bool output_active,
342 							 UpdateType utype, TocBackend & backend) const
343 {
344 	if (!buffer_) {
345 		//FIXME: buffer_ should be set at creation for this inset! Problem is
346 		// This inset is created at too many places (see Parser::parse1() in
347 		// MathParser.cpp).
348 		return;
349 	}
350 
351 	TocBuilder & b = backend.builder("equation");
352 	// compute first and last item
353 	row_type first = nrows();
354 	for (row_type row = 0; row != nrows(); ++row)
355 		if (numbered(row)) {
356 			first = row;
357 			break;
358 		}
359 	if (first == nrows())
360 		// no equation
361 		return;
362 	row_type last = nrows() - 1;
363 	for (; last != 0; --last)
364 		if (numbered(last))
365 			break;
366 	// add equation numbers
367 	b.pushItem(pit, docstring(), output_active);
368 	if (first != last)
369 		b.argumentItem(bformat(from_ascii("(%1$s-%2$s)"),
370 		                       numbers_[first], numbers_[last]));
371 	for (row_type row = 0; row != nrows(); ++row) {
372 		if (!numbered(row))
373 			continue;
374 		if (label_[row])
375 			label_[row]->addToToc(pit, output_active, utype, backend);
376 		docstring label = nicelabel(row);
377 		if (first == last)
378 			// this is the only equation
379 			b.argumentItem(label);
380 		else {
381 			// insert as sub-items
382 			b.pushItem(pit, label, output_active);
383 			b.pop();
384 		}
385 	}
386 	b.pop();
387 }
388 
389 
editXY(Cursor & cur,int x,int y)390 Inset * InsetMathHull::editXY(Cursor & cur, int x, int y)
391 {
392 	if (previewState(&cur.bv())) {
393 		edit(cur, true);
394 		return this;
395 	}
396 	return InsetMathNest::editXY(cur, x, y);
397 }
398 
399 
currentMode() const400 InsetMath::mode_type InsetMathHull::currentMode() const
401 {
402 	switch (type_) {
403 	case hullNone:
404 		return UNDECIDED_MODE;
405 
406 	// definitely math mode ...
407 	case hullUnknown:
408 	case hullSimple:
409 	case hullEquation:
410 	case hullMultline:
411 	case hullGather:
412 	case hullEqnArray:
413 	case hullAlign:
414 	case hullFlAlign:
415 	case hullAlignAt:
416 	case hullXAlignAt:
417 	case hullXXAlignAt:
418 	case hullRegexp:
419 		return MATH_MODE;
420 	}
421 	// avoid warning
422 	return MATH_MODE;
423 }
424 
425 
idxFirst(Cursor & cur) const426 bool InsetMathHull::idxFirst(Cursor & cur) const
427 {
428 	cur.idx() = 0;
429 	cur.pos() = 0;
430 	return true;
431 }
432 
433 
idxLast(Cursor & cur) const434 bool InsetMathHull::idxLast(Cursor & cur) const
435 {
436 	cur.idx() = nargs() - 1;
437 	cur.pos() = cur.lastpos();
438 	return true;
439 }
440 
441 
442 // FIXME: InsetMathGrid should be changed to let the real column alignment be
443 // given by a virtual method like displayColAlign, because the values produced
444 // by defaultColAlign can be invalidated by lfuns such as add-column. For the
445 // moment the values produced by defaultColAlign are not used, notably because
446 // alignment is not implemented in the LyXHTML output.
defaultColAlign(col_type col)447 char InsetMathHull::defaultColAlign(col_type col)
448 {
449 	return colAlign(type_, col);
450 }
451 
452 
displayColAlign(idx_type idx) const453 char InsetMathHull::displayColAlign(idx_type idx) const
454 {
455 	switch (type_) {
456 	case hullMultline: {
457 		row_type const r = row(idx);
458 		if (r == 0)
459 			return 'l';
460 		if (r == nrows() - 1)
461 			return 'r';
462 		return 'c';
463 	}
464 	case hullEqnArray:
465 	case hullGather:
466 	case hullAlign:
467 	case hullAlignAt:
468 	case hullXAlignAt:
469 	case hullXXAlignAt:
470 	case hullFlAlign:
471 		return colAlign(type_, col(idx));
472 	default:
473 		break;
474 	}
475 	return InsetMathGrid::displayColAlign(idx);
476 }
477 
478 
displayColSpace(col_type col) const479 int InsetMathHull::displayColSpace(col_type col) const
480 {
481 	return colSpace(type_, col);
482 }
483 
484 
485 // FIXME: same comment as for defaultColAlign applies.
defaultColSpace(col_type col)486 int InsetMathHull::defaultColSpace(col_type col)
487 {
488 	return colSpace(type_, col);
489 }
490 
491 
standardFont() const492 string InsetMathHull::standardFont() const
493 {
494 	switch (type_) {
495 	case hullRegexp:
496 		return "texttt";
497 	case hullNone:
498 		return "lyxnochange";
499 	default:
500 		return "mathnormal";
501 	}
502 }
503 
504 
standardColor() const505 ColorCode InsetMathHull::standardColor() const
506 {
507 	switch (type_) {
508 	case hullRegexp:
509 	case hullNone:
510 		return Color_foreground;
511 
512 	default:
513 		return Color_math;
514 	}
515 }
516 
517 
previewState(const BufferView * const bv) const518 bool InsetMathHull::previewState(const BufferView *const bv) const
519 {
520 	if (!editing(bv) && RenderPreview::previewMath()
521 	    && type_ != hullRegexp)
522 	{
523 		graphics::PreviewImage const * pimage =
524 			preview_->getPreviewImage(bv->buffer());
525 		return pimage && pimage->image();
526 	}
527 	return false;
528 }
529 
530 
531 namespace {
532 static const int ERROR_FRAME_WIDTH = 2;
533 }
534 
metrics(MetricsInfo & mi,Dimension & dim) const535 void InsetMathHull::metrics(MetricsInfo & mi, Dimension & dim) const
536 {
537 	if (previewState(mi.base.bv)) {
538 		preview_->metrics(mi, dim);
539 		if (previewTooSmall(dim)) {
540 			// preview image is too small
541 			dim.wid += 2 * ERROR_FRAME_WIDTH;
542 			dim.asc += 2 * ERROR_FRAME_WIDTH;
543 		} else {
544 			// insert a one pixel gap in front of the formula
545 			dim.wid += 1;
546 			if (display())
547 				dim.des += displayMargin();
548 		}
549 		return;
550 	}
551 
552 	Changer dummy1 = mi.base.changeFontSet(standardFont());
553 	Changer dummy2 = mi.base.font.changeStyle(display() ? LM_ST_DISPLAY
554 	                                                    : LM_ST_TEXT);
555 
556 	// let the cells adjust themselves
557 	InsetMathGrid::metrics(mi, dim);
558 
559 	if (display()) {
560 		dim.asc += displayMargin();
561 		dim.des += displayMargin();
562 	}
563 
564 	if (numberedType()) {
565 		Changer dummy = mi.base.changeFontSet("mathrm");
566 		int l = 0;
567 		for (row_type row = 0; row < nrows(); ++row)
568 			l = max(l, mathed_string_width(mi.base.font, nicelabel(row)));
569 
570 		if (l)
571 			// Value was hardcoded to 30 pixels
572 			dim.wid += Length(0.3, Length::IN).inPixels(mi.base) + l;
573 	}
574 
575 	// reserve some space for marker.
576 	dim.wid += 2;
577 }
578 
579 
previewTooSmall(Dimension const & dim) const580 bool InsetMathHull::previewTooSmall(Dimension const & dim) const
581 {
582 	return dim.width() <= 10 && dim.height() <= 10;
583 }
584 
585 
backgroundColor(PainterInfo const & pi) const586 ColorCode InsetMathHull::backgroundColor(PainterInfo const & pi) const
587 {
588 	BufferView const * const bv = pi.base.bv;
589 	if (previewState(bv)) {
590 		Dimension const dim = dimension(*pi.base.bv);
591 		if (previewTooSmall(dim))
592 			return Color_error;
593 		return graphics::PreviewLoader::backgroundColor();
594 	}
595 	return Color_mathbg;
596 }
597 
598 
drawMarkers(PainterInfo & pi,int x,int y) const599 void InsetMathHull::drawMarkers(PainterInfo & pi, int x, int y) const
600 {
601 	ColorCode pen_color = mouseHovered(pi.base.bv) || editing(pi.base.bv)?
602 		Color_mathframe : Color_mathcorners;
603 	// If the corners have the same color as the background, do not paint them.
604 	if (lcolor.getX11Name(Color_mathbg) == lcolor.getX11Name(pen_color))
605 		return;
606 
607 	Inset::drawMarkers(pi, x, y);
608 	Dimension const dim = dimension(*pi.base.bv);
609 	int const t = x + dim.width() - 1;
610 	int const a = y - dim.ascent();
611 	pi.pain.line(x, a + 3, x, a, pen_color);
612 	pi.pain.line(t, a + 3, t, a, pen_color);
613 	pi.pain.line(x, a, x + 3, a, pen_color);
614 	pi.pain.line(t - 3, a, t, a, pen_color);
615 }
616 
617 
drawBackground(PainterInfo & pi,int x,int y) const618 void InsetMathHull::drawBackground(PainterInfo & pi, int x, int y) const
619 {
620 	Dimension const dim = dimension(*pi.base.bv);
621 	if (previewTooSmall(dim)) {
622 		pi.pain.fillRectangle(x, y - 2 * ERROR_FRAME_WIDTH,
623 		    dim.wid, dim.asc + dim.des, backgroundColor(pi));
624 		return;
625 	}
626 	pi.pain.fillRectangle(x + 1, y - dim.asc + 1, dim.wid - 2,
627 			dim.asc + dim.des - 1, pi.backgroundColor(this));
628 }
629 
630 
draw(PainterInfo & pi,int x,int y) const631 void InsetMathHull::draw(PainterInfo & pi, int x, int y) const
632 {
633 	BufferView const * const bv = pi.base.bv;
634 	Dimension const dim = dimension(*bv);
635 
636 	if (type_ == hullRegexp)
637 		pi.pain.rectangle(x + 2, y - dim.ascent() + 1,
638 		                  dim.width() - 3, dim.height() - 2, Color_regexpframe);
639 
640 	if (previewState(bv)) {
641 		// Do not draw change tracking cue if taken care of by RowPainter
642 		// already.
643 		Changer dummy = !canPaintChange(*bv) ? make_change(pi.change_, Change())
644 			: Changer();
645 		if (previewTooSmall(dim)) {
646 			// we have an extra frame
647 			preview_->draw(pi, x + ERROR_FRAME_WIDTH, y);
648 		} else {
649 			// one pixel gap in front
650 			preview_->draw(pi, x + 1, y);
651 		}
652 		return;
653 	}
654 
655 	ColorCode color = pi.selected && lyxrc.use_system_colors
656 				? Color_selectiontext : standardColor();
657 	bool const really_change_color = pi.base.font.color() == Color_none;
658 	Changer dummy0 = really_change_color ? pi.base.font.changeColor(color)
659 		: Changer();
660 	Changer dummy1 = pi.base.changeFontSet(standardFont());
661 	Changer dummy2 = pi.base.font.changeStyle(display() ? LM_ST_DISPLAY
662 	                                                    : LM_ST_TEXT);
663 
664 	int xmath = x;
665 	BufferParams::MathNumber const math_number = buffer().params().getMathNumber();
666 	if (numberedType() && math_number == BufferParams::LEFT) {
667 		Changer dummy = pi.base.changeFontSet("mathrm");
668 		int l = 0;
669 		for (row_type row = 0; row < nrows(); ++row)
670 			l = max(l, mathed_string_width(pi.base.font, nicelabel(row)));
671 
672 		if (l)
673 			// Value was hardcoded to 30 pixels
674 			xmath += Length(0.3, Length::IN).inPixels(pi.base) + l;
675 	}
676 
677 	InsetMathGrid::draw(pi, xmath + 1, y);
678 	drawMarkers(pi, x, y);
679 
680 	if (numberedType()) {
681 		Changer dummy = pi.base.changeFontSet("mathrm");
682 		for (row_type row = 0; row < nrows(); ++row) {
683 			int const yy = y + rowinfo_[row].offset_;
684 			docstring const nl = nicelabel(row);
685 			if (math_number == BufferParams::LEFT)
686 				pi.draw(x, yy, nl);
687 			else {
688 				int l = mathed_string_width(pi.base.font, nl);
689 				pi.draw(x + dim.wid - l, yy, nl);
690 			}
691 		}
692 	}
693 
694 	// drawing change line
695 	if (canPaintChange(*bv))
696 		pi.change_.paintCue(pi, x + 1, y + 1 - dim.asc,
697 		                    x + dim.wid, y + dim.des);
698 }
699 
700 
metricsT(TextMetricsInfo const & mi,Dimension & dim) const701 void InsetMathHull::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
702 {
703 	if (display()) {
704 		InsetMathGrid::metricsT(mi, dim);
705 	} else {
706 		odocstringstream os;
707 		otexrowstream ots(os);
708 		WriteStream wi(ots, false, true, WriteStream::wsDefault);
709 		write(wi);
710 		dim.wid = os.str().size();
711 		dim.asc = 1;
712 		dim.des = 0;
713 	}
714 }
715 
716 
drawT(TextPainter & pain,int x,int y) const717 void InsetMathHull::drawT(TextPainter & pain, int x, int y) const
718 {
719 	if (display()) {
720 		InsetMathGrid::drawT(pain, x, y);
721 	} else {
722 		odocstringstream os;
723 		otexrowstream ots(os);
724 		WriteStream wi(ots, false, true, WriteStream::wsDefault);
725 		write(wi);
726 		pain.draw(x, y, os.str().c_str());
727 	}
728 }
729 
730 
latexString(InsetMathHull const & inset)731 static docstring latexString(InsetMathHull const & inset)
732 {
733 	odocstringstream ls;
734 	// This has to be static, because a preview snippet or a math
735 	// macro containing math in text mode (such as $\text{$\phi$}$ or
736 	// \newcommand{\xxx}{\text{$\phi$}}) gets processed twice. The
737 	// first time as a whole, and the second time only the inner math.
738 	// In this last case inset.buffer() would be invalid.
739 	static Encoding const * encoding = 0;
740 	if (inset.isBufferValid())
741 		encoding = &(inset.buffer().params().encoding());
742 	otexrowstream ots(ls);
743 	WriteStream wi(ots, false, true, WriteStream::wsPreview, encoding);
744 	inset.write(wi);
745 	return ls.str();
746 }
747 
748 
initUnicodeMath() const749 void InsetMathHull::initUnicodeMath() const
750 {
751 	// Trigger classification of the unicode symbols in this inset
752 	docstring const dummy = latexString(*this);
753 }
754 
755 
addPreview(DocIterator const & inset_pos,graphics::PreviewLoader &) const756 void InsetMathHull::addPreview(DocIterator const & inset_pos,
757 	graphics::PreviewLoader & /*ploader*/) const
758 {
759 	if (RenderPreview::previewMath()) {
760 		preparePreview(inset_pos);
761 	}
762 }
763 
764 
usedMacros(MathData const & md,DocIterator const & pos,MacroNameSet & macros,MacroNameSet & defs) const765 void InsetMathHull::usedMacros(MathData const & md, DocIterator const & pos,
766                                MacroNameSet & macros, MacroNameSet & defs) const
767 {
768 	MacroNameSet::iterator const end = macros.end();
769 
770 	for (size_t i = 0; i < md.size(); ++i) {
771 		InsetMathMacro const * mi = md[i].nucleus()->asMacro();
772 		InsetMathMacroTemplate const * mt = md[i].nucleus()->asMacroTemplate();
773 		InsetMathScript const * si = md[i].nucleus()->asScriptInset();
774 		InsetMathFracBase const * fi = md[i].nucleus()->asFracBaseInset();
775 		InsetMathGrid const * gi = md[i].nucleus()->asGridInset();
776 		InsetMathNest const * ni = md[i].nucleus()->asNestInset();
777 		if (mi) {
778 			// Look for macros in the arguments of this macro.
779 			for (idx_type idx = 0; idx < mi->nargs(); ++idx)
780 				usedMacros(mi->cell(idx), pos, macros, defs);
781 			// Make sure this is a macro defined in the document
782 			// (as we also spot the macros in the symbols file)
783 			// or that we have not already accounted for it.
784 			docstring const name = mi->name();
785 			if (macros.find(name) == end)
786 				continue;
787 			macros.erase(name);
788 			// Look for macros in the definition of this macro.
789 			MathData ar(pos.buffer());
790 			MacroData const * data =
791 				pos.buffer()->getMacro(name, pos, true);
792 			if (data) {
793 				odocstringstream macro_def;
794 				data->write(macro_def, true);
795 				macro_def << endl;
796 				defs.insert(macro_def.str());
797 				asArray(data->definition(), ar);
798 			}
799 			usedMacros(ar, pos, macros, defs);
800 		} else if (mt) {
801 			MathData ar(pos.buffer());
802 			asArray(mt->definition(), ar);
803 			usedMacros(ar, pos, macros, defs);
804 		} else if (si) {
805 			if (!si->nuc().empty())
806 				usedMacros(si->nuc(), pos, macros, defs);
807 			if (si->hasDown())
808 				usedMacros(si->down(), pos, macros, defs);
809 			if (si->hasUp())
810 				usedMacros(si->up(), pos, macros, defs);
811 		} else if (fi || gi) {
812 			idx_type nidx = fi ? fi->nargs() : gi->nargs();
813 			for (idx_type idx = 0; idx < nidx; ++idx)
814 				usedMacros(fi ? fi->cell(idx) : gi->cell(idx),
815 				           pos, macros, defs);
816 		} else if (ni) {
817 			usedMacros(ni->cell(0), pos, macros, defs);
818 		}
819 	}
820 }
821 
822 
preparePreview(DocIterator const & pos,bool forexport) const823 void InsetMathHull::preparePreview(DocIterator const & pos,
824                                    bool forexport) const
825 {
826 	// there is no need to do all the macro stuff if we're not
827 	// actually going to generate the preview.
828 	if (!RenderPreview::previewMath() && !forexport)
829 		return;
830 
831 	Buffer const * buffer = pos.buffer();
832 
833 	// collect macros at this position
834 	MacroNameSet macros;
835 	buffer->listMacroNames(macros);
836 
837 	// collect definitions only for the macros used in this inset
838 	MacroNameSet defs;
839 	for (idx_type idx = 0; idx < nargs(); ++idx)
840 		usedMacros(cell(idx), pos, macros, defs);
841 
842 	MacroNameSet::iterator it = defs.begin();
843 	MacroNameSet::iterator end = defs.end();
844 	docstring macro_preamble;
845 	for (; it != end; ++it)
846 		macro_preamble.append(*it);
847 
848 	// set the font series and size for this snippet
849 	DocIterator dit = pos.getInnerText();
850 	Paragraph const & par = dit.paragraph();
851 	Font font = par.getFontSettings(buffer->params(), dit.pos());
852 	font.fontInfo().realize(par.layout().font);
853 	string const lsize = font.latexSize();
854 	docstring setfont;
855 	docstring endfont;
856 	if (font.fontInfo().series() == BOLD_SERIES) {
857 		setfont += from_ascii("\\textbf{");
858 		endfont += '}';
859 	}
860 	if (lsize != "normalsize" && !prefixIs(lsize, "error"))
861 		setfont += from_ascii("\\" + lsize + '\n');
862 
863 	docstring setcnt;
864 	if (forexport && haveNumbers()) {
865 		docstring eqstr = from_ascii("equation");
866 		CounterMap::const_iterator it = counter_map.find(eqstr);
867 		if (it != counter_map.end()) {
868 			int num = it->second;
869 			if (num >= 0)
870 				setcnt += from_ascii("\\setcounter{") + eqstr + '}' +
871 				          '{' + convert<docstring>(num) + '}' + '\n';
872 		}
873 		for (size_t i = 0; i != numcnts; ++i) {
874 			docstring cnt = from_ascii(counters_to_save[i]);
875 			it = counter_map.find(cnt);
876 			if (it == counter_map.end())
877 					continue;
878 			int num = it->second;
879 			if (num > 0)
880 				setcnt += from_ascii("\\setcounter{") + cnt + '}' +
881 				          '{' + convert<docstring>(num) + '}';
882 		}
883 	}
884 	docstring const snippet = macro_preamble + setfont + setcnt
885 	                          + latexString(*this) + endfont;
886 	LYXERR(Debug::MACROS, "Preview snippet: " << snippet);
887 	preview_->addPreview(snippet, *buffer, forexport);
888 }
889 
890 
reloadPreview(DocIterator const & pos) const891 void InsetMathHull::reloadPreview(DocIterator const & pos) const
892 {
893 	preparePreview(pos);
894 	preview_->startLoading(*pos.buffer());
895 }
896 
897 
loadPreview(DocIterator const & pos) const898 void InsetMathHull::loadPreview(DocIterator const & pos) const
899 {
900 	bool const forexport = true;
901 	preparePreview(pos, forexport);
902 	preview_->startLoading(*pos.buffer(), forexport);
903 }
904 
905 
notifyCursorLeaves(Cursor const & old,Cursor & cur)906 bool InsetMathHull::notifyCursorLeaves(Cursor const & old, Cursor & cur)
907 {
908 	if (RenderPreview::previewMath()) {
909 		reloadPreview(old);
910 		cur.screenUpdateFlags(Update::Force);
911 	}
912 	return false;
913 }
914 
915 
label(row_type row) const916 docstring InsetMathHull::label(row_type row) const
917 {
918 	LASSERT(row < nrows(), return docstring());
919 	if (InsetLabel * il = label_[row])
920 		return il->screenLabel();
921 	return docstring();
922 }
923 
924 
label(row_type row,docstring const & label)925 void InsetMathHull::label(row_type row, docstring const & label)
926 {
927 	//lyxerr << "setting label '" << label << "' for row " << row << endl;
928 	if (label_[row]) {
929 		if (label.empty()) {
930 			delete label_[row];
931 			label_[row] = dummy_pointer;
932 		} else {
933 			if (buffer_)
934 				label_[row]->updateLabelAndRefs(label);
935 			else
936 				label_[row]->setParam("name", label);
937 		}
938 		return;
939 	}
940 	InsetCommandParams p(LABEL_CODE);
941 	p["name"] = label;
942 	label_[row] = new InsetLabel(buffer_, p);
943 	if (buffer_)
944 		label_[row]->setBuffer(buffer());
945 }
946 
947 
numbered(row_type row,Numbered num)948 void InsetMathHull::numbered(row_type row, Numbered num)
949 {
950 	numbered_[row] = num;
951 	if (!numbered(row) && label_[row]) {
952 		delete label_[row];
953 		label_[row] = 0;
954 	}
955 }
956 
957 
numbered(row_type row) const958 bool InsetMathHull::numbered(row_type row) const
959 {
960 	return numbered_[row] == NUMBER;
961 }
962 
963 
ams() const964 bool InsetMathHull::ams() const
965 {
966 	switch (type_) {
967 	case hullAlign:
968 	case hullFlAlign:
969 	case hullMultline:
970 	case hullGather:
971 	case hullAlignAt:
972 	case hullXAlignAt:
973 	case hullXXAlignAt:
974 		return true;
975 	case hullUnknown:
976 	case hullRegexp:
977 		return false;
978 	case hullNone:
979 	case hullSimple:
980 	case hullEquation:
981 	case hullEqnArray:
982 		break;
983 	}
984 	for (size_t row = 0; row < numbered_.size(); ++row)
985 		if (numbered_[row] == NOTAG)
986 			return true;
987 	return false;
988 }
989 
990 
outerDisplay() const991 bool InsetMathHull::outerDisplay() const
992 {
993 	switch (type_) {
994 	case hullEquation:
995 	case hullEqnArray:
996 	case hullAlign:
997 	case hullFlAlign:
998 	case hullGather:
999 	case hullMultline:
1000 		return true;
1001 	case hullNone:
1002 	case hullSimple:
1003 	case hullAlignAt:
1004 	case hullXAlignAt:
1005 	case hullXXAlignAt:
1006 	case hullUnknown:
1007 	case hullRegexp:
1008 		break;
1009 	}
1010 	return false;
1011 }
1012 
1013 
display() const1014 Inset::DisplayType InsetMathHull::display() const
1015 {
1016 	switch (type_) {
1017 	case hullUnknown:
1018 	case hullSimple:
1019 	case hullNone:
1020 	case hullRegexp:
1021 		return Inline;
1022 	case hullEqnArray:
1023 	case hullAlign:
1024 	case hullFlAlign:
1025 	case hullAlignAt:
1026 	case hullXAlignAt:
1027 	case hullXXAlignAt:
1028 	case hullEquation:
1029 	case hullMultline:
1030 	case hullGather:
1031 		if (buffer().params().is_math_indent)
1032 			return AlignLeft;
1033 		else
1034 			return AlignCenter;
1035 	}
1036 	// avoid warning
1037 	return AlignCenter;
1038 }
1039 
1040 
indent(BufferView const & bv) const1041 int InsetMathHull::indent(BufferView const & bv) const
1042 {
1043 	// FIXME: set this in the textclass. This value is what the article class uses.
1044 	static Length default_indent(2.5, Length::EM);
1045 	if (display() != Inline && buffer().params().is_math_indent) {
1046 		Length const & len = buffer().params().getMathIndent();
1047 		if (len.empty())
1048 			return bv.inPixels(default_indent);
1049 		else
1050 			return bv.inPixels(len);
1051 	} else
1052 		return Inset::indent(bv);
1053 }
1054 
1055 
numberedType() const1056 bool InsetMathHull::numberedType() const
1057 {
1058 	switch (type_) {
1059 	case hullUnknown:
1060 	case hullNone:
1061 	case hullSimple:
1062 	case hullXXAlignAt:
1063 	case hullRegexp:
1064 		return false;
1065 	case hullEqnArray:
1066 	case hullAlign:
1067 	case hullFlAlign:
1068 	case hullAlignAt:
1069 	case hullXAlignAt:
1070 	case hullEquation:
1071 	case hullMultline:
1072 	case hullGather:
1073 		break;
1074 	}
1075 	for (row_type row = 0; row < nrows(); ++row)
1076 		if (numbered(row))
1077 			return true;
1078 	return false;
1079 }
1080 
1081 
validate(LaTeXFeatures & features) const1082 void InsetMathHull::validate(LaTeXFeatures & features) const
1083 {
1084 	if (features.runparams().isLaTeX()) {
1085 		if (ams())
1086 			features.require("amsmath");
1087 
1088 		if (type_ == hullRegexp) {
1089 			features.require("color");
1090 			docstring frcol = from_utf8(lcolor.getLaTeXName(Color_regexpframe));
1091 			docstring bgcol = from_ascii("white");
1092 			features.addPreambleSnippet(
1093 				"\\newcommand{\\regexp}[1]{\\fcolorbox{"
1094 				+ frcol + "}{"
1095 				+ bgcol + "}{\\ensuremath{\\mathtt{#1}}}}");
1096 			features.addPreambleSnippet(
1097 				from_ascii("\\newcommand{\\endregexp}{}"));
1098 		} else if (outerDisplay() && features.inDeletedInset()
1099 			   && !features.mustProvide("ct-dvipost")) {
1100 				features.require("ct-tikz-math-sout");
1101 		}
1102 
1103 		// Validation is necessary only if not using AMS math.
1104 		// To be safe, we will always run mathedvalidate.
1105 		//if (features.amsstyle)
1106 		//  return;
1107 
1108 		//features.binom      = true;
1109 	} else if (features.runparams().math_flavor == OutputParams::MathAsHTML) {
1110 		// it would be better to do this elsewhere, but we can't validate in
1111 		// InsetMathMatrix and we have no way, outside MathExtern, to know if
1112 		// we even have any matrices.
1113 				features.addCSSSnippet(
1114 					"table.matrix{display: inline-block; vertical-align: middle; text-align:center;}\n"
1115 					"table.matrix td{padding: 0.25px;}\n"
1116 					"td.ldelim{width: 0.5ex; border: thin solid black; border-right: none;}\n"
1117 					"td.rdelim{width: 0.5ex; border: thin solid black; border-left: none;}");
1118 	}
1119 	InsetMathGrid::validate(features);
1120 }
1121 
1122 
header_write(WriteStream & os) const1123 void InsetMathHull::header_write(WriteStream & os) const
1124 {
1125 	bool n = numberedType();
1126 
1127 	switch(type_) {
1128 	case hullNone:
1129 		break;
1130 
1131 	case hullSimple:
1132 		if (os.ulemCmd())
1133 			os << "\\mbox{";
1134 		os << '$';
1135 		os.startOuterRow();
1136 		if (cell(0).empty())
1137 			os << ' ';
1138 		break;
1139 
1140 	case hullEquation:
1141 		writeMathdisplayPreamble(os);
1142 		os << "\n";
1143 		os.startOuterRow();
1144 		if (n)
1145 			os << "\\begin{equation" << star(n) << "}\n";
1146 		else
1147 			os << "\\[\n";
1148 		break;
1149 
1150 	case hullEqnArray:
1151 	case hullAlign:
1152 	case hullFlAlign:
1153 	case hullGather:
1154 	case hullMultline:
1155 		writeMathdisplayPreamble(os);
1156 		os << "\n";
1157 		os.startOuterRow();
1158 		os << "\\begin{" << hullName(type_) << star(n) << "}\n";
1159 		break;
1160 
1161 	case hullAlignAt:
1162 	case hullXAlignAt:
1163 		os << "\n";
1164 		os.startOuterRow();
1165 		os << "\\begin{" << hullName(type_) << star(n) << '}'
1166 		  << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
1167 		break;
1168 
1169 	case hullXXAlignAt:
1170 		os << "\n";
1171 		os.startOuterRow();
1172 		os << "\\begin{" << hullName(type_) << '}'
1173 		  << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
1174 		break;
1175 
1176 	case hullRegexp:
1177 		os << "\\regexp{";
1178 		break;
1179 
1180 	case hullUnknown:
1181 		os << "\n";
1182 		os.startOuterRow();
1183 		os << "\\begin{unknown" << star(n) << "}\n";
1184 		break;
1185 	}
1186 }
1187 
1188 
footer_write(WriteStream & os) const1189 void InsetMathHull::footer_write(WriteStream & os) const
1190 {
1191 	bool n = numberedType();
1192 
1193 	switch(type_) {
1194 	case hullNone:
1195 		os << "\n";
1196 		break;
1197 
1198 	case hullSimple:
1199 		os << '$';
1200 		if (os.ulemCmd())
1201 			os << "}";
1202 		break;
1203 
1204 	case hullEquation:
1205 		os << "\n";
1206 		os.startOuterRow();
1207 		if (n)
1208 			os << "\\end{equation" << star(n) << "}\n";
1209 		else
1210 			os << "\\]\n";
1211 		writeMathdisplayPostamble(os);
1212 		break;
1213 
1214 	case hullEqnArray:
1215 	case hullAlign:
1216 	case hullFlAlign:
1217 	case hullGather:
1218 	case hullMultline:
1219 		os << "\n";
1220 		os.startOuterRow();
1221 		os << "\\end{" << hullName(type_) << star(n) << "}\n";
1222 		writeMathdisplayPostamble(os);
1223 		break;
1224 
1225 	case hullAlignAt:
1226 	case hullXAlignAt:
1227 		os << "\n";
1228 		os.startOuterRow();
1229 		os << "\\end{" << hullName(type_) << star(n) << "}\n";
1230 		break;
1231 
1232 	case hullXXAlignAt:
1233 		os << "\n";
1234 		os.startOuterRow();
1235 		os << "\\end{" << hullName(type_) << "}\n";
1236 		break;
1237 
1238 	case hullRegexp:
1239 		// Only used as a heuristic to find the regexp termination, when searching in ignore-format mode
1240 		os << "\\endregexp{}}";
1241 		break;
1242 
1243 	case hullUnknown:
1244 		os << "\n";
1245 		os.startOuterRow();
1246 		os << "\\end{unknown" << star(n) << "}\n";
1247 		break;
1248 	}
1249 }
1250 
1251 
allowsTabularFeatures() const1252 bool InsetMathHull::allowsTabularFeatures() const
1253 {
1254 	switch (type_) {
1255 	case hullEqnArray:
1256 	case hullAlign:
1257 	case hullAlignAt:
1258 	case hullXAlignAt:
1259 	case hullXXAlignAt:
1260 	case hullFlAlign:
1261 	case hullMultline:
1262 	case hullGather:
1263 		return true;
1264 	case hullNone:
1265 	case hullSimple:
1266 	case hullEquation:
1267 	case hullRegexp:
1268 	case hullUnknown:
1269 		break;
1270 	}
1271 	return false;
1272 }
1273 
1274 
rowChangeOK() const1275 bool InsetMathHull::rowChangeOK() const
1276 {
1277 	return
1278 		type_ == hullEqnArray || type_ == hullAlign ||
1279 		type_ == hullFlAlign || type_ == hullAlignAt ||
1280 		type_ == hullXAlignAt || type_ == hullXXAlignAt ||
1281 		type_ == hullGather || type_ == hullMultline;
1282 }
1283 
1284 
colChangeOK() const1285 bool InsetMathHull::colChangeOK() const
1286 {
1287 	return
1288 		type_ == hullAlign || type_ == hullFlAlign ||type_ == hullAlignAt ||
1289 		type_ == hullXAlignAt || type_ == hullXXAlignAt;
1290 }
1291 
1292 
addRow(row_type row)1293 void InsetMathHull::addRow(row_type row)
1294 {
1295 	if (!rowChangeOK())
1296 		return;
1297 
1298 	bool numbered = numberedType();
1299 	// Move the number and raw pointer, do not call label() (bug 7511)
1300 	InsetLabel * label = dummy_pointer;
1301 	docstring number = empty_docstring();
1302 	if (type_ == hullMultline) {
1303 		if (row + 1 == nrows())  {
1304 			numbered_[row] = NONUMBER;
1305 			swap(label, label_[row]);
1306 			swap(number, numbers_[row]);
1307 		} else
1308 			numbered = false;
1309 	}
1310 
1311 	numbered_.insert(numbered_.begin() + row + 1, numbered ? NUMBER : NONUMBER);
1312 	numbers_.insert(numbers_.begin() + row + 1, number);
1313 	label_.insert(label_.begin() + row + 1, label);
1314 	InsetMathGrid::addRow(row);
1315 }
1316 
1317 
swapRow(row_type row)1318 void InsetMathHull::swapRow(row_type row)
1319 {
1320 	if (nrows() <= 1)
1321 		return;
1322 	if (row + 1 == nrows())
1323 		--row;
1324 	swap(numbered_[row], numbered_[row + 1]);
1325 	swap(numbers_[row], numbers_[row + 1]);
1326 	swap(label_[row], label_[row + 1]);
1327 	InsetMathGrid::swapRow(row);
1328 }
1329 
1330 
delRow(row_type row)1331 void InsetMathHull::delRow(row_type row)
1332 {
1333 	if (nrows() <= 1 || !rowChangeOK())
1334 		return;
1335 	if (row + 1 == nrows() && type_ == hullMultline) {
1336 		swap(numbered_[row - 1], numbered_[row]);
1337 		swap(numbers_[row - 1], numbers_[row]);
1338 		swap(label_[row - 1], label_[row]);
1339 		InsetMathGrid::delRow(row);
1340 		return;
1341 	}
1342 	InsetMathGrid::delRow(row);
1343 	// The last dummy row has no number info nor a label.
1344 	// Test nrows() + 1 because we have already erased the row.
1345 	if (row == nrows() + 1)
1346 		row--;
1347 	numbered_.erase(numbered_.begin() + row);
1348 	numbers_.erase(numbers_.begin() + row);
1349 	delete label_[row];
1350 	label_.erase(label_.begin() + row);
1351 }
1352 
1353 
addCol(col_type col)1354 void InsetMathHull::addCol(col_type col)
1355 {
1356 	if (!colChangeOK())
1357 		return;
1358 	InsetMathGrid::addCol(col);
1359 }
1360 
1361 
delCol(col_type col)1362 void InsetMathHull::delCol(col_type col)
1363 {
1364 	if (ncols() <= 1 || !colChangeOK())
1365 		return;
1366 	InsetMathGrid::delCol(col);
1367 }
1368 
1369 
nicelabel(row_type row) const1370 docstring InsetMathHull::nicelabel(row_type row) const
1371 {
1372 	if (!numbered(row))
1373 		return docstring();
1374 	docstring const & val = numbers_[row];
1375 	if (!label_[row])
1376 		return '(' + val + ')';
1377 	return '(' + val + ',' + label_[row]->screenLabel() + ')';
1378 }
1379 
1380 
glueall(HullType type)1381 void InsetMathHull::glueall(HullType type)
1382 {
1383 	MathData ar;
1384 	for (idx_type i = 0; i < nargs(); ++i)
1385 		ar.append(cell(i));
1386 	InsetLabel * label = 0;
1387 	if (type == hullEquation) {
1388 		// preserve first non-empty label
1389 		for (row_type row = 0; row < nrows(); ++row) {
1390 			if (label_[row]) {
1391 				label = label_[row];
1392 				label_[row] = 0;
1393 				break;
1394 			}
1395 		}
1396 	}
1397 	*this = InsetMathHull(buffer_, hullSimple);
1398 	label_[0] = label;
1399 	cell(0) = ar;
1400 	setDefaults();
1401 }
1402 
1403 
splitTo2Cols()1404 void InsetMathHull::splitTo2Cols()
1405 {
1406 	LASSERT(ncols() == 1, return);
1407 	InsetMathGrid::addCol(1);
1408 	for (row_type row = 0; row < nrows(); ++row) {
1409 		idx_type const i = 2 * row;
1410 		pos_type pos = firstRelOp(cell(i));
1411 		cell(i + 1) = MathData(buffer_, cell(i).begin() + pos, cell(i).end());
1412 		cell(i).erase(pos, cell(i).size());
1413 	}
1414 }
1415 
1416 
splitTo3Cols()1417 void InsetMathHull::splitTo3Cols()
1418 {
1419 	LASSERT(ncols() < 3, return);
1420 	if (ncols() < 2)
1421 		splitTo2Cols();
1422 	InsetMathGrid::addCol(2);
1423 	for (row_type row = 0; row < nrows(); ++row) {
1424 		idx_type const i = 3 * row + 1;
1425 		if (!cell(i).empty()) {
1426 			cell(i + 1) = MathData(buffer_, cell(i).begin() + 1, cell(i).end());
1427 			cell(i).erase(1, cell(i).size());
1428 		}
1429 	}
1430 }
1431 
1432 
changeCols(col_type cols)1433 void InsetMathHull::changeCols(col_type cols)
1434 {
1435 	if (ncols() == cols)
1436 		return;
1437 	else if (ncols() < cols) {
1438 		// split columns
1439 		if (cols < 3)
1440 			splitTo2Cols();
1441 		else {
1442 			splitTo3Cols();
1443 			while (ncols() < cols)
1444 				InsetMathGrid::addCol(ncols());
1445 		}
1446 		return;
1447 	}
1448 
1449 	// combine columns
1450 	for (row_type row = 0; row < nrows(); ++row) {
1451 		idx_type const i = row * ncols();
1452 		for (col_type col = cols; col < ncols(); ++col) {
1453 			cell(i + cols - 1).append(cell(i + col));
1454 		}
1455 	}
1456 	// delete columns
1457 	while (ncols() > cols) {
1458 		InsetMathGrid::delCol(ncols() - 1);
1459 	}
1460 }
1461 
1462 
getType() const1463 HullType InsetMathHull::getType() const
1464 {
1465 	return type_;
1466 }
1467 
1468 
setType(HullType type)1469 void InsetMathHull::setType(HullType type)
1470 {
1471 	type_ = type;
1472 	setDefaults();
1473 }
1474 
1475 
isMutable(HullType type)1476 bool InsetMathHull::isMutable(HullType type)
1477 {
1478 	switch (type) {
1479 	case hullNone:
1480 	case hullSimple:
1481 	case hullEquation:
1482 	case hullEqnArray:
1483 	case hullAlign:
1484 	case hullFlAlign:
1485 	case hullAlignAt:
1486 	case hullXAlignAt:
1487 	case hullXXAlignAt:
1488 	case hullMultline:
1489 	case hullGather:
1490 		return true;
1491 	case hullUnknown:
1492 	case hullRegexp:
1493 		return false;
1494 	}
1495 	// avoid warning
1496 	return false;
1497 }
1498 
1499 
mutate(HullType newtype)1500 void InsetMathHull::mutate(HullType newtype)
1501 {
1502 	//lyxerr << "mutating from '" << type_ << "' to '" << newtype << "'" << endl;
1503 
1504   	if (newtype == type_)
1505 		return;
1506 
1507 	// This guards the algorithm below it, which is designed with certain types
1508 	// in mind.
1509 	if (!isMutable(newtype) || !isMutable(type_)) {
1510 		lyxerr << "mutation from '" << to_utf8(hullName(type_))
1511 		       << "' to '" << to_utf8(hullName(newtype))
1512 		       << "' not implemented" << endl;
1513 		return;
1514 	}
1515 
1516 	// we try to move along the chain
1517 	// none <-> simple <-> equation <-> eqnarray -> *align* -> multline, gather -+
1518 	//                                     ^                                     |
1519 	//                                     +-------------------------------------+
1520 	// we use eqnarray as intermediate type for mutations that are not
1521 	// directly supported because it handles labels and numbering for
1522 	// "down mutation".
1523 
1524 	switch (type_) {
1525 	case hullNone:
1526 		setType(hullSimple);
1527 		numbered(0, false);
1528 		mutate(newtype);
1529 		break;
1530 
1531 	case hullSimple:
1532 		if (newtype == hullNone) {
1533 			setType(hullNone);
1534 			numbered(0, false);
1535 		} else {
1536 			setType(hullEquation);
1537 			numbered(0, label_[0] ? true : false);
1538 			mutate(newtype);
1539 		}
1540 		break;
1541 
1542 	case hullEquation:
1543 		switch (newtype) {
1544 		case hullNone:
1545 		case hullSimple:
1546 			setType(hullSimple);
1547 			numbered(0, false);
1548 			mutate(newtype);
1549 			break;
1550 		case hullEqnArray:
1551 			// split it "nicely" on the first relop
1552 			splitTo3Cols();
1553 			setType(hullEqnArray);
1554 			break;
1555 		case hullMultline:
1556 		case hullGather:
1557 			setType(newtype);
1558 			break;
1559 		default:
1560 			// *align*
1561 			// split it "nicely"
1562 			splitTo2Cols();
1563 			setType(hullAlign);
1564 			mutate(newtype);
1565 			break;
1566 		}
1567 		break;
1568 
1569 	case hullEqnArray:
1570 		switch (newtype) {
1571 		case hullNone:
1572 		case hullSimple:
1573 		case hullEquation:
1574 			glueall(newtype);
1575 			mutate(newtype);
1576 			break;
1577 		default:
1578 			// align & Co.
1579 			changeCols(2);
1580 			setType(hullAlign);
1581 			mutate(newtype);
1582 			break;
1583 		}
1584 		break;
1585 
1586 	case hullAlign:
1587 	case hullAlignAt:
1588 	case hullXAlignAt:
1589 	case hullFlAlign:
1590 		switch (newtype) {
1591 		case hullNone:
1592 		case hullSimple:
1593 		case hullEquation:
1594 		case hullEqnArray:
1595 			changeCols(3);
1596 			setType(hullEqnArray);
1597 			mutate(newtype);
1598 			break;
1599 		case hullGather:
1600 		case hullMultline:
1601 			changeCols(1);
1602 			setType(newtype);
1603 			break;
1604 		case hullXXAlignAt:
1605 			for (row_type row = 0; row < nrows(); ++row)
1606 				numbered(row, false);
1607 			setType(newtype);
1608 			break;
1609 		default:
1610 			setType(newtype);
1611 			break;
1612 		}
1613 		break;
1614 
1615 	case hullXXAlignAt:
1616 		for (row_type row = 0; row < nrows(); ++row)
1617 			numbered(row, false);
1618 		switch (newtype) {
1619 		case hullNone:
1620 		case hullSimple:
1621 		case hullEquation:
1622 		case hullEqnArray:
1623 			changeCols(3);
1624 			setType(hullEqnArray);
1625 			mutate(newtype);
1626 			break;
1627 		case hullGather:
1628 		case hullMultline:
1629 			changeCols(1);
1630 			setType(newtype);
1631 			break;
1632 		default:
1633 			setType(newtype);
1634 			break;
1635 		}
1636 		break;
1637 
1638 	case hullMultline:
1639 	case hullGather:
1640 		switch (newtype) {
1641 		case hullGather:
1642 		case hullMultline:
1643 			setType(newtype);
1644 			break;
1645 		case hullAlign:
1646 		case hullFlAlign:
1647 		case hullAlignAt:
1648 		case hullXAlignAt:
1649 			splitTo2Cols();
1650 			setType(newtype);
1651 			break;
1652 		case hullXXAlignAt:
1653 			splitTo2Cols();
1654 			for (row_type row = 0; row < nrows(); ++row)
1655 				numbered(row, false);
1656 			setType(newtype);
1657 			break;
1658 		default:
1659 			// first we mutate to EqnArray
1660 			splitTo3Cols();
1661 			setType(hullEqnArray);
1662 			mutate(newtype);
1663 			break;
1664 		}
1665 		break;
1666 
1667 	default:
1668 		// we passed the guard so we should not be here
1669 		LASSERT("Mutation not implemented, but should have been.", return);
1670 		break;
1671 	}// switch
1672 }
1673 
1674 
eolString(row_type row,bool fragile,bool latex,bool last_eoln) const1675 docstring InsetMathHull::eolString(row_type row, bool fragile, bool latex,
1676 		bool last_eoln) const
1677 {
1678 	docstring res;
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 + '}';
1685 		}
1686 		if (type_ != hullMultline) {
1687 			if (numbered_[row]  == NONUMBER)
1688 				res += "\\nonumber ";
1689 			else if (numbered_[row]  == NOTAG)
1690 				res += "\\notag ";
1691 		}
1692 	}
1693 	// Never add \\ on the last empty line of eqnarray and friends
1694 	last_eoln = false;
1695 	return res + InsetMathGrid::eolString(row, fragile, latex, last_eoln);
1696 }
1697 
write(WriteStream & os) const1698 void InsetMathHull::write(WriteStream & os) const
1699 {
1700 	ModeSpecifier specifier(os, MATH_MODE);
1701 	header_write(os);
1702 	InsetMathGrid::write(os);
1703 	footer_write(os);
1704 }
1705 
1706 
normalize(NormalStream & os) const1707 void InsetMathHull::normalize(NormalStream & os) const
1708 {
1709 	os << "[formula " << hullName(type_) << ' ';
1710 	InsetMathGrid::normalize(os);
1711 	os << "] ";
1712 }
1713 
1714 
infoize(odocstream & os) const1715 void InsetMathHull::infoize(odocstream & os) const
1716 {
1717 	os << bformat(_("Type: %1$s"), hullName(type_));
1718 }
1719 
1720 
check() const1721 void InsetMathHull::check() const
1722 {
1723 	LATTEST(numbered_.size() == nrows());
1724 	LATTEST(numbers_.size() == nrows());
1725 	LATTEST(label_.size() == nrows());
1726 }
1727 
1728 
doExtern(Cursor & cur,FuncRequest & func)1729 void InsetMathHull::doExtern(Cursor & cur, FuncRequest & func)
1730 {
1731 	docstring dlang;
1732 	docstring extra;
1733 	idocstringstream iss(func.argument());
1734 	iss >> dlang >> extra;
1735 	if (extra.empty())
1736 		extra = from_ascii("noextra");
1737 	string const lang = to_ascii(dlang);
1738 
1739 	// replace selection with result of computation
1740 	if (reduceSelectionToOneCell(cur)) {
1741 		MathData ar;
1742 		asArray(grabAndEraseSelection(cur), ar);
1743 		lyxerr << "use selection: " << ar << endl;
1744 		cur.insert(pipeThroughExtern(lang, extra, ar));
1745 		return;
1746 	}
1747 
1748 	// only inline, display or eqnarray math is allowed
1749 	switch (getType()) {
1750 	case hullSimple:
1751 	case hullEquation:
1752 	case hullEqnArray:
1753 		break;
1754 	default:
1755 		frontend::Alert::warning(_("Bad math environment"),
1756 				_("Computation cannot be performed for AMS "
1757 				  "math environments.\nChange the math "
1758 				  "formula type and try again."));
1759 		return;
1760 	}
1761 
1762 	MathData eq;
1763 	eq.push_back(MathAtom(new InsetMathChar('=')));
1764 
1765 	// go to first item in line
1766 	cur.idx() -= cur.idx() % ncols();
1767 	cur.pos() = 0;
1768 
1769 	if (getType() == hullSimple) {
1770 		size_type pos = cur.cell().find_last(eq);
1771 		MathData ar;
1772 		if (pos == cur.cell().size()) {
1773 			ar = cur.cell();
1774 			lyxerr << "use whole cell: " << ar << endl;
1775 		} else {
1776 			ar = MathData(buffer_, cur.cell().begin() + pos + 1, cur.cell().end());
1777 			lyxerr << "use partial cell form pos: " << pos << endl;
1778 		}
1779 		cur.cell().append(eq);
1780 		cur.cell().append(pipeThroughExtern(lang, extra, ar));
1781 		cur.pos() = cur.lastpos();
1782 		return;
1783 	}
1784 
1785 	if (getType() == hullEquation) {
1786 		lyxerr << "use equation inset" << endl;
1787 		mutate(hullEqnArray);
1788 		MathData & ar = cur.cell();
1789 		lyxerr << "use cell: " << ar << endl;
1790 		++cur.idx();
1791 		cur.cell() = eq;
1792 		++cur.idx();
1793 		cur.cell() = pipeThroughExtern(lang, extra, ar);
1794 		// move to end of line
1795 		cur.pos() = cur.lastpos();
1796 		return;
1797 	}
1798 
1799 	{
1800 		lyxerr << "use eqnarray" << endl;
1801 		cur.idx() += 2 - cur.idx() % ncols();
1802 		cur.pos() = 0;
1803 		MathData ar = cur.cell();
1804 		lyxerr << "use cell: " << ar << endl;
1805 		// FIXME: temporarily disabled
1806 		addRow(cur.row());
1807 		++cur.idx();
1808 		++cur.idx();
1809 		cur.cell() = eq;
1810 		++cur.idx();
1811 		cur.cell() = pipeThroughExtern(lang, extra, ar);
1812 		cur.pos() = cur.lastpos();
1813 	}
1814 }
1815 
1816 
doDispatch(Cursor & cur,FuncRequest & cmd)1817 void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd)
1818 {
1819 	//lyxerr << "action: " << cmd.action() << endl;
1820 	switch (cmd.action()) {
1821 
1822 	case LFUN_FINISHED_BACKWARD:
1823 	case LFUN_FINISHED_FORWARD:
1824 	case LFUN_FINISHED_RIGHT:
1825 	case LFUN_FINISHED_LEFT:
1826 		//lyxerr << "action: " << cmd.action() << endl;
1827 		InsetMathGrid::doDispatch(cur, cmd);
1828 		break;
1829 
1830 	case LFUN_PARAGRAPH_BREAK:
1831 		// just swallow this
1832 		break;
1833 
1834 	case LFUN_NEWLINE_INSERT:
1835 		// some magic for the common case
1836 		if (type_ == hullSimple || type_ == hullEquation) {
1837 			cur.recordUndoInset();
1838 			bool const align =
1839 				cur.bv().buffer().params().use_package("amsmath") != BufferParams::package_off;
1840 			mutate(align ? hullAlign : hullEqnArray);
1841 			// mutate() may change labels and such.
1842 			cur.forceBufferUpdate();
1843 			cur.idx() = nrows() * ncols() - 1;
1844 			cur.pos() = cur.lastpos();
1845 		}
1846 		InsetMathGrid::doDispatch(cur, cmd);
1847 		break;
1848 
1849 	case LFUN_MATH_NUMBER_TOGGLE: {
1850 		//lyxerr << "toggling all numbers" << endl;
1851 		cur.recordUndoInset();
1852 		bool old = numberedType();
1853 		if (type_ == hullMultline)
1854 			numbered(nrows() - 1, !old);
1855 		else
1856 			for (row_type row = 0; row < nrows(); ++row)
1857 				numbered(row, !old);
1858 
1859 		cur.message(old ? _("No number") : _("Number"));
1860 		cur.forceBufferUpdate();
1861 		break;
1862 	}
1863 
1864 	case LFUN_MATH_NUMBER_LINE_TOGGLE: {
1865 		cur.recordUndoInset();
1866 		row_type r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1867 		bool old = numbered(r);
1868 		cur.message(old ? _("No number") : _("Number"));
1869 		numbered(r, !old);
1870 		cur.forceBufferUpdate();
1871 		break;
1872 	}
1873 
1874 	case LFUN_LABEL_INSERT: {
1875 		row_type r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1876 		docstring old_label = label(r);
1877 		docstring const default_label = from_ascii("eq:");
1878 		if (old_label.empty())
1879 			old_label = default_label;
1880 
1881 		InsetCommandParams p(LABEL_CODE);
1882 		p["name"] = cmd.argument().empty() ? old_label : cmd.argument();
1883 		string const data = InsetCommand::params2string(p);
1884 
1885 		if (cmd.argument().empty())
1886 			cur.bv().showDialog("label", data);
1887 		else {
1888 			FuncRequest fr(LFUN_INSET_INSERT, data);
1889 			dispatch(cur, fr);
1890 		}
1891 		break;
1892 	}
1893 
1894 	case LFUN_LABEL_COPY_AS_REFERENCE: {
1895 		row_type row;
1896 		if (cmd.argument().empty() && &cur.inset() == this)
1897 			// if there is no argument and we're inside math, we retrieve
1898 			// the row number from the cursor position.
1899 			row = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1900 		else {
1901 			// if there is an argument, find the corresponding label, else
1902 			// check whether there is at least one label.
1903 			for (row = 0; row != nrows(); ++row)
1904 				if (numbered(row) && label_[row]
1905 					  && (cmd.argument().empty() || label(row) == cmd.argument()))
1906 					break;
1907 		}
1908 
1909 		if (row == nrows())
1910 			break;
1911 
1912 		InsetCommandParams p(REF_CODE, "ref");
1913 		p["reference"] = label(row);
1914 		cap::clearSelection();
1915 		cap::copyInset(cur, new InsetRef(buffer_, p), label(row));
1916 		break;
1917 	}
1918 
1919 	case LFUN_WORD_DELETE_FORWARD:
1920 	case LFUN_CHAR_DELETE_FORWARD:
1921 		if (col(cur.idx()) + 1 == ncols()
1922 		    && cur.pos() == cur.lastpos()
1923 		    && !cur.selection()) {
1924 			if (!label(row(cur.idx())).empty()) {
1925 				cur.recordUndoInset();
1926 				label(row(cur.idx()), docstring());
1927 			} else if (numbered(row(cur.idx()))) {
1928 				cur.recordUndoInset();
1929 				numbered(row(cur.idx()), false);
1930 				cur.forceBufferUpdate();
1931 			} else {
1932 				InsetMathGrid::doDispatch(cur, cmd);
1933 				return;
1934 			}
1935 		} else {
1936 			InsetMathGrid::doDispatch(cur, cmd);
1937 			return;
1938 		}
1939 		break;
1940 
1941 	case LFUN_INSET_INSERT: {
1942 		//lyxerr << "arg: " << to_utf8(cmd.argument()) << endl;
1943 		// FIXME: this should be cleaned up to use InsetLabel methods directly.
1944 		string const name = cmd.getArg(0);
1945 		if (name == "label") {
1946 			InsetCommandParams p(LABEL_CODE);
1947 			InsetCommand::string2params(to_utf8(cmd.argument()), p);
1948 			docstring str = p["name"];
1949 			cur.recordUndoInset();
1950 			row_type const r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1951 			str = trim(str);
1952 			if (!str.empty())
1953 				numbered(r, true);
1954 			docstring old = label(r);
1955 			if (str != old) {
1956 				if (label_[r])
1957 					// The label will take care of the reference update.
1958 					label(r, str);
1959 				else {
1960 					label(r, str);
1961 					// Newly created inset so initialize it.
1962 					label_[r]->initView();
1963 				}
1964 			}
1965 			cur.forceBufferUpdate();
1966 			break;
1967 		}
1968 		InsetMathGrid::doDispatch(cur, cmd);
1969 		return;
1970 	}
1971 
1972 	case LFUN_MATH_EXTERN:
1973 		cur.recordUndoInset();
1974 		doExtern(cur, cmd);
1975 		break;
1976 
1977 	case LFUN_MATH_MUTATE: {
1978 		cur.recordUndoInset();
1979 		row_type row = cur.row();
1980 		col_type col = cur.col();
1981 		mutate(hullType(cmd.argument()));
1982 		cur.idx() = row * ncols() + col;
1983 		if (cur.idx() > cur.lastidx()) {
1984 			cur.idx() = cur.lastidx();
1985 			cur.pos() = cur.lastpos();
1986 		}
1987 		if (cur.pos() > cur.lastpos())
1988 			cur.pos() = cur.lastpos();
1989 
1990 		cur.forceBufferUpdate();
1991 		// FIXME: find some more clever handling of the selection,
1992 		// i.e. preserve it.
1993 		cur.clearSelection();
1994 		//cur.dispatched(FINISHED);
1995 		break;
1996 	}
1997 
1998 	case LFUN_MATH_DISPLAY: {
1999 		cur.recordUndoInset();
2000 		mutate(type_ == hullSimple ? hullEquation : hullSimple);
2001 		// if the cursor is in a cell that got merged, move it to
2002 		// start of the hull inset.
2003 		if (cur.idx() > 0) {
2004 			cur.idx() = 0;
2005 			cur.pos() = 0;
2006 		}
2007 		if (cur.pos() > cur.lastpos())
2008 			cur.pos() = cur.lastpos();
2009 
2010 		break;
2011 	}
2012 
2013 	case LFUN_TABULAR_FEATURE:
2014 		if (!allowsTabularFeatures())
2015 			cur.undispatched();
2016 		else
2017 			InsetMathGrid::doDispatch(cur, cmd);
2018 		break;
2019 
2020 	default:
2021 		InsetMathGrid::doDispatch(cur, cmd);
2022 		break;
2023 	}
2024 }
2025 
2026 
2027 namespace {
2028 
allowDisplayMath(Cursor const & cur)2029 bool allowDisplayMath(Cursor const & cur)
2030 {
2031 	LATTEST(cur.depth() > 1);
2032 	Cursor tmpcur = cur;
2033 	tmpcur.pop();
2034 	FuncStatus status;
2035 	FuncRequest cmd(LFUN_MATH_DISPLAY);
2036 	return tmpcur.getStatus(cmd, status) && status.enabled();
2037 }
2038 
2039 } // namespace
2040 
2041 
getStatus(Cursor & cur,FuncRequest const & cmd,FuncStatus & status) const2042 bool InsetMathHull::getStatus(Cursor & cur, FuncRequest const & cmd,
2043 		FuncStatus & status) const
2044 {
2045 	switch (cmd.action()) {
2046 	case LFUN_FINISHED_BACKWARD:
2047 	case LFUN_FINISHED_FORWARD:
2048 	case LFUN_FINISHED_RIGHT:
2049 	case LFUN_FINISHED_LEFT:
2050 	case LFUN_UP:
2051 	case LFUN_DOWN:
2052 	case LFUN_NEWLINE_INSERT:
2053 	case LFUN_MATH_EXTERN:
2054 		// we handle these
2055 		status.setEnabled(true);
2056 		return true;
2057 
2058 	// we never allow this in math, and we want to bind enter
2059 	// to another actions in command-alternatives
2060 	case LFUN_PARAGRAPH_BREAK:
2061 		status.setEnabled(false);
2062 		return true;
2063 	case LFUN_MATH_MUTATE: {
2064 		HullType const ht = hullType(cmd.argument());
2065 		status.setOnOff(type_ == ht);
2066 		status.setEnabled(isMutable(ht) && isMutable(type_));
2067 
2068 		if (ht != hullSimple && status.enabled())
2069 			status.setEnabled(allowDisplayMath(cur));
2070 		return true;
2071 	}
2072 	case LFUN_MATH_DISPLAY: {
2073 		status.setEnabled(display() != Inline || allowDisplayMath(cur));
2074 		status.setOnOff(display() != Inline);
2075 		return true;
2076 	}
2077 
2078 	case LFUN_MATH_NUMBER_TOGGLE:
2079 		// FIXME: what is the right test, this or the one of
2080 		// LABEL_INSERT?
2081 		status.setEnabled(display() != Inline);
2082 		status.setOnOff(numberedType());
2083 		return true;
2084 
2085 	case LFUN_MATH_NUMBER_LINE_TOGGLE: {
2086 		// FIXME: what is the right test, this or the one of
2087 		// LABEL_INSERT?
2088 		bool const enable = (type_ == hullMultline)
2089 			? (nrows() - 1 == cur.row())
2090 			: display() != Inline;
2091 		row_type const r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
2092 		status.setEnabled(enable);
2093 		status.setOnOff(enable && numbered(r));
2094 		return true;
2095 	}
2096 
2097 	case LFUN_LABEL_INSERT:
2098 		status.setEnabled(type_ != hullSimple);
2099 		return true;
2100 
2101 	case LFUN_LABEL_COPY_AS_REFERENCE: {
2102 		bool enabled = false;
2103 		row_type row;
2104 		if (cmd.argument().empty() && &cur.inset() == this) {
2105 			// if there is no argument and we're inside math, we retrieve
2106 			// the row number from the cursor position.
2107 			row = (type_ == hullMultline) ? nrows() - 1 : cur.row();
2108 			enabled = numberedType() && label_[row] && numbered(row);
2109 		} else {
2110 			// if there is an argument, find the corresponding label, else
2111 			// check whether there is at least one label.
2112 			for (row_type row = 0; row != nrows(); ++row) {
2113 				if (numbered(row) && label_[row] &&
2114 					(cmd.argument().empty() || label(row) == cmd.argument())) {
2115 						enabled = true;
2116 						break;
2117 				}
2118 			}
2119 		}
2120 		status.setEnabled(enabled);
2121 		return true;
2122 	}
2123 
2124 	case LFUN_INSET_INSERT:
2125 		if (cmd.getArg(0) == "label") {
2126 			status.setEnabled(type_ != hullSimple);
2127 			return true;
2128 		}
2129 		return InsetMathGrid::getStatus(cur, cmd, status);
2130 
2131 	case LFUN_TABULAR_FEATURE: {
2132 		if (!allowsTabularFeatures())
2133 			return false;
2134 		string s = cmd.getArg(0);
2135 		if (!rowChangeOK()
2136 		    && (s == "append-row"
2137 			|| s == "delete-row"
2138 			|| s == "copy-row")) {
2139 			status.message(bformat(
2140 				from_utf8(N_("Can't change number of rows in '%1$s'")),
2141 				hullName(type_)));
2142 			status.setEnabled(false);
2143 			return true;
2144 		}
2145 		if (!colChangeOK()
2146 		    && (s == "append-column"
2147 			|| s == "delete-column"
2148 			|| s == "copy-column")) {
2149 			status.message(bformat(
2150 				from_utf8(N_("Can't change number of columns in '%1$s'")),
2151 				hullName(type_)));
2152 			status.setEnabled(false);
2153 			return true;
2154 		}
2155 		if (s == "add-vline-left" || s == "add-vline-right") {
2156 			status.message(bformat(
2157 				from_utf8(N_("Can't add vertical grid lines in '%1$s'")),
2158 				hullName(type_)));
2159 			status.setEnabled(false);
2160 			return true;
2161 		}
2162 		if (s == "valign-top" || s == "valign-middle"
2163 		 || s == "valign-bottom" || s == "align-left"
2164 		 || s == "align-center" || s == "align-right") {
2165 			status.setEnabled(false);
2166 			return true;
2167 		}
2168 		return InsetMathGrid::getStatus(cur, cmd, status);
2169 	}
2170 
2171 	default:
2172 		return InsetMathGrid::getStatus(cur, cmd, status);
2173 	}
2174 }
2175 
2176 
leftMargin() const2177 int InsetMathHull::leftMargin() const
2178 {
2179 	return (getType() == hullSimple) ? 0 : InsetMathGrid::leftMargin();
2180 }
2181 
2182 
rightMargin() const2183 int InsetMathHull::rightMargin() const
2184 {
2185 	return (getType() == hullSimple) ? 0 : InsetMathGrid::rightMargin();
2186 }
2187 
2188 
border() const2189 int InsetMathHull::border() const
2190 {
2191 	return (getType() == hullSimple) ? 0 : InsetMathGrid::border();
2192 }
2193 
2194 
2195 /////////////////////////////////////////////////////////////////////
2196 
2197 
2198 
2199 // simply scrap this function if you want
mutateToText()2200 void InsetMathHull::mutateToText()
2201 {
2202 #if 0
2203 	// translate to latex
2204 	ostringstream os;
2205 	latex(os, false, false);
2206 	string str = os.str();
2207 
2208 	// insert this text
2209 	Text * lt = view_->cursor().innerText();
2210 	string::const_iterator cit = str.begin();
2211 	string::const_iterator end = str.end();
2212 	for (; cit != end; ++cit)
2213 		view_->getIntl()->getTransManager().TranslateAndInsert(*cit, lt);
2214 
2215 	// remove ourselves
2216 	//dispatch(LFUN_ESCAPE);
2217 #endif
2218 }
2219 
2220 
handleFont(Cursor & cur,docstring const & arg,docstring const & font)2221 void InsetMathHull::handleFont(Cursor & cur, docstring const & arg,
2222 	docstring const & font)
2223 {
2224 	// this whole function is a hack and won't work for incremental font
2225 	// changes...
2226 	cur.recordUndo();
2227 	if (cur.inset().asInsetMath()->name() == font)
2228 		cur.handleFont(to_utf8(font));
2229 	else {
2230 		cur.handleNest(createInsetMath(font, cur.buffer()));
2231 		cur.insert(arg);
2232 	}
2233 }
2234 
2235 
handleFont2(Cursor & cur,docstring const & arg)2236 void InsetMathHull::handleFont2(Cursor & cur, docstring const & arg)
2237 {
2238 	cur.recordUndo();
2239 	Font font;
2240 	bool b;
2241 	font.fromString(to_utf8(arg), b);
2242 	if (font.fontInfo().color() != Color_inherit) {
2243 		MathAtom at = MathAtom(new InsetMathColor(buffer_, true, font.fontInfo().color()));
2244 		cur.handleNest(at, 0);
2245 	}
2246 }
2247 
2248 
edit(Cursor & cur,bool front,EntryDirection entry_from)2249 void InsetMathHull::edit(Cursor & cur, bool front, EntryDirection entry_from)
2250 {
2251 	cur.push(*this);
2252 	bool enter_front = (entry_from == Inset::ENTRY_DIRECTION_LEFT ||
2253 		(entry_from == Inset::ENTRY_DIRECTION_IGNORE && front));
2254 	enter_front ? idxFirst(cur) : idxLast(cur);
2255 	// The inset formula dimension is not necessarily the same as the
2256 	// one of the instant preview image, so we have to indicate to the
2257 	// BufferView that a metrics update is needed.
2258 	cur.screenUpdateFlags(Update::Force);
2259 }
2260 
2261 
revealCodes(Cursor & cur) const2262 void InsetMathHull::revealCodes(Cursor & cur) const
2263 {
2264 	if (!cur.inMathed())
2265 		return;
2266 	odocstringstream os;
2267 	cur.info(os, false);
2268 	cur.message(os.str());
2269 /*
2270 	// write something to the minibuffer
2271 	// translate to latex
2272 	cur.markInsert(bv);
2273 	ostringstream os;
2274 	write(os);
2275 	string str = os.str();
2276 	cur.markErase(bv);
2277 	string::size_type pos = 0;
2278 	string res;
2279 	for (string::iterator it = str.begin(); it != str.end(); ++it) {
2280 		if (*it == '\n')
2281 			res += ' ';
2282 		else if (*it == '\0') {
2283 			res += "  -X-  ";
2284 			pos = it - str.begin();
2285 		}
2286 		else
2287 			res += *it;
2288 	}
2289 	if (pos > 30)
2290 		res = res.substr(pos - 30);
2291 	if (res.size() > 60)
2292 		res = res.substr(0, 60);
2293 	cur.message(res);
2294 */
2295 }
2296 
2297 
2298 /////////////////////////////////////////////////////////////////////
2299 
2300 
2301 #if 0
2302 bool InsetMathHull::searchForward(BufferView * bv, string const & str,
2303 				     bool, bool)
2304 {
2305 	// FIXME: completely broken
2306 	static InsetMathHull * lastformula = 0;
2307 	static CursorBase current = DocIterator(ibegin(nucleus()));
2308 	static MathData ar;
2309 	static string laststr;
2310 
2311 	if (lastformula != this || laststr != str) {
2312 		//lyxerr << "reset lastformula to " << this << endl;
2313 		lastformula = this;
2314 		laststr = str;
2315 		current	= ibegin(nucleus());
2316 		ar.clear();
2317 		mathed_parse_cell(ar, str, Parse::NORMAL, &buffer());
2318 	} else {
2319 		increment(current);
2320 	}
2321 	//lyxerr << "searching '" << str << "' in " << this << ar << endl;
2322 
2323 	for (DocIterator it = current; it != iend(nucleus()); increment(it)) {
2324 		CursorSlice & top = it.back();
2325 		MathData const & a = top.asInsetMath()->cell(top.idx_);
2326 		if (a.matchpart(ar, top.pos_)) {
2327 			bv->cursor().setSelection(it, ar.size());
2328 			current = it;
2329 			top.pos_ += ar.size();
2330 			bv->update();
2331 			return true;
2332 		}
2333 	}
2334 
2335 	//lyxerr << "not found!" << endl;
2336 	lastformula = 0;
2337 	return false;
2338 }
2339 #endif
2340 
2341 
write(ostream & os) const2342 void InsetMathHull::write(ostream & os) const
2343 {
2344 	odocstringstream oss;
2345 	otexrowstream ots(oss);
2346 	WriteStream wi(ots, false, false, WriteStream::wsDefault);
2347 	oss << "Formula ";
2348 	write(wi);
2349 	os << to_utf8(oss.str());
2350 }
2351 
2352 
read(Lexer & lex)2353 void InsetMathHull::read(Lexer & lex)
2354 {
2355 	MathAtom at;
2356 	mathed_parse_normal(buffer_, at, lex, Parse::TRACKMACRO);
2357 	operator=(*at->asHullInset());
2358 }
2359 
2360 
readQuiet(Lexer & lex)2361 bool InsetMathHull::readQuiet(Lexer & lex)
2362 {
2363 	MathAtom at;
2364 	bool success = mathed_parse_normal(buffer_, at, lex, Parse::QUIET);
2365 	if (success)
2366 		operator=(*at->asHullInset());
2367 	return success;
2368 }
2369 
2370 
plaintext(odocstringstream & os,OutputParams const & op,size_t max_length) const2371 int InsetMathHull::plaintext(odocstringstream & os,
2372         OutputParams const & op, size_t max_length) const
2373 {
2374 	// disables ASCII-art for export of equations. See #2275.
2375 	if (0 && display()) {
2376 		Dimension dim;
2377 		TextMetricsInfo mi;
2378 		metricsT(mi, dim);
2379 		TextPainter tpain(dim.width(), dim.height());
2380 		drawT(tpain, 0, dim.ascent());
2381 		tpain.show(os, 3);
2382 		// reset metrics cache to "real" values
2383 		//metrics();
2384 		return tpain.textheight();
2385 	}
2386 
2387 	odocstringstream oss;
2388 	otexrowstream ots(oss);
2389 	Encoding const * const enc = encodings.fromLyXName("utf8");
2390 	WriteStream wi(ots, false, true, WriteStream::wsDefault, enc);
2391 
2392 	// Fix Bug #6139
2393 	if (type_ == hullRegexp)
2394 		write(wi);
2395 	else {
2396 		for (row_type r = 0; r < nrows(); ++r) {
2397 			for (col_type c = 0; c < ncols(); ++c)
2398 				wi << (c == 0 ? "" : "\t") << cell(index(r, c));
2399 			// if it's for the TOC, we write just the first line
2400 			// and do not include the newline.
2401 			if (op.for_toc || op.for_tooltip || oss.str().size() >= max_length)
2402 				break;
2403 			if (r < nrows() - 1)
2404 				wi << "\n";
2405 		}
2406 	}
2407 	docstring const str = oss.str();
2408 	os << str;
2409 	return str.size();
2410 }
2411 
2412 
docbook(odocstream & os,OutputParams const & runparams) const2413 int InsetMathHull::docbook(odocstream & os, OutputParams const & runparams) const
2414 {
2415 	MathStream ms(os);
2416 	int res = 0;
2417 	docstring name;
2418 	if (getType() == hullSimple)
2419 		name = from_ascii("inlineequation");
2420 	else
2421 		name = from_ascii("informalequation");
2422 
2423 	docstring bname = name;
2424 	if (!label(0).empty())
2425 		bname += " id='" + sgml::cleanID(buffer(), runparams, label(0)) + "'";
2426 
2427 	++ms.tab(); ms.cr(); ms.os() << '<' << bname << '>';
2428 
2429 	odocstringstream ls;
2430 	otexstream ols(ls);
2431 	if (runparams.flavor == OutputParams::XML) {
2432 		ms << MTag("alt role='tex' ");
2433 		// Workaround for db2latex: db2latex always includes equations with
2434 		// \ensuremath{} or \begin{display}\end{display}
2435 		// so we strip LyX' math environment
2436 		WriteStream wi(ols, false, false, WriteStream::wsDefault, runparams.encoding);
2437 		InsetMathGrid::write(wi);
2438 		ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&amp;"), "<", "&lt;"));
2439 		ms << ETag("alt");
2440 		ms << MTag("math");
2441 		ms << ETag("alt");
2442 		ms << MTag("math");
2443 		InsetMathGrid::mathmlize(ms);
2444 		ms << ETag("math");
2445 	} else {
2446 		ms << MTag("alt role='tex'");
2447 		latex(ols, runparams);
2448 		res = ols.texrow().rows();
2449 		ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&amp;"), "<", "&lt;"));
2450 		ms << ETag("alt");
2451 	}
2452 
2453 	ms << from_ascii("<graphic fileref=\"eqn/");
2454 	if (!label(0).empty())
2455 		ms << sgml::cleanID(buffer(), runparams, label(0));
2456 	else
2457 		ms << sgml::uniqueID(from_ascii("anon"));
2458 
2459 	if (runparams.flavor == OutputParams::XML)
2460 		ms << from_ascii("\"/>");
2461 	else
2462 		ms << from_ascii("\">");
2463 
2464 	ms.cr(); --ms.tab(); ms.os() << "</" << name << '>';
2465 
2466 	return ms.line() + res;
2467 }
2468 
2469 
haveNumbers() const2470 bool InsetMathHull::haveNumbers() const
2471 {
2472 	bool havenumbers = false;
2473 	// inline formulas are never numbered (bug 7351 part 3)
2474 	if (getType() == hullSimple)
2475 		return havenumbers;
2476 	for (size_t i = 0; i != numbered_.size(); ++i) {
2477 		if (numbered(i)) {
2478 			havenumbers = true;
2479 			break;
2480 		}
2481 	}
2482 	return havenumbers;
2483 }
2484 
2485 
2486 // FIXME XHTML
2487 // We need to do something about alignment here.
2488 //
2489 // This duplicates code from InsetMathGrid, but
2490 // we need access here to number information,
2491 // and we simply do not have that in InsetMathGrid.
htmlize(HtmlStream & os) const2492 void InsetMathHull::htmlize(HtmlStream & os) const
2493 {
2494 	bool const havenumbers = haveNumbers();
2495 	bool const havetable = havenumbers || nrows() > 1 || ncols() > 1;
2496 
2497 	if (!havetable) {
2498 		os << cell(index(0, 0));
2499 		return;
2500 	}
2501 
2502 	os << MTag("table", "class='mathtable'");
2503 	for (row_type row = 0; row < nrows(); ++row) {
2504 		os << MTag("tr");
2505 		for (col_type col = 0; col < ncols(); ++col) {
2506 			os << MTag("td");
2507 			os << cell(index(row, col));
2508 			os << ETag("td");
2509 		}
2510 		if (havenumbers) {
2511 			os << MTag("td");
2512 			docstring const & num = numbers_[row];
2513 			if (!num.empty())
2514 				os << '(' << num << ')';
2515 		  os << ETag("td");
2516 		}
2517 		os << ETag("tr");
2518 	}
2519 	os << ETag("table");
2520 }
2521 
2522 
2523 // this duplicates code from InsetMathGrid, but
2524 // we need access here to number information,
2525 // and we simply do not have that in InsetMathGrid.
mathmlize(MathStream & os) const2526 void InsetMathHull::mathmlize(MathStream & os) const
2527 {
2528 	bool const havenumbers = haveNumbers();
2529 	bool const havetable = havenumbers || nrows() > 1 || ncols() > 1;
2530 
2531 	if (havetable)
2532 		os << MTag("mtable");
2533 	char const * const celltag = havetable ? "mtd" : "mrow";
2534 	// FIXME There does not seem to be wide support at the moment
2535 	// for mlabeledtr, so we have to use just mtr for now.
2536 	// char const * const rowtag = havenumbers ? "mlabeledtr" : "mtr";
2537 	char const * const rowtag = "mtr";
2538 	for (row_type row = 0; row < nrows(); ++row) {
2539 		if (havetable)
2540 			os << MTag(rowtag);
2541 		for (col_type col = 0; col < ncols(); ++col) {
2542 			os << MTag(celltag)
2543 			   << cell(index(row, col))
2544 			   << ETag(celltag);
2545 		}
2546 		// fleqn?
2547 		if (havenumbers) {
2548 			os << MTag("mtd");
2549 			docstring const & num = numbers_[row];
2550 			if (!num.empty())
2551 				os << '(' << num << ')';
2552 		  os << ETag("mtd");
2553 		}
2554 		if (havetable)
2555 			os << ETag(rowtag);
2556 	}
2557 	if (havetable)
2558 		os << ETag("mtable");
2559 }
2560 
2561 
mathAsLatex(WriteStream & os) const2562 void InsetMathHull::mathAsLatex(WriteStream & os) const
2563 {
2564 	MathEnsurer ensurer(os, false);
2565 	bool havenumbers = haveNumbers();
2566 	bool const havetable = havenumbers || nrows() > 1 || ncols() > 1;
2567 
2568 	if (!havetable) {
2569 		os << cell(index(0, 0));
2570 		return;
2571 	}
2572 
2573 	os << "<table class='mathtable'>";
2574 	for (row_type row = 0; row < nrows(); ++row) {
2575 		os << "<tr>";
2576 		for (col_type col = 0; col < ncols(); ++col) {
2577 			os << "<td class='math'>";
2578 			os << cell(index(row, col));
2579 			os << "</td>";
2580 		}
2581 		if (havenumbers) {
2582 			os << "<td>";
2583 			docstring const & num = numbers_[row];
2584 			if (!num.empty())
2585 				os << '(' << num << ')';
2586 		  os << "</td>";
2587 		}
2588 		os << "</tr>";
2589 	}
2590 	os << "</table>";
2591 }
2592 
2593 
xhtml(XHTMLStream & xs,OutputParams const & op) const2594 docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const & op) const
2595 {
2596 	BufferParams::MathOutput const mathtype =
2597 		buffer().masterBuffer()->params().html_math_output;
2598 
2599 	bool success = false;
2600 
2601 	// we output all the labels just at the beginning of the equation.
2602 	// this should be fine.
2603 	for (size_t i = 0; i != label_.size(); ++i) {
2604 		InsetLabel const * const il = label_[i];
2605 		if (!il)
2606 			continue;
2607 		il->xhtml(xs, op);
2608 	}
2609 
2610 	// FIXME Eventually we would like to do this inset by inset.
2611 	if (mathtype == BufferParams::MathML) {
2612 		odocstringstream os;
2613 		MathStream ms(os);
2614 		try {
2615 			mathmlize(ms);
2616 			success = true;
2617 		} catch (MathExportException const &) {}
2618 		if (success) {
2619 			if (getType() == hullSimple)
2620 				xs << html::StartTag("math",
2621 							"xmlns=\"http://www.w3.org/1998/Math/MathML\"", true);
2622 			else
2623 				xs << html::StartTag("math",
2624 				      "display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"", true);
2625 			xs << XHTMLStream::ESCAPE_NONE
2626 				 << os.str()
2627 				 << html::EndTag("math");
2628 		}
2629 	} else if (mathtype == BufferParams::HTML) {
2630 		odocstringstream os;
2631 		HtmlStream ms(os);
2632 		try {
2633 			htmlize(ms);
2634 			success = true;
2635 		} catch (MathExportException const &) {}
2636 		if (success) {
2637 			string const tag = (getType() == hullSimple) ? "span" : "div";
2638 			xs << html::StartTag(tag, "class='formula'", true)
2639 			   << XHTMLStream::ESCAPE_NONE
2640 			   << os.str()
2641 			   << html::EndTag(tag);
2642 		}
2643 	}
2644 
2645 	// what we actually want is this:
2646 	// if (
2647 	//     ((mathtype == BufferParams::MathML || mathtype == BufferParams::HTML)
2648 	//       && !success)
2649 	//     || mathtype == BufferParams::Images
2650 	//    )
2651 	// but what follows is equivalent, since we'll enter only if either (a) we
2652 	// tried and failed with MathML or HTML or (b) didn't try yet at all but
2653 	// aren't doing LaTeX.
2654 	//
2655 	// so this is for Images.
2656 	if (!success && mathtype != BufferParams::LaTeX) {
2657 		graphics::PreviewImage const * pimage = 0;
2658 		if (!op.dryrun) {
2659 			loadPreview(docit_);
2660 			pimage = preview_->getPreviewImage(buffer());
2661 			// FIXME Do we always have png?
2662 		}
2663 
2664 		if (pimage || op.dryrun) {
2665 			string const filename = pimage ? pimage->filename().onlyFileName()
2666 			                               : "previewimage.png";
2667 			if (pimage) {
2668 				// if we are not in the master buffer, then we need to see that the
2669 				// generated image is copied there; otherwise, preview fails.
2670 				Buffer const * mbuf = buffer().masterBuffer();
2671 				if (mbuf != &buffer()) {
2672 					string mbtmp = mbuf->temppath();
2673 					FileName const mbufimg(support::addName(mbtmp, filename));
2674 					pimage->filename().copyTo(mbufimg);
2675 				}
2676 				// add the file to the list of files to be exported
2677 				op.exportdata->addExternalFile("xhtml", pimage->filename());
2678 			}
2679 
2680 			string const tag = (getType() == hullSimple) ? "span" : "div";
2681 			xs << html::CR()
2682 			   << html::StartTag(tag, "style = \"text-align: center;\"")
2683 				 << html::CompTag("img", "src=\"" + filename + "\" alt=\"Mathematical Equation\"")
2684 				 << html::EndTag(tag)
2685 				 << html::CR();
2686 			success = true;
2687 		}
2688 	}
2689 
2690 	// so we'll pass this test if we've failed everything else, or
2691 	// if mathtype was LaTeX, since we won't have entered any of the
2692 	// earlier branches
2693 	if (!success /* || mathtype != BufferParams::LaTeX */) {
2694 		// Unfortunately, we cannot use latexString() because we do not want
2695 		// $...$ or whatever.
2696 		odocstringstream ls;
2697 		otexrowstream ots(ls);
2698 		WriteStream wi(ots, false, true, WriteStream::wsPreview);
2699 		ModeSpecifier specifier(wi, MATH_MODE);
2700 		mathAsLatex(wi);
2701 		docstring const latex = ls.str();
2702 
2703 		// class='math' allows for use of jsMath
2704 		// http://www.math.union.edu/~dpvc/jsMath/
2705 		// FIXME XHTML
2706 		// probably should allow for some kind of customization here
2707 		string const tag = (getType() == hullSimple) ? "span" : "div";
2708 		xs << html::StartTag(tag, "class='math'")
2709 		   << latex
2710 		   << html::EndTag(tag)
2711 		   << html::CR();
2712 	}
2713 	return docstring();
2714 }
2715 
2716 
toString(odocstream & os) const2717 void InsetMathHull::toString(odocstream & os) const
2718 {
2719 	odocstringstream ods;
2720 	plaintext(ods, OutputParams(0));
2721 	os << ods.str();
2722 }
2723 
2724 
forOutliner(docstring & os,size_t const,bool const) const2725 void InsetMathHull::forOutliner(docstring & os, size_t const, bool const) const
2726 {
2727 	odocstringstream ods;
2728 	OutputParams op(0);
2729 	op.for_toc = true;
2730 	// FIXME: this results in spilling TeX into the LyXHTML output since the
2731 	// outliner is used to generate the LyXHTML list of figures/etc.
2732 	plaintext(ods, op);
2733 	os += ods.str();
2734 }
2735 
2736 
contextMenuName() const2737 string InsetMathHull::contextMenuName() const
2738 {
2739 	return "context-math";
2740 }
2741 
2742 
recordLocation(DocIterator const & di)2743 void InsetMathHull::recordLocation(DocIterator const & di)
2744 {
2745 	docit_ = di;
2746 }
2747 
2748 
canPaintChange(BufferView const &) const2749 bool InsetMathHull::canPaintChange(BufferView const &) const
2750 {
2751 	// We let RowPainter do it seamlessly for inline insets
2752 	return display() != Inline;
2753 }
2754 
2755 
2756 } // namespace lyx
2757