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