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