]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathHull.cpp
Fix problem with drawing of math backround with selections.
[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, true),
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, true),
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::status() == LyXRC::PREVIEW_ON
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::status() == LyXRC::PREVIEW_ON) {
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::status() != LyXRC::PREVIEW_ON && !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::status() == LyXRC::PREVIEW_ON) {
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, bool 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];
746 }
747
748
749 bool InsetMathHull::ams() const
750 {
751         return type_ == hullAlign
752                 || type_ == hullFlAlign
753                 || type_ == hullMultline
754                 || type_ == hullGather
755                 || type_ == hullAlignAt
756                 || type_ == hullXAlignAt
757                 || type_ == hullXXAlignAt;
758 }
759
760
761 Inset::DisplayType InsetMathHull::display() const
762 {
763         if (type_ == hullSimple || type_ == hullNone || type_ == hullRegexp)
764                 return Inline;
765         return AlignCenter;
766 }
767
768 bool InsetMathHull::numberedType() const
769 {
770         if (type_ == hullNone)
771                 return false;
772         if (type_ == hullSimple)
773                 return false;
774         if (type_ == hullXXAlignAt)
775                 return false;
776         if (type_ == hullRegexp)
777                 return false;
778         for (row_type row = 0; row < nrows(); ++row)
779                 if (numbered_[row])
780                         return true;
781         return false;
782 }
783
784
785 void InsetMathHull::validate(LaTeXFeatures & features) const
786 {
787         if (features.runparams().isLaTeX()) {
788                 if (ams())
789                         features.require("amsmath");
790
791                 if (type_ == hullRegexp) {
792                         features.require("color");
793                         string frcol = lcolor.getLaTeXName(Color_regexpframe);
794                         string bgcol = "white";
795                         features.addPreambleSnippet(
796                                 string("\\newcommand{\\regexp}[1]{\\fcolorbox{")
797                                 + frcol + string("}{")
798                                 + bgcol + string("}{\\ensuremath{\\mathtt{#1}}}}"));
799                         features.addPreambleSnippet(
800                                 string("\\newcommand{\\endregexp}{}"));
801                 }
802
803                 // Validation is necessary only if not using AMS math.
804                 // To be safe, we will always run mathedvalidate.
805                 //if (features.amsstyle)
806                 //  return;
807
808                 //features.binom      = true;
809         } else if (features.runparams().math_flavor == OutputParams::MathAsHTML) {
810                 // it would be better to do this elsewhere, but we can't validate in
811                 // InsetMathMatrix and we have no way, outside MathExtern, to know if
812                 // we even have any matrices.
813                                 features.addCSSSnippet(
814                                         "table.matrix{display: inline-block; vertical-align: middle; text-align:center;}\n"
815                                         "table.matrix td{padding: 0.25px;}\n"
816                                         "td.ldelim{width: 0.5ex; border: thin solid black; border-right: none;}\n"
817                                         "td.rdelim{width: 0.5ex; border: thin solid black; border-left: none;}");
818         }
819         InsetMathGrid::validate(features);
820 }
821
822
823 void InsetMathHull::header_write(WriteStream & os) const
824 {
825         bool n = numberedType();
826
827         switch(type_) {
828         case hullNone:
829                 break;
830
831         case hullSimple:
832                 os << '$';
833                 if (cell(0).empty())
834                         os << ' ';
835                 break;
836
837         case hullEquation:
838                 if (n)
839                         os << "\n\\begin{equation" << star(n) << "}\n";
840                 else
841                         os << "\n\\[\n";
842                 break;
843
844         case hullEqnArray:
845         case hullAlign:
846         case hullFlAlign:
847         case hullGather:
848         case hullMultline:
849                 os << "\n\\begin{" << hullName(type_) << star(n) << "}\n";
850                 break;
851
852         case hullAlignAt:
853         case hullXAlignAt:
854                 os << "\n\\begin{" << hullName(type_) << star(n) << '}'
855                   << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
856                 break;
857
858         case hullXXAlignAt:
859                 os << "\n\\begin{" << hullName(type_) << '}'
860                   << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
861                 break;
862
863         case hullRegexp:
864                 os << "\\regexp{";
865                 break;
866
867         default:
868                 os << "\n\\begin{unknown" << star(n) << "}\n";
869                 break;
870         }
871 }
872
873
874 void InsetMathHull::footer_write(WriteStream & os) const
875 {
876         bool n = numberedType();
877
878         switch(type_) {
879         case hullNone:
880                 os << "\n";
881                 break;
882
883         case hullSimple:
884                 os << '$';
885                 break;
886
887         case hullEquation:
888                 if (n)
889                         os << "\n\\end{equation" << star(n) << "}\n";
890                 else
891                         os << "\n\\]\n";
892                 break;
893
894         case hullEqnArray:
895         case hullAlign:
896         case hullFlAlign:
897         case hullAlignAt:
898         case hullXAlignAt:
899         case hullGather:
900         case hullMultline:
901                 os << "\n\\end{" << hullName(type_) << star(n) << "}\n";
902                 break;
903
904         case hullXXAlignAt:
905                 os << "\n\\end{" << hullName(type_) << "}\n";
906                 break;
907
908         case hullRegexp:
909                 // Only used as a heuristic to find the regexp termination, when searching in ignore-format mode
910                 os << "\\endregexp{}}";
911                 break;
912
913         default:
914                 os << "\n\\end{unknown" << star(n) << "}\n";
915                 break;
916         }
917 }
918
919
920 bool InsetMathHull::rowChangeOK() const
921 {
922         return
923                 type_ == hullEqnArray || type_ == hullAlign ||
924                 type_ == hullFlAlign || type_ == hullAlignAt ||
925                 type_ == hullXAlignAt || type_ == hullXXAlignAt ||
926                 type_ == hullGather || type_ == hullMultline;
927 }
928
929
930 bool InsetMathHull::colChangeOK() const
931 {
932         return
933                 type_ == hullAlign || type_ == hullFlAlign ||type_ == hullAlignAt ||
934                 type_ == hullXAlignAt || type_ == hullXXAlignAt;
935 }
936
937
938 void InsetMathHull::addRow(row_type row)
939 {
940         if (!rowChangeOK())
941                 return;
942
943         bool numbered = numberedType();
944         // Move the number and raw pointer, do not call label() (bug 7511)
945         InsetLabel * label = dummy_pointer;
946         docstring number = empty_docstring();
947         if (type_ == hullMultline) {
948                 if (row + 1 == nrows())  {
949                         numbered_[row] = false;
950                         swap(label, label_[row]);
951                         swap(number, numbers_[row]);
952                 } else
953                         numbered = false;
954         }
955
956         numbered_.insert(numbered_.begin() + row + 1, numbered);
957         numbers_.insert(numbers_.begin() + row + 1, number);
958         label_.insert(label_.begin() + row + 1, label);
959         InsetMathGrid::addRow(row);
960 }
961
962
963 void InsetMathHull::swapRow(row_type row)
964 {
965         if (nrows() <= 1)
966                 return;
967         if (row + 1 == nrows())
968                 --row;
969         // gcc implements the standard std::vector<bool> which is *not* a container:
970         //   http://www.gotw.ca/publications/N1185.pdf
971         // As a results, it doesn't like this:
972         //      swap(numbered_[row], numbered_[row + 1]);
973         // so we do it manually:
974         bool const b = numbered_[row];
975         numbered_[row] = numbered_[row + 1];
976         numbered_[row + 1] = b;
977         swap(numbers_[row], numbers_[row + 1]);
978         swap(label_[row], label_[row + 1]);
979         InsetMathGrid::swapRow(row);
980 }
981
982
983 void InsetMathHull::delRow(row_type row)
984 {
985         if (nrows() <= 1 || !rowChangeOK())
986                 return;
987         if (row + 1 == nrows() && type_ == hullMultline) {
988                 bool const b = numbered_[row - 1];
989                 numbered_[row - 1] = numbered_[row];
990                 numbered_[row] = b;
991                 swap(numbers_[row - 1], numbers_[row]);
992                 swap(label_[row - 1], label_[row]);
993                 InsetMathGrid::delRow(row);
994                 return;
995         }
996         InsetMathGrid::delRow(row);
997         // The last dummy row has no number info nor a label.
998         // Test nrows() + 1 because we have already erased the row.
999         if (row == nrows() + 1)
1000                 row--;
1001         numbered_.erase(numbered_.begin() + row);
1002         numbers_.erase(numbers_.begin() + row);
1003         delete label_[row];
1004         label_.erase(label_.begin() + row);
1005 }
1006
1007
1008 void InsetMathHull::addCol(col_type col)
1009 {
1010         if (!colChangeOK())
1011                 return;
1012         InsetMathGrid::addCol(col);
1013 }
1014
1015
1016 void InsetMathHull::delCol(col_type col)
1017 {
1018         if (ncols() <= 1 || !colChangeOK())
1019                 return;
1020         InsetMathGrid::delCol(col);
1021 }
1022
1023
1024 docstring InsetMathHull::nicelabel(row_type row) const
1025 {
1026         if (!numbered_[row])
1027                 return docstring();
1028         docstring const & val = numbers_[row];
1029         if (!label_[row])
1030                 return '(' + val + ')';
1031         return '(' + val + ',' + label_[row]->screenLabel() + ')';
1032 }
1033
1034
1035 void InsetMathHull::glueall(HullType type)
1036 {
1037         MathData ar;
1038         for (idx_type i = 0; i < nargs(); ++i)
1039                 ar.append(cell(i));
1040         InsetLabel * label = 0;
1041         if (type == hullEquation) {
1042                 // preserve first non-empty label
1043                 for (row_type row = 0; row < nrows(); ++row) {
1044                         if (label_[row]) {
1045                                 label = label_[row];
1046                                 label_[row] = 0;
1047                                 break;
1048                         }
1049                 }
1050         }
1051         *this = InsetMathHull(buffer_, hullSimple);
1052         label_[0] = label;
1053         cell(0) = ar;
1054         setDefaults();
1055 }
1056
1057
1058 void InsetMathHull::splitTo2Cols()
1059 {
1060         LASSERT(ncols() == 1, return);
1061         InsetMathGrid::addCol(1);
1062         for (row_type row = 0; row < nrows(); ++row) {
1063                 idx_type const i = 2 * row;
1064                 pos_type pos = firstRelOp(cell(i));
1065                 cell(i + 1) = MathData(buffer_, cell(i).begin() + pos, cell(i).end());
1066                 cell(i).erase(pos, cell(i).size());
1067         }
1068 }
1069
1070
1071 void InsetMathHull::splitTo3Cols()
1072 {
1073         LASSERT(ncols() < 3, return);
1074         if (ncols() < 2)
1075                 splitTo2Cols();
1076         InsetMathGrid::addCol(2);
1077         for (row_type row = 0; row < nrows(); ++row) {
1078                 idx_type const i = 3 * row + 1;
1079                 if (!cell(i).empty()) {
1080                         cell(i + 1) = MathData(buffer_, cell(i).begin() + 1, cell(i).end());
1081                         cell(i).erase(1, cell(i).size());
1082                 }
1083         }
1084 }
1085
1086
1087 void InsetMathHull::changeCols(col_type cols)
1088 {
1089         if (ncols() == cols)
1090                 return;
1091         else if (ncols() < cols) {
1092                 // split columns
1093                 if (cols < 3)
1094                         splitTo2Cols();
1095                 else {
1096                         splitTo3Cols();
1097                         while (ncols() < cols)
1098                                 InsetMathGrid::addCol(ncols());
1099                 }
1100                 return;
1101         }
1102
1103         // combine columns
1104         for (row_type row = 0; row < nrows(); ++row) {
1105                 idx_type const i = row * ncols();
1106                 for (col_type col = cols; col < ncols(); ++col) {
1107                         cell(i + cols - 1).append(cell(i + col));
1108                 }
1109         }
1110         // delete columns
1111         while (ncols() > cols) {
1112                 InsetMathGrid::delCol(ncols() - 1);
1113         }
1114 }
1115
1116
1117 HullType InsetMathHull::getType() const
1118 {
1119         return type_;
1120 }
1121
1122
1123 void InsetMathHull::setType(HullType type)
1124 {
1125         type_ = type;
1126         setDefaults();
1127 }
1128
1129
1130 void InsetMathHull::mutate(HullType newtype)
1131 {
1132         //lyxerr << "mutating from '" << type_ << "' to '" << newtype << "'" << endl;
1133
1134         // we try to move along the chain
1135         // none <-> simple <-> equation <-> eqnarray -> *align* -> multline, gather -+
1136         //                                     ^                                     |
1137         //                                     +-------------------------------------+
1138         // we use eqnarray as intermediate type for mutations that are not
1139         // directly supported because it handles labels and numbering for
1140         // "down mutation".
1141
1142         if (newtype == type_) {
1143                 // done
1144         }
1145
1146         else if (newtype < hullNone) {
1147                 // unknown type
1148                 dump();
1149         }
1150
1151         else if (type_ == hullNone) {
1152                 setType(hullSimple);
1153                 numbered(0, false);
1154                 mutate(newtype);
1155         }
1156
1157         else if (type_ == hullSimple) {
1158                 if (newtype == hullNone) {
1159                         setType(hullNone);
1160                         numbered(0, false);
1161                 } else {
1162                         setType(hullEquation);
1163                         numbered(0, label_[0] ? true : false);
1164                         mutate(newtype);
1165                 }
1166         }
1167
1168         else if (type_ == hullEquation) {
1169                 if (newtype < type_) {
1170                         setType(hullSimple);
1171                         numbered(0, false);
1172                         mutate(newtype);
1173                 } else if (newtype == hullEqnArray) {
1174                         // split it "nicely" on the first relop
1175                         splitTo3Cols();
1176                         setType(hullEqnArray);
1177                 } else if (newtype == hullMultline || newtype == hullGather) {
1178                         setType(newtype);
1179                 } else {
1180                         // split it "nicely"
1181                         splitTo2Cols();
1182                         setType(hullAlign);
1183                         mutate(newtype);
1184                 }
1185         }
1186
1187         else if (type_ == hullEqnArray) {
1188                 if (newtype < type_) {
1189                         glueall(newtype);
1190                         mutate(newtype);
1191                 } else { // align & Co.
1192                         changeCols(2);
1193                         setType(hullAlign);
1194                         mutate(newtype);
1195                 }
1196         }
1197
1198         else if (type_ ==  hullAlign || type_ == hullAlignAt ||
1199                  type_ == hullXAlignAt || type_ == hullFlAlign) {
1200                 if (newtype < hullAlign) {
1201                         changeCols(3);
1202                         setType(hullEqnArray);
1203                         mutate(newtype);
1204                 } else if (newtype == hullGather || newtype == hullMultline) {
1205                         changeCols(1);
1206                         setType(newtype);
1207                 } else if (newtype ==   hullXXAlignAt) {
1208                         for (row_type row = 0; row < nrows(); ++row)
1209                                 numbered(row, false);
1210                         setType(newtype);
1211                 } else {
1212                         setType(newtype);
1213                 }
1214         }
1215
1216         else if (type_ == hullXXAlignAt) {
1217                 for (row_type row = 0; row < nrows(); ++row)
1218                         numbered(row, false);
1219                 if (newtype < hullAlign) {
1220                         changeCols(3);
1221                         setType(hullEqnArray);
1222                         mutate(newtype);
1223                 } else if (newtype == hullGather || newtype == hullMultline) {
1224                         changeCols(1);
1225                         setType(newtype);
1226                 } else {
1227                         setType(newtype);
1228                 }
1229         }
1230
1231         else if (type_ == hullMultline || type_ == hullGather) {
1232                 if (newtype == hullGather || newtype == hullMultline)
1233                         setType(newtype);
1234                 else if (newtype == hullAlign || newtype == hullFlAlign  ||
1235                          newtype == hullAlignAt || newtype == hullXAlignAt) {
1236                         splitTo2Cols();
1237                         setType(newtype);
1238                 } else if (newtype ==   hullXXAlignAt) {
1239                         splitTo2Cols();
1240                         for (row_type row = 0; row < nrows(); ++row)
1241                                 numbered(row, false);
1242                         setType(newtype);
1243                 } else {
1244                         splitTo3Cols();
1245                         setType(hullEqnArray);
1246                         mutate(newtype);
1247                 }
1248         }
1249
1250         else {
1251                 lyxerr << "mutation from '" << to_utf8(hullName(type_))
1252                        << "' to '" << to_utf8(hullName(newtype))
1253                        << "' not implemented" << endl;
1254         }
1255 }
1256
1257
1258 docstring InsetMathHull::eolString(row_type row, bool fragile, bool latex,
1259                 bool last_eoln) const
1260 {
1261         docstring res;
1262         if (numberedType()) {
1263                 if (label_[row] && numbered_[row]) {
1264                         docstring const name =
1265                                 latex ? escape(label_[row]->getParam("name"))
1266                                       : label_[row]->getParam("name");
1267                         res += "\\label{" + name + '}';
1268                 }
1269                 if (!numbered_[row] && (type_ != hullMultline))
1270                         res += "\\nonumber ";
1271         }
1272         // Never add \\ on the last empty line of eqnarray and friends
1273         last_eoln = false;
1274         return res + InsetMathGrid::eolString(row, fragile, latex, last_eoln);
1275 }
1276
1277
1278 void InsetMathHull::write(WriteStream & os) const
1279 {
1280         ModeSpecifier specifier(os, MATH_MODE);
1281         header_write(os);
1282         InsetMathGrid::write(os);
1283         footer_write(os);
1284 }
1285
1286
1287 void InsetMathHull::normalize(NormalStream & os) const
1288 {
1289         os << "[formula " << hullName(type_) << ' ';
1290         InsetMathGrid::normalize(os);
1291         os << "] ";
1292 }
1293
1294
1295 void InsetMathHull::infoize(odocstream & os) const
1296 {
1297         os << "Type: " << hullName(type_);
1298 }
1299
1300
1301 void InsetMathHull::check() const
1302 {
1303         LATTEST(numbered_.size() == nrows());
1304         LATTEST(numbers_.size() == nrows());
1305         LATTEST(label_.size() == nrows());
1306 }
1307
1308
1309 void InsetMathHull::doExtern(Cursor & cur, FuncRequest & func)
1310 {
1311         docstring dlang;
1312         docstring extra;
1313         idocstringstream iss(func.argument());
1314         iss >> dlang >> extra;
1315         if (extra.empty())
1316                 extra = from_ascii("noextra");
1317         string const lang = to_ascii(dlang);
1318
1319         // FIXME: temporarily disabled
1320         //if (cur.selection()) {
1321         //      MathData ar;
1322         //      selGet(cur.ar);
1323         //      lyxerr << "use selection: " << ar << endl;
1324         //      insert(pipeThroughExtern(lang, extra, ar));
1325         //      return;
1326         //}
1327
1328         // only inline, display or eqnarray math is allowed
1329         if (getType() > hullEqnArray) {
1330                 frontend::Alert::warning(_("Bad math environment"),
1331                                 _("Computation cannot be performed for AMS "
1332                                   "math environments.\nChange the math "
1333                                   "formula type and try again."));
1334                 return;
1335         }
1336
1337         MathData eq;
1338         eq.push_back(MathAtom(new InsetMathChar('=')));
1339
1340         // go to first item in line
1341         cur.idx() -= cur.idx() % ncols();
1342         cur.pos() = 0;
1343
1344         if (getType() == hullSimple) {
1345                 size_type pos = cur.cell().find_last(eq);
1346                 MathData ar;
1347                 if (cur.inMathed() && cur.selection()) {
1348                         asArray(grabAndEraseSelection(cur), ar);
1349                 } else if (!pos == cur.cell().empty()) {
1350                         ar = cur.cell();
1351                         lyxerr << "use whole cell: " << ar << endl;
1352                 } else {
1353                         ar = MathData(buffer_, cur.cell().begin() + pos + 1, cur.cell().end());
1354                         lyxerr << "use partial cell form pos: " << pos << endl;
1355                 }
1356                 cur.cell().append(eq);
1357                 cur.cell().append(pipeThroughExtern(lang, extra, ar));
1358                 cur.pos() = cur.lastpos();
1359                 return;
1360         }
1361
1362         if (getType() == hullEquation) {
1363                 lyxerr << "use equation inset" << endl;
1364                 mutate(hullEqnArray);
1365                 MathData & ar = cur.cell();
1366                 lyxerr << "use cell: " << ar << endl;
1367                 ++cur.idx();
1368                 cur.cell() = eq;
1369                 ++cur.idx();
1370                 cur.cell() = pipeThroughExtern(lang, extra, ar);
1371                 // move to end of line
1372                 cur.pos() = cur.lastpos();
1373                 return;
1374         }
1375
1376         {
1377                 lyxerr << "use eqnarray" << endl;
1378                 cur.idx() += 2 - cur.idx() % ncols();
1379                 cur.pos() = 0;
1380                 MathData ar = cur.cell();
1381                 lyxerr << "use cell: " << ar << endl;
1382                 // FIXME: temporarily disabled
1383                 addRow(cur.row());
1384                 ++cur.idx();
1385                 ++cur.idx();
1386                 cur.cell() = eq;
1387                 ++cur.idx();
1388                 cur.cell() = pipeThroughExtern(lang, extra, ar);
1389                 cur.pos() = cur.lastpos();
1390         }
1391 }
1392
1393
1394 void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd)
1395 {
1396         //lyxerr << "action: " << cmd.action() << endl;
1397         switch (cmd.action()) {
1398
1399         case LFUN_FINISHED_BACKWARD:
1400         case LFUN_FINISHED_FORWARD:
1401         case LFUN_FINISHED_RIGHT:
1402         case LFUN_FINISHED_LEFT:
1403                 //lyxerr << "action: " << cmd.action() << endl;
1404                 InsetMathGrid::doDispatch(cur, cmd);
1405                 cur.undispatched();
1406                 break;
1407
1408         case LFUN_PARAGRAPH_BREAK:
1409                 // just swallow this
1410                 break;
1411
1412         case LFUN_NEWLINE_INSERT:
1413                 // some magic for the common case
1414                 if (type_ == hullSimple || type_ == hullEquation) {
1415                         cur.recordUndoInset();
1416                         bool const align =
1417                                 cur.bv().buffer().params().use_package("amsmath") == BufferParams::package_on;
1418                         mutate(align ? hullAlign : hullEqnArray);
1419                         // mutate() may change labels and such.
1420                         cur.forceBufferUpdate();
1421                         cur.idx() = nrows() * ncols() - 1;
1422                         cur.pos() = cur.lastpos();
1423                 }
1424                 InsetMathGrid::doDispatch(cur, cmd);
1425                 break;
1426
1427         case LFUN_MATH_NUMBER_TOGGLE: {
1428                 //lyxerr << "toggling all numbers" << endl;
1429                 cur.recordUndoInset();
1430                 bool old = numberedType();
1431                 if (type_ == hullMultline)
1432                         numbered(nrows() - 1, !old);
1433                 else
1434                         for (row_type row = 0; row < nrows(); ++row)
1435                                 numbered(row, !old);
1436
1437                 cur.message(old ? _("No number") : _("Number"));
1438                 cur.forceBufferUpdate();
1439                 break;
1440         }
1441
1442         case LFUN_MATH_NUMBER_LINE_TOGGLE: {
1443                 cur.recordUndoInset();
1444                 row_type r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1445                 bool old = numbered(r);
1446                 cur.message(old ? _("No number") : _("Number"));
1447                 numbered(r, !old);
1448                 cur.forceBufferUpdate();
1449                 break;
1450         }
1451
1452         case LFUN_LABEL_INSERT: {
1453                 row_type r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1454                 docstring old_label = label(r);
1455                 docstring const default_label = from_ascii("eq:");
1456                 if (old_label.empty())
1457                         old_label = default_label;
1458
1459                 InsetCommandParams p(LABEL_CODE);
1460                 p["name"] = cmd.argument().empty() ? old_label : cmd.argument();
1461                 string const data = InsetCommand::params2string(p);
1462
1463                 if (cmd.argument().empty())
1464                         cur.bv().showDialog("label", data);
1465                 else {
1466                         FuncRequest fr(LFUN_INSET_INSERT, data);
1467                         dispatch(cur, fr);
1468                 }
1469                 break;
1470         }
1471
1472         case LFUN_LABEL_COPY_AS_REFERENCE: {
1473                 row_type row;
1474                 if (cmd.argument().empty() && &cur.inset() == this)
1475                         // if there is no argument and we're inside math, we retrieve
1476                         // the row number from the cursor position.
1477                         row = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1478                 else {
1479                         // if there is an argument, find the corresponding label, else
1480                         // check whether there is at least one label.
1481                         for (row = 0; row != nrows(); ++row)
1482                                 if (numbered_[row] && label_[row]
1483                                           && (cmd.argument().empty() || label(row) == cmd.argument()))
1484                                         break;
1485                 }
1486
1487                 if (row == nrows())
1488                         break;
1489
1490                 InsetCommandParams p(REF_CODE, "ref");
1491                 p["reference"] = label(row);
1492                 cap::clearSelection();
1493                 cap::copyInset(cur, new InsetRef(buffer_, p), label(row));
1494                 break;
1495         }
1496
1497         case LFUN_WORD_DELETE_FORWARD:
1498         case LFUN_CHAR_DELETE_FORWARD:
1499                 if (col(cur.idx()) + 1 == ncols()
1500                     && cur.pos() == cur.lastpos()
1501                     && !cur.selection()) {
1502                         if (!label(row(cur.idx())).empty()) {
1503                                 cur.recordUndoInset();
1504                                 label(row(cur.idx()), docstring());
1505                         } else if (numbered(row(cur.idx()))) {
1506                                 cur.recordUndoInset();
1507                                 numbered(row(cur.idx()), false);
1508                                 cur.forceBufferUpdate();
1509                         } else {
1510                                 InsetMathGrid::doDispatch(cur, cmd);
1511                                 return;
1512                         }
1513                 } else {
1514                         InsetMathGrid::doDispatch(cur, cmd);
1515                         return;
1516                 }
1517                 break;
1518
1519         case LFUN_INSET_INSERT: {
1520                 //lyxerr << "arg: " << to_utf8(cmd.argument()) << endl;
1521                 // FIXME: this should be cleaned up to use InsetLabel methods directly.
1522                 string const name = cmd.getArg(0);
1523                 if (name == "label") {
1524                         InsetCommandParams p(LABEL_CODE);
1525                         InsetCommand::string2params(to_utf8(cmd.argument()), p);
1526                         docstring str = p["name"];
1527                         cur.recordUndoInset();
1528                         row_type const r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1529                         str = trim(str);
1530                         if (!str.empty())
1531                                 numbered(r, true);
1532                         docstring old = label(r);
1533                         if (str != old) {
1534                                 if (label_[r])
1535                                         // The label will take care of the reference update.
1536                                         label(r, str);
1537                                 else {
1538                                         label(r, str);
1539                                         // Newly created inset so initialize it.
1540                                         label_[r]->initView();
1541                                 }
1542                         }
1543                         cur.forceBufferUpdate();
1544                         break;
1545                 }
1546                 InsetMathGrid::doDispatch(cur, cmd);
1547                 return;
1548         }
1549
1550         case LFUN_MATH_EXTERN:
1551                 cur.recordUndoInset();
1552                 doExtern(cur, cmd);
1553                 break;
1554
1555         case LFUN_MATH_MUTATE: {
1556                 cur.recordUndoInset();
1557                 row_type row = cur.row();
1558                 col_type col = cur.col();
1559                 mutate(hullType(cmd.argument()));
1560                 cur.idx() = row * ncols() + col;
1561                 if (cur.idx() > cur.lastidx()) {
1562                         cur.idx() = cur.lastidx();
1563                         cur.pos() = cur.lastpos();
1564                 }
1565                 if (cur.pos() > cur.lastpos())
1566                         cur.pos() = cur.lastpos();
1567
1568                 cur.forceBufferUpdate();
1569                 // FIXME: find some more clever handling of the selection,
1570                 // i.e. preserve it.
1571                 cur.clearSelection();
1572                 //cur.dispatched(FINISHED);
1573                 break;
1574         }
1575
1576         case LFUN_MATH_DISPLAY: {
1577                 cur.recordUndoInset();
1578                 mutate(type_ == hullSimple ? hullEquation : hullSimple);
1579                 cur.idx() = 0;
1580                 cur.pos() = cur.lastpos();
1581                 //cur.dispatched(FINISHED);
1582                 break;
1583         }
1584
1585         default:
1586                 InsetMathGrid::doDispatch(cur, cmd);
1587                 break;
1588         }
1589 }
1590
1591
1592 bool InsetMathHull::getStatus(Cursor & cur, FuncRequest const & cmd,
1593                 FuncStatus & status) const
1594 {
1595         switch (cmd.action()) {
1596         case LFUN_FINISHED_BACKWARD:
1597         case LFUN_FINISHED_FORWARD:
1598         case LFUN_FINISHED_RIGHT:
1599         case LFUN_FINISHED_LEFT:
1600         case LFUN_UP:
1601         case LFUN_DOWN:
1602         case LFUN_NEWLINE_INSERT:
1603         case LFUN_MATH_EXTERN:
1604                 // we handle these
1605                 status.setEnabled(true);
1606                 return true;
1607
1608         // we never allow this in math, and we want to bind enter
1609         // to another actions in command-alternatives
1610         case LFUN_PARAGRAPH_BREAK:
1611                 status.setEnabled(false);
1612                 return true;
1613         case LFUN_MATH_MUTATE: {
1614                 HullType const ht = hullType(cmd.argument());
1615                 status.setOnOff(type_ == ht);
1616                 status.setEnabled(true);
1617
1618                 if (ht != hullSimple) {
1619                         Cursor tmpcur = cur;
1620                         while (!tmpcur.empty()) {
1621                                 InsetCode code = tmpcur.inset().lyxCode();
1622                                 if (code == BOX_CODE) {
1623                                         return true;
1624                                 } else if (code == TABULAR_CODE) {
1625                                         FuncRequest tmpcmd(LFUN_MATH_DISPLAY);
1626                                         if (tmpcur.getStatus(tmpcmd, status) && !status.enabled())
1627                                                 return true;
1628                                 }
1629                                 tmpcur.pop_back();
1630                         }
1631                 }
1632                 return true;
1633         }
1634         case LFUN_MATH_DISPLAY: {
1635                 bool enable = true;
1636                 if (cur.depth() > 1) {
1637                         Inset const & in = cur[cur.depth()-2].inset();
1638                         if (in.lyxCode() == SCRIPT_CODE)
1639                                 enable = display() != Inline;
1640                 }
1641                 status.setEnabled(enable);
1642                 return true;
1643         }
1644
1645         case LFUN_MATH_NUMBER_TOGGLE:
1646                 // FIXME: what is the right test, this or the one of
1647                 // LABEL_INSERT?
1648                 status.setEnabled(display() != Inline);
1649                 status.setOnOff(numberedType());
1650                 return true;
1651
1652         case LFUN_MATH_NUMBER_LINE_TOGGLE: {
1653                 // FIXME: what is the right test, this or the one of
1654                 // LABEL_INSERT?
1655                 bool const enable = (type_ == hullMultline)
1656                         ? (nrows() - 1 == cur.row())
1657                         : display() != Inline;
1658                 row_type const r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1659                 status.setEnabled(enable);
1660                 status.setOnOff(enable && numbered(r));
1661                 return true;
1662         }
1663
1664         case LFUN_LABEL_INSERT:
1665                 status.setEnabled(type_ != hullSimple);
1666                 return true;
1667
1668         case LFUN_LABEL_COPY_AS_REFERENCE: {
1669                 bool enabled = false;
1670                 row_type row;
1671                 if (cmd.argument().empty() && &cur.inset() == this) {
1672                         // if there is no argument and we're inside math, we retrieve
1673                         // the row number from the cursor position.
1674                         row = (type_ == hullMultline) ? nrows() - 1 : cur.row();
1675                         enabled = numberedType() && label_[row] && numbered_[row];
1676                 } else {
1677                         // if there is an argument, find the corresponding label, else
1678                         // check whether there is at least one label.
1679                         for (row_type row = 0; row != nrows(); ++row) {
1680                                 if (numbered_[row] && label_[row] &&
1681                                         (cmd.argument().empty() || label(row) == cmd.argument())) {
1682                                                 enabled = true;
1683                                                 break;
1684                                 }
1685                         }
1686                 }
1687                 status.setEnabled(enabled);
1688                 return true;
1689         }
1690
1691         case LFUN_INSET_INSERT:
1692                 if (cmd.getArg(0) == "label") {
1693                         status.setEnabled(type_ != hullSimple);
1694                         return true;
1695                 }
1696                 return InsetMathGrid::getStatus(cur, cmd, status);
1697
1698         case LFUN_INSET_MODIFY: {
1699                 istringstream is(to_utf8(cmd.argument()));
1700                 string s;
1701                 is >> s;
1702                 if (s != "tabular")
1703                         return InsetMathGrid::getStatus(cur, cmd, status);
1704                 is >> s;
1705                 if (!rowChangeOK()
1706                     && (s == "append-row"
1707                         || s == "delete-row"
1708                         || s == "copy-row")) {
1709                         status.message(bformat(
1710                                 from_utf8(N_("Can't change number of rows in '%1$s'")),
1711                                 hullName(type_)));
1712                         status.setEnabled(false);
1713                         return true;
1714                 }
1715                 if (!colChangeOK()
1716                     && (s == "append-column"
1717                         || s == "delete-column"
1718                         || s == "copy-column")) {
1719                         status.message(bformat(
1720                                 from_utf8(N_("Can't change number of columns in '%1$s'")),
1721                                 hullName(type_)));
1722                         status.setEnabled(false);
1723                         return true;
1724                 }
1725                 if ((type_ == hullSimple
1726                   || type_ == hullEquation
1727                   || type_ == hullNone) &&
1728                     (s == "add-hline-above" || s == "add-hline-below")) {
1729                         status.message(bformat(
1730                                 from_utf8(N_("Can't add horizontal grid lines in '%1$s'")),
1731                                 hullName(type_)));
1732                         status.setEnabled(false);
1733                         return true;
1734                 }
1735                 if (s == "add-vline-left" || s == "add-vline-right") {
1736                         status.message(bformat(
1737                                 from_utf8(N_("Can't add vertical grid lines in '%1$s'")),
1738                                 hullName(type_)));
1739                         status.setEnabled(false);
1740                         return true;
1741                 }
1742                 if (s == "valign-top" || s == "valign-middle"
1743                  || s == "valign-bottom" || s == "align-left"
1744                  || s == "align-center" || s == "align-right") {
1745                         status.setEnabled(false);
1746                         return true;
1747                 }
1748                 return InsetMathGrid::getStatus(cur, cmd, status);
1749         }
1750
1751         default:
1752                 return InsetMathGrid::getStatus(cur, cmd, status);
1753         }
1754
1755         // This cannot really happen, but inserted to shut-up gcc
1756         return InsetMathGrid::getStatus(cur, cmd, status);
1757 }
1758
1759
1760 /////////////////////////////////////////////////////////////////////
1761
1762
1763
1764 // simply scrap this function if you want
1765 void InsetMathHull::mutateToText()
1766 {
1767 #if 0
1768         // translate to latex
1769         ostringstream os;
1770         latex(os, false, false);
1771         string str = os.str();
1772
1773         // insert this text
1774         Text * lt = view_->cursor().innerText();
1775         string::const_iterator cit = str.begin();
1776         string::const_iterator end = str.end();
1777         for (; cit != end; ++cit)
1778                 view_->getIntl()->getTransManager().TranslateAndInsert(*cit, lt);
1779
1780         // remove ourselves
1781         //dispatch(LFUN_ESCAPE);
1782 #endif
1783 }
1784
1785
1786 void InsetMathHull::handleFont(Cursor & cur, docstring const & arg,
1787         docstring const & font)
1788 {
1789         // this whole function is a hack and won't work for incremental font
1790         // changes...
1791         cur.recordUndo();
1792         if (cur.inset().asInsetMath()->name() == font)
1793                 cur.handleFont(to_utf8(font));
1794         else {
1795                 cur.handleNest(createInsetMath(font, cur.buffer()));
1796                 cur.insert(arg);
1797         }
1798 }
1799
1800
1801 void InsetMathHull::handleFont2(Cursor & cur, docstring const & arg)
1802 {
1803         cur.recordUndo();
1804         Font font;
1805         bool b;
1806         font.fromString(to_utf8(arg), b);
1807         if (font.fontInfo().color() != Color_inherit) {
1808                 MathAtom at = MathAtom(new InsetMathColor(buffer_, true, font.fontInfo().color()));
1809                 cur.handleNest(at, 0);
1810         }
1811 }
1812
1813
1814 void InsetMathHull::edit(Cursor & cur, bool front, EntryDirection entry_from)
1815 {
1816         cur.push(*this);
1817         bool enter_front = (entry_from == Inset::ENTRY_DIRECTION_LEFT ||
1818                 (entry_from == Inset::ENTRY_DIRECTION_IGNORE && front));
1819         enter_front ? idxFirst(cur) : idxLast(cur);
1820         // The inset formula dimension is not necessarily the same as the
1821         // one of the instant preview image, so we have to indicate to the
1822         // BufferView that a metrics update is needed.
1823         cur.screenUpdateFlags(Update::Force);
1824 }
1825
1826
1827 void InsetMathHull::revealCodes(Cursor & cur) const
1828 {
1829         if (!cur.inMathed())
1830                 return;
1831         odocstringstream os;
1832         cur.info(os);
1833         cur.message(os.str());
1834 /*
1835         // write something to the minibuffer
1836         // translate to latex
1837         cur.markInsert(bv);
1838         ostringstream os;
1839         write(os);
1840         string str = os.str();
1841         cur.markErase(bv);
1842         string::size_type pos = 0;
1843         string res;
1844         for (string::iterator it = str.begin(); it != str.end(); ++it) {
1845                 if (*it == '\n')
1846                         res += ' ';
1847                 else if (*it == '\0') {
1848                         res += "  -X-  ";
1849                         pos = it - str.begin();
1850                 }
1851                 else
1852                         res += *it;
1853         }
1854         if (pos > 30)
1855                 res = res.substr(pos - 30);
1856         if (res.size() > 60)
1857                 res = res.substr(0, 60);
1858         cur.message(res);
1859 */
1860 }
1861
1862
1863 /////////////////////////////////////////////////////////////////////
1864
1865
1866 #if 0
1867 bool InsetMathHull::searchForward(BufferView * bv, string const & str,
1868                                      bool, bool)
1869 {
1870         // FIXME: completely broken
1871         static InsetMathHull * lastformula = 0;
1872         static CursorBase current = DocIterator(ibegin(nucleus()));
1873         static MathData ar;
1874         static string laststr;
1875
1876         if (lastformula != this || laststr != str) {
1877                 //lyxerr << "reset lastformula to " << this << endl;
1878                 lastformula = this;
1879                 laststr = str;
1880                 current = ibegin(nucleus());
1881                 ar.clear();
1882                 mathed_parse_cell(ar, str, Parse::NORMAL, &buffer());
1883         } else {
1884                 increment(current);
1885         }
1886         //lyxerr << "searching '" << str << "' in " << this << ar << endl;
1887
1888         for (DocIterator it = current; it != iend(nucleus()); increment(it)) {
1889                 CursorSlice & top = it.back();
1890                 MathData const & a = top.asInsetMath()->cell(top.idx_);
1891                 if (a.matchpart(ar, top.pos_)) {
1892                         bv->cursor().setSelection(it, ar.size());
1893                         current = it;
1894                         top.pos_ += ar.size();
1895                         bv->update();
1896                         return true;
1897                 }
1898         }
1899
1900         //lyxerr << "not found!" << endl;
1901         lastformula = 0;
1902         return false;
1903 }
1904 #endif
1905
1906
1907 void InsetMathHull::write(ostream & os) const
1908 {
1909         odocstringstream oss;
1910         WriteStream wi(oss, false, false, WriteStream::wsDefault);
1911         oss << "Formula ";
1912         write(wi);
1913         os << to_utf8(oss.str());
1914 }
1915
1916
1917 void InsetMathHull::read(Lexer & lex)
1918 {
1919         MathAtom at;
1920         mathed_parse_normal(buffer_, at, lex, Parse::TRACKMACRO);
1921         operator=(*at->asHullInset());
1922 }
1923
1924
1925 bool InsetMathHull::readQuiet(Lexer & lex)
1926 {
1927         MathAtom at;
1928         bool success = mathed_parse_normal(buffer_, at, lex, Parse::QUIET);
1929         if (success)
1930                 operator=(*at->asHullInset());
1931         return success;
1932 }
1933
1934
1935 int InsetMathHull::plaintext(odocstringstream & os,
1936         OutputParams const & op, size_t max_length) const
1937 {
1938         // disables ASCII-art for export of equations. See #2275.
1939         if (0 && display()) {
1940                 Dimension dim;
1941                 TextMetricsInfo mi;
1942                 metricsT(mi, dim);
1943                 TextPainter tpain(dim.width(), dim.height());
1944                 drawT(tpain, 0, dim.ascent());
1945                 tpain.show(os, 3);
1946                 // reset metrics cache to "real" values
1947                 //metrics();
1948                 return tpain.textheight();
1949         }
1950
1951         odocstringstream oss;
1952         Encoding const * const enc = encodings.fromLyXName("utf8");
1953         WriteStream wi(oss, false, true, WriteStream::wsDefault, enc);
1954
1955         // Fix Bug #6139
1956         if (type_ == hullRegexp)
1957                 write(wi);
1958         else {
1959                 for (row_type r = 0; r < nrows(); ++r) {
1960                         for (col_type c = 0; c < ncols(); ++c)
1961                                 wi << (c == 0 ? "" : "\t") << cell(index(r, c));
1962                         // if it's for the TOC, we write just the first line
1963                         // and do not include the newline.
1964                         if (op.for_toc || op.for_tooltip || oss.str().size() >= max_length)
1965                                 break;
1966                         wi << "\n";
1967                 }
1968         }
1969         docstring const str = oss.str();
1970         os << str;
1971         return str.size();
1972 }
1973
1974
1975 int InsetMathHull::docbook(odocstream & os, OutputParams const & runparams) const
1976 {
1977         MathStream ms(os);
1978         int res = 0;
1979         docstring name;
1980         if (getType() == hullSimple)
1981                 name = from_ascii("inlineequation");
1982         else
1983                 name = from_ascii("informalequation");
1984
1985         docstring bname = name;
1986         if (!label(0).empty())
1987                 bname += " id='" + sgml::cleanID(buffer(), runparams, label(0)) + "'";
1988
1989         ++ms.tab(); ms.cr(); ms.os() << '<' << bname << '>';
1990
1991         odocstringstream ls;
1992         if (runparams.flavor == OutputParams::XML) {
1993                 ms << MTag("alt role='tex' ");
1994                 // Workaround for db2latex: db2latex always includes equations with
1995                 // \ensuremath{} or \begin{display}\end{display}
1996                 // so we strip LyX' math environment
1997                 WriteStream wi(ls, false, false, WriteStream::wsDefault, runparams.encoding);
1998                 InsetMathGrid::write(wi);
1999                 ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&amp;"), "<", "&lt;"));
2000                 ms << ETag("alt");
2001                 ms << MTag("math");
2002                 ms << ETag("alt");
2003                 ms << MTag("math");
2004                 InsetMathGrid::mathmlize(ms);
2005                 ms << ETag("math");
2006         } else {
2007                 TexRow texrow;
2008                 texrow.reset();
2009                 otexstream ols(ls, texrow);
2010                 ms << MTag("alt role='tex'");
2011                 latex(ols, runparams);
2012                 res = texrow.rows();
2013                 ms << from_utf8(subst(subst(to_utf8(ls.str()), "&", "&amp;"), "<", "&lt;"));
2014                 ms << ETag("alt");
2015         }
2016
2017         ms << from_ascii("<graphic fileref=\"eqn/");
2018         if (!label(0).empty())
2019                 ms << sgml::cleanID(buffer(), runparams, label(0));
2020         else
2021                 ms << sgml::uniqueID(from_ascii("anon"));
2022
2023         if (runparams.flavor == OutputParams::XML)
2024                 ms << from_ascii("\"/>");
2025         else
2026                 ms << from_ascii("\">");
2027
2028         ms.cr(); --ms.tab(); ms.os() << "</" << name << '>';
2029
2030         return ms.line() + res;
2031 }
2032
2033
2034 bool InsetMathHull::haveNumbers() const
2035 {
2036         bool havenumbers = false;
2037         // inline formulas are never numbered (bug 7351 part 3)
2038         if (getType() == hullSimple)
2039                 return havenumbers;
2040         for (size_t i = 0; i != numbered_.size(); ++i) {
2041                 if (numbered_[i]) {
2042                         havenumbers = true;
2043                         break;
2044                 }
2045         }
2046         return havenumbers;
2047 }
2048
2049
2050 // FIXME XHTML
2051 // We need to do something about alignment here.
2052 //
2053 // This duplicates code from InsetMathGrid, but
2054 // we need access here to number information,
2055 // and we simply do not have that in InsetMathGrid.
2056 void InsetMathHull::htmlize(HtmlStream & os) const
2057 {
2058         bool const havenumbers = haveNumbers();
2059         bool const havetable = havenumbers || nrows() > 1 || ncols() > 1;
2060
2061         if (!havetable) {
2062                 os << cell(index(0, 0));
2063                 return;
2064         }
2065
2066         os << MTag("table", "class='mathtable'");
2067         for (row_type row = 0; row < nrows(); ++row) {
2068                 os << MTag("tr");
2069                 for (col_type col = 0; col < ncols(); ++col) {
2070                         os << MTag("td");
2071                         os << cell(index(row, col));
2072                         os << ETag("td");
2073                 }
2074                 if (havenumbers) {
2075                         os << MTag("td");
2076                         docstring const & num = numbers_[row];
2077                         if (!num.empty())
2078                                 os << '(' << num << ')';
2079                   os << ETag("td");
2080                 }
2081                 os << ETag("tr");
2082         }
2083         os << ETag("table");
2084 }
2085
2086
2087 // this duplicates code from InsetMathGrid, but
2088 // we need access here to number information,
2089 // and we simply do not have that in InsetMathGrid.
2090 void InsetMathHull::mathmlize(MathStream & os) const
2091 {
2092         bool const havenumbers = haveNumbers();
2093         bool const havetable = havenumbers || nrows() > 1 || ncols() > 1;
2094
2095         if (havetable)
2096                 os << MTag("mtable");
2097         char const * const celltag = havetable ? "mtd" : "mrow";
2098         // FIXME There does not seem to be wide support at the moment
2099         // for mlabeledtr, so we have to use just mtr for now.
2100         // char const * const rowtag = havenumbers ? "mlabeledtr" : "mtr";
2101         char const * const rowtag = "mtr";
2102         for (row_type row = 0; row < nrows(); ++row) {
2103                 if (havetable)
2104                         os << MTag(rowtag);
2105                 for (col_type col = 0; col < ncols(); ++col) {
2106                         os << MTag(celltag)
2107                            << cell(index(row, col))
2108                            << ETag(celltag);
2109                 }
2110                 // fleqn?
2111                 if (havenumbers) {
2112                         os << MTag("mtd");
2113                         docstring const & num = numbers_[row];
2114                         if (!num.empty())
2115                                 os << '(' << num << ')';
2116                   os << ETag("mtd");
2117                 }
2118                 if (havetable)
2119                         os << ETag(rowtag);
2120         }
2121         if (havetable)
2122                 os << ETag("mtable");
2123 }
2124
2125
2126 void InsetMathHull::mathAsLatex(WriteStream & os) const
2127 {
2128         MathEnsurer ensurer(os, false);
2129         bool havenumbers = haveNumbers();
2130         bool const havetable = havenumbers || nrows() > 1 || ncols() > 1;
2131
2132         if (!havetable) {
2133                 os << cell(index(0, 0));
2134                 return;
2135         }
2136
2137         os << "<table class='mathtable'>";
2138         for (row_type row = 0; row < nrows(); ++row) {
2139                 os << "<tr>";
2140                 for (col_type col = 0; col < ncols(); ++col) {
2141                         os << "<td class='math'>";
2142                         os << cell(index(row, col));
2143                         os << "</td>";
2144                 }
2145                 if (havenumbers) {
2146                         os << "<td>";
2147                         docstring const & num = numbers_[row];
2148                         if (!num.empty())
2149                                 os << '(' << num << ')';
2150                   os << "</td>";
2151                 }
2152                 os << "</tr>";
2153         }
2154         os << "</table>";
2155 }
2156
2157
2158 docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const & op) const
2159 {
2160         BufferParams::MathOutput const mathtype =
2161                 buffer().masterBuffer()->params().html_math_output;
2162
2163         bool success = false;
2164
2165         // we output all the labels just at the beginning of the equation.
2166         // this should be fine.
2167         for (size_t i = 0; i != label_.size(); ++i) {
2168                 InsetLabel const * const il = label_[i];
2169                 if (!il)
2170                         continue;
2171                 il->xhtml(xs, op);
2172         }
2173
2174         // FIXME Eventually we would like to do this inset by inset.
2175         if (mathtype == BufferParams::MathML) {
2176                 odocstringstream os;
2177                 MathStream ms(os);
2178                 try {
2179                         mathmlize(ms);
2180                         success = true;
2181                 } catch (MathExportException const &) {}
2182                 if (success) {
2183                         if (getType() == hullSimple)
2184                                 xs << html::StartTag("math",
2185                                                         "xmlns=\"http://www.w3.org/1998/Math/MathML\"", true);
2186                         else
2187                                 xs << html::StartTag("math",
2188                                       "display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"", true);
2189                         xs << XHTMLStream::ESCAPE_NONE
2190                                  << os.str()
2191                                  << html::EndTag("math");
2192                 }
2193         } else if (mathtype == BufferParams::HTML) {
2194                 odocstringstream os;
2195                 HtmlStream ms(os);
2196                 try {
2197                         htmlize(ms);
2198                         success = true;
2199                 } catch (MathExportException const &) {}
2200                 if (success) {
2201                         string const tag = (getType() == hullSimple) ? "span" : "div";
2202                         xs << html::StartTag(tag, "class='formula'", true)
2203                            << XHTMLStream::ESCAPE_NONE
2204                            << os.str()
2205                            << html::EndTag(tag);
2206                 }
2207         }
2208
2209         // what we actually want is this:
2210         // if (
2211         //     ((mathtype == BufferParams::MathML || mathtype == BufferParams::HTML)
2212         //       && !success)
2213         //     || mathtype == BufferParams::Images
2214         //    )
2215         // but what follows is equivalent, since we'll enter only if either (a) we
2216         // tried and failed with MathML or HTML or (b) didn't try yet at all but
2217         // aren't doing LaTeX, in which case we are doing Images.
2218         if (!success && mathtype != BufferParams::LaTeX) {
2219                 graphics::PreviewImage const * pimage = 0;
2220                 if (!op.dryrun) {
2221                         loadPreview(docit_);
2222                         pimage = preview_->getPreviewImage(buffer());
2223                         // FIXME Do we always have png?
2224                 }
2225
2226                 if (pimage || op.dryrun) {
2227                         string const filename = pimage ? pimage->filename().onlyFileName()
2228                                                        : "previewimage.png";
2229                         if (pimage) {
2230                                 // if we are not in the master buffer, then we need to see that the
2231                                 // generated image is copied there; otherwise, preview fails.
2232                                 Buffer const * mbuf = buffer().masterBuffer();
2233                                 if (mbuf != &buffer()) {
2234                                         string mbtmp = mbuf->temppath();
2235                                         FileName const mbufimg(support::addName(mbtmp, filename));
2236                                         pimage->filename().copyTo(mbufimg);
2237                                 }
2238                                 // add the file to the list of files to be exported
2239                                 op.exportdata->addExternalFile("xhtml", pimage->filename());
2240                         }
2241
2242                         string const tag = (getType() == hullSimple) ? "span" : "div";
2243                         xs << html::CR()
2244                            << html::StartTag(tag)
2245                                  << html::CompTag("img", "src=\"" + filename + "\" alt=\"Mathematical Equation\"")
2246                                  << html::EndTag(tag)
2247                                  << html::CR();
2248                         success = true;
2249                 }
2250         }
2251
2252         // so we'll pass this test if we've failed everything else, or
2253         // if mathtype was LaTeX, since we won't have entered any of the
2254         // earlier branches
2255         if (!success /* || mathtype != BufferParams::LaTeX */) {
2256                 // Unfortunately, we cannot use latexString() because we do not want
2257                 // $...$ or whatever.
2258                 odocstringstream ls;
2259                 WriteStream wi(ls, false, true, WriteStream::wsPreview);
2260                 ModeSpecifier specifier(wi, MATH_MODE);
2261                 mathAsLatex(wi);
2262                 docstring const latex = ls.str();
2263
2264                 // class='math' allows for use of jsMath
2265                 // http://www.math.union.edu/~dpvc/jsMath/
2266                 // FIXME XHTML
2267                 // probably should allow for some kind of customization here
2268                 string const tag = (getType() == hullSimple) ? "span" : "div";
2269                 xs << html::StartTag(tag, "class='math'")
2270                    << latex
2271                    << html::EndTag(tag)
2272                    << html::CR();
2273         }
2274         return docstring();
2275 }
2276
2277
2278 void InsetMathHull::toString(odocstream & os) const
2279 {
2280         odocstringstream ods;
2281         plaintext(ods, OutputParams(0));
2282         os << ods.str();
2283 }
2284
2285
2286 void InsetMathHull::forOutliner(docstring & os, size_t) const
2287 {
2288         odocstringstream ods;
2289         OutputParams op(0);
2290         op.for_toc = true;
2291         plaintext(ods, op);
2292         os += ods.str();
2293 }
2294
2295
2296 string InsetMathHull::contextMenuName() const
2297 {
2298         return "context-math";
2299 }
2300
2301
2302 void InsetMathHull::recordLocation(DocIterator const & di)
2303 {
2304         docit_ = di;
2305 }
2306
2307 } // namespace lyx