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