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