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