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