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