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