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