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