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