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