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