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