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