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