]> git.lyx.org Git - lyx.git/blob - src/mathed/math_hullinset.C
fix bug 2067
[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         // The last dummy row has no number info nor a label.
592         // Test nrows() + 1 because we have already erased the row.
593         if (row == nrows() + 1)
594                 row--;
595         nonum_.erase(nonum_.begin() + row);
596         label_.erase(label_.begin() + row);
597 }
598
599
600 void MathHullInset::addCol(col_type col)
601 {
602         if (!colChangeOK())
603                 return;
604         MathGridInset::addCol(col);
605 }
606
607
608 void MathHullInset::delCol(col_type col)
609 {
610         if (ncols() <= 1 || !colChangeOK())
611                 return;
612         MathGridInset::delCol(col);
613 }
614
615
616 string MathHullInset::nicelabel(row_type row) const
617 {
618         if (nonum_[row])
619                 return string();
620         if (label_[row].empty())
621                 return string("(#)");
622         return '(' + label_[row] + ')';
623 }
624
625
626 void MathHullInset::glueall()
627 {
628         MathArray ar;
629         for (idx_type i = 0; i < nargs(); ++i)
630                 ar.append(cell(i));
631         *this = MathHullInset("simple");
632         cell(0) = ar;
633         setDefaults();
634 }
635
636
637 void MathHullInset::splitTo2Cols()
638 {
639         BOOST_ASSERT(ncols() == 1);
640         MathGridInset::addCol(1);
641         for (row_type row = 0; row < nrows(); ++row) {
642                 idx_type const i = 2 * row;
643                 pos_type pos = firstRelOp(cell(i));
644                 cell(i + 1) = MathArray(cell(i).begin() + pos, cell(i).end());
645                 cell(i).erase(pos, cell(i).size());
646         }
647 }
648
649
650 void MathHullInset::splitTo3Cols()
651 {
652         BOOST_ASSERT(ncols() < 3);
653         if (ncols() < 2)
654                 splitTo2Cols();
655         MathGridInset::addCol(1);
656         for (row_type row = 0; row < nrows(); ++row) {
657                 idx_type const i = 3 * row + 1;
658                 if (cell(i).size()) {
659                         cell(i + 1) = MathArray(cell(i).begin() + 1, cell(i).end());
660                         cell(i).erase(1, cell(i).size());
661                 }
662         }
663 }
664
665
666 void MathHullInset::changeCols(col_type cols)
667 {
668         if (ncols() == cols)
669                 return;
670         else if (ncols() < cols) {
671                 // split columns
672                 if (cols < 3)
673                         splitTo2Cols();
674                 else {
675                         splitTo3Cols();
676                         while (ncols() < cols)
677                                 MathGridInset::addCol(ncols() - 1);
678                 }
679                 return;
680         }
681
682         // combine columns
683         for (row_type row = 0; row < nrows(); ++row) {
684                 idx_type const i = row * ncols();
685                 for (col_type col = cols; col < ncols(); ++col) {
686                         cell(i + cols - 1).append(cell(i + col));
687                 }
688         }
689         // delete columns
690         while (ncols() > cols) {
691                 MathGridInset::delCol(ncols() - 1);
692         }
693 }
694
695
696 string const & MathHullInset::getType() const
697 {
698         return type_;
699 }
700
701
702 void MathHullInset::setType(string const & type)
703 {
704         type_ = type;
705         setDefaults();
706 }
707
708
709
710 void MathHullInset::mutate(string const & newtype)
711 {
712         //lyxerr << "mutating from '" << type_ << "' to '" << newtype << "'" << endl;
713
714         // we try to move along the chain
715         // none <-> simple <-> equation <-> eqnarray -> *align* -> multline, gather -+
716         //                                     ^                                     |
717         //                                     +-------------------------------------+
718         // we use eqnarray as intermediate type for mutations that are not
719         // directly supported because it handles labels and numbering for
720         // "down mutation".
721
722         if (newtype == "dump") {
723                 dump();
724         }
725
726         else if (newtype == type_) {
727                 // done
728         }
729
730         else if (typecode(newtype) < 0) {
731                 // unknown type
732         }
733
734         else if (type_ == "none") {
735                 setType("simple");
736                 numbered(0, false);
737                 mutate(newtype);
738         }
739
740         else if (type_ == "simple") {
741                 if (newtype == "none") {
742                         setType("none");
743                         numbered(0, false);
744                 } else {
745                         setType("equation");
746                         numbered(0, false);
747                         mutate(newtype);
748                 }
749         }
750
751         else if (type_ == "equation") {
752                 if (smaller(newtype, type_)) {
753                         setType("simple");
754                         numbered(0, false);
755                         mutate(newtype);
756                 } else if (newtype == "eqnarray") {
757                         // split it "nicely" on the first relop
758                         splitTo3Cols();
759                         setType("eqnarray");
760                 } else if (newtype == "multline" || newtype == "gather") {
761                         setType(newtype);
762                 } else {
763                         // split it "nicely"
764                         splitTo2Cols();
765                         setType("align");
766                         mutate(newtype);
767                 }
768         }
769
770         else if (type_ == "eqnarray") {
771                 if (smaller(newtype, type_)) {
772                         // set correct (no)numbering
773                         bool allnonum = true;
774                         for (row_type row = 0; row < nrows(); ++row)
775                                 if (!nonum_[row])
776                                         allnonum = false;
777
778                         // set first non-empty label
779                         string label;
780                         for (row_type row = 0; row < nrows(); ++row) {
781                                 if (!label_[row].empty()) {
782                                         label = label_[row];
783                                         break;
784                                 }
785                         }
786
787                         glueall();
788                         nonum_[0] = allnonum;
789                         label_[0] = label;
790                         mutate(newtype);
791                 } else { // align & Co.
792                         changeCols(2);
793                         setType("align");
794                         mutate(newtype);
795                 }
796         }
797
798         else if (type_ ==  "align"   || type_ == "alignat" ||
799                  type_ == "xalignat" || type_ == "flalign") {
800                 if (smaller(newtype, "align")) {
801                         changeCols(3);
802                         setType("eqnarray");
803                         mutate(newtype);
804                 } else if (newtype == "gather" || newtype == "multline") {
805                         changeCols(1);
806                         setType(newtype);
807                 } else if (newtype ==   "xxalignat") {
808                         for (row_type row = 0; row < nrows(); ++row)
809                                 numbered(row, false);
810                         setType(newtype);
811                 } else {
812                         setType(newtype);
813                 }
814         }
815
816         else if (type_ == "xxalignat") {
817                 for (row_type row = 0; row < nrows(); ++row)
818                         numbered(row, false);
819                 if (smaller(newtype, "align")) {
820                         changeCols(3);
821                         setType("eqnarray");
822                         mutate(newtype);
823                 } else if (newtype == "gather" || newtype == "multline") {
824                         changeCols(1);
825                         setType(newtype);
826                 } else {
827                         setType(newtype);
828                 }
829         }
830
831         else if (type_ == "multline" || type_ == "gather") {
832                 if (newtype == "gather" || newtype == "multline")
833                         setType(newtype);
834                 else if (newtype ==   "align"   || newtype == "flalign"  ||
835                          newtype ==   "alignat" || newtype == "xalignat") {
836                         splitTo2Cols();
837                         setType(newtype);
838                 } else if (newtype ==   "xxalignat") {
839                         splitTo2Cols();
840                         for (row_type row = 0; row < nrows(); ++row)
841                                 numbered(row, false);
842                         setType(newtype);
843                 } else {
844                         splitTo3Cols();
845                         setType("eqnarray");
846                         mutate(newtype);
847                 }
848         }
849
850         else {
851                 lyxerr << "mutation from '" << type_
852                        << "' to '" << newtype << "' not implemented" << endl;
853         }
854 }
855
856
857 string MathHullInset::eolString(row_type row, bool emptyline, bool fragile) const
858 {
859         string res;
860         if (numberedType()) {
861                 if (!label_[row].empty() && !nonum_[row])
862                         res += "\\label{" + label_[row] + '}';
863                 if (nonum_[row] && (type_ != "multline"))
864                         res += "\\nonumber ";
865         }
866         return res + MathGridInset::eolString(row, emptyline, fragile);
867 }
868
869
870 void MathHullInset::write(WriteStream & os) const
871 {
872         header_write(os);
873         MathGridInset::write(os);
874         footer_write(os);
875 }
876
877
878 void MathHullInset::normalize(NormalStream & os) const
879 {
880         os << "[formula " << type_ << ' ';
881         MathGridInset::normalize(os);
882         os << "] ";
883 }
884
885
886 void MathHullInset::mathmlize(MathMLStream & os) const
887 {
888         MathGridInset::mathmlize(os);
889 }
890
891
892 void MathHullInset::infoize(ostream & os) const
893 {
894         os << "Type: " << type_;
895 }
896
897
898 void MathHullInset::check() const
899 {
900         BOOST_ASSERT(nonum_.size() == nrows());
901         BOOST_ASSERT(label_.size() == nrows());
902 }
903
904
905 void MathHullInset::doExtern(LCursor & cur, FuncRequest & func)
906 {
907         string lang;
908         string extra;
909         istringstream iss(func.argument);
910         iss >> lang >> extra;
911         if (extra.empty())
912                 extra = "noextra";
913
914 #ifdef WITH_WARNINGS
915 #warning temporarily disabled
916         //if (cur.selection()) {
917         //      MathArray ar;
918         //      selGet(cur.ar);
919         //      lyxerr << "use selection: " << ar << endl;
920         //      insert(pipeThroughExtern(lang, extra, ar));
921         //      return;
922         //}
923 #endif
924
925         MathArray eq;
926         eq.push_back(MathAtom(new MathCharInset('=')));
927
928         // go to first item in line
929         cur.idx() -= cur.idx() % ncols();
930         cur.pos() = 0;
931
932         if (getType() == "simple") {
933                 size_type pos = cur.cell().find_last(eq);
934                 MathArray ar;
935                 if (cur.inMathed() && cur.selection()) {
936                         asArray(grabAndEraseSelection(cur), ar);
937                 } else if (pos == cur.cell().size()) {
938                         ar = cur.cell();
939                         lyxerr << "use whole cell: " << ar << endl;
940                 } else {
941                         ar = MathArray(cur.cell().begin() + pos + 1, cur.cell().end());
942                         lyxerr << "use partial cell form pos: " << pos << endl;
943                 }
944                 cur.cell().append(eq);
945                 cur.cell().append(pipeThroughExtern(lang, extra, ar));
946                 cur.pos() = cur.lastpos();
947                 return;
948         }
949
950         if (getType() == "equation") {
951                 lyxerr << "use equation inset" << endl;
952                 mutate("eqnarray");
953                 MathArray & ar = cur.cell();
954                 lyxerr << "use cell: " << ar << endl;
955                 ++cur.idx();
956                 cur.cell() = eq;
957                 ++cur.idx();
958                 cur.cell() = pipeThroughExtern(lang, extra, ar);
959                 // move to end of line
960                 cur.pos() = cur.lastpos();
961                 return;
962         }
963
964         {
965                 lyxerr << "use eqnarray" << endl;
966                 cur.idx() += 2 - cur.idx() % ncols();
967                 cur.pos() = 0;
968                 MathArray ar = cur.cell();
969                 lyxerr << "use cell: " << ar << endl;
970 #ifdef WITH_WARNINGS
971 #warning temporarily disabled
972 #endif
973                 addRow(cur.row());
974                 ++cur.idx();
975                 ++cur.idx();
976                 cur.cell() = eq;
977                 ++cur.idx();
978                 cur.cell() = pipeThroughExtern(lang, extra, ar);
979                 cur.pos() = cur.lastpos();
980         }
981 }
982
983
984 void MathHullInset::doDispatch(LCursor & cur, FuncRequest & cmd)
985 {
986         lyxerr << "action: " << cmd.action << endl;
987         switch (cmd.action) {
988
989         case LFUN_FINISHED_LEFT:
990         case LFUN_FINISHED_RIGHT:
991         case LFUN_FINISHED_UP:
992         case LFUN_FINISHED_DOWN:
993                 lyxerr << "action: " << cmd.action << endl;
994                 MathGridInset::doDispatch(cur, cmd);
995                 notifyCursorLeaves(cur);
996                 cur.undispatched();
997                 break;
998
999         case LFUN_BREAKPARAGRAPH:
1000                 // just swallow this
1001                 break;
1002
1003         case LFUN_BREAKLINE:
1004                 // some magic for the common case
1005                 if (type_ == "simple" || type_ == "equation") {
1006                         recordUndoInset(cur);
1007                         bool const align =
1008                                 cur.bv().buffer()->params().use_amsmath == BufferParams::AMS_ON;
1009                         mutate(align ? "align" : "eqnarray");
1010                         cur.idx() = 0;
1011                         cur.pos() = cur.lastpos();
1012                 }
1013                 MathGridInset::doDispatch(cur, cmd);
1014                 break;
1015
1016         case LFUN_MATH_NUMBER:
1017                 //lyxerr << "toggling all numbers" << endl;
1018                 if (display()) {
1019                         recordUndoInset(cur);
1020                         bool old = numberedType();
1021                         if (type_ == "multline")
1022                                 numbered(nrows() - 1, !old);
1023                         else
1024                                 for (row_type row = 0; row < nrows(); ++row)
1025                                         numbered(row, !old);
1026                         cur.message(old ? _("No number") : _("Number"));
1027                 }
1028                 break;
1029
1030         case LFUN_MATH_NONUMBER:
1031                 if (display()) {
1032                         recordUndoInset(cur);
1033                         row_type r = (type_ == "multline") ? nrows() - 1 : cur.row();
1034                         bool old = numbered(r);
1035                         cur.message(old ? _("No number") : _("Number"));
1036                         numbered(r, !old);
1037                 }
1038                 break;
1039
1040         case LFUN_INSERT_LABEL: {
1041                 recordUndoInset(cur);
1042                 row_type r = (type_ == "multline") ? nrows() - 1 : cur.row();
1043                 string old_label = label(r);
1044                 string const default_label =
1045                         (lyxrc.label_init_length >= 0) ? "eq:" : "";
1046                 string const contents = cmd.argument.empty() ?
1047                         label(r) : cmd.argument;
1048
1049                 InsetCommandParams p("label", contents);
1050                 string const data = InsetCommandMailer::params2string("label", p);
1051
1052                 if (cmd.argument.empty()) {
1053                         cur.bv().owner()->getDialogs().show("label", data, 0);
1054                 } else {
1055                         FuncRequest fr(LFUN_INSET_INSERT, data);
1056                         dispatch(cur, fr);
1057                 }
1058                 break;
1059         }
1060
1061         case LFUN_INSET_INSERT: {
1062                 //lyxerr << "arg: " << cmd.argument << endl;
1063                 string const name = cmd.getArg(0);
1064                 if (name == "label") {
1065                         InsetCommandParams p;
1066                         InsetCommandMailer::string2params(name, cmd.argument, p);
1067                         string str = p.getContents();
1068                         recordUndoInset(cur);
1069                         row_type const r = (type_ == "multline") ? nrows() - 1 : cur.row();
1070                         str = lyx::support::trim(str);
1071                         if (!str.empty())
1072                                 numbered(r, true);
1073                         string old = label(r);
1074                         if (str != old) {
1075                                 cur.bv().buffer()->changeRefsIfUnique(old, str);
1076                                 label(r, str);
1077                         }
1078                 }
1079                 break;
1080         }
1081
1082         case LFUN_MATH_EXTERN:
1083                 recordUndoInset(cur);
1084                 doExtern(cur, cmd);
1085                 break;
1086
1087         case LFUN_MATH_MUTATE: {
1088                 recordUndoInset(cur);
1089                 row_type row = cur.row();
1090                 col_type col = cur.col();
1091                 mutate(cmd.argument);
1092                 cur.idx() = row * ncols() + col;
1093                 if (cur.idx() > cur.lastidx()) {
1094                         cur.idx() = cur.lastidx();
1095                         cur.pos() = cur.lastpos();
1096                 }
1097                 if (cur.pos() > cur.lastpos())
1098                         cur.pos() = cur.lastpos();
1099                 //cur.dispatched(FINISHED);
1100                 break;
1101         }
1102
1103         case LFUN_MATH_DISPLAY: {
1104                 recordUndoInset(cur);
1105                 mutate(type_ == "simple" ? "equation" : "simple");
1106                 cur.idx() = 0;
1107                 cur.pos() = cur.lastpos();
1108                 //cur.dispatched(FINISHED);
1109                 break;
1110         }
1111
1112         default:
1113                 MathGridInset::doDispatch(cur, cmd);
1114                 break;
1115         }
1116 }
1117
1118
1119 bool MathHullInset::getStatus(LCursor & cur, FuncRequest const & cmd,
1120                 FuncStatus & status) const
1121 {
1122         switch (cmd.action) {
1123         case LFUN_FINISHED_LEFT:
1124         case LFUN_FINISHED_RIGHT:
1125         case LFUN_FINISHED_UP:
1126         case LFUN_FINISHED_DOWN:
1127                 status.enabled(true);
1128                 return true;
1129         case LFUN_BREAKLINE:
1130         case LFUN_MATH_NUMBER:
1131         case LFUN_MATH_NONUMBER:
1132         case LFUN_MATH_EXTERN:
1133         case LFUN_MATH_MUTATE:
1134         case LFUN_MATH_DISPLAY:
1135                 // we handle these
1136                 status.enabled(true);
1137                 return true;
1138         case LFUN_INSERT_LABEL:
1139                 status.enabled(type_ != "simple");
1140                 return true;
1141         case LFUN_TABULAR_FEATURE: {
1142                 istringstream is(cmd.argument);
1143                 string s;
1144                 is >> s;
1145                 if (!rowChangeOK()
1146                     && (s == "append-row"
1147                         || s == "delete-row"
1148                         || s == "copy-row")) {
1149                         status.message(bformat(
1150                                 N_("Can't change number of rows in '%1$s'"),
1151                                 type_));
1152                         status.enabled(false);
1153                         return true;
1154                 }
1155                 if (!colChangeOK()
1156                     && (s == "append-column"
1157                         || s == "delete-column"
1158                         || s == "copy-column")) {
1159                         status.message(bformat(
1160                                 N_("Can't change number of columns in '%1$s'"),
1161                                 type_));
1162                         status.enabled(false);
1163                         return true;
1164                 }
1165                 if ((type_ == "simple"
1166                   || type_ == "equation"
1167                   || type_ == "none") &&
1168                     (s == "add-hline-above" || s == "add-hline-below")) {
1169                         status.message(bformat(
1170                                 N_("Can't add horizontal grid lines in '%1$s'"),
1171                                 type_));
1172                         status.enabled(false);
1173                         return true;
1174                 }
1175                 if (s == "add-vline-left" || s == "add-vline-right") {
1176                         status.message(bformat(
1177                                 N_("Can't add vertical grid lines in '%1$s'"),
1178                                 type_));
1179                         status.enabled(false);
1180                         return true;
1181                 }
1182                 if (s == "valign-top" || s == "valign-middle"
1183                  || s == "valign-bottom" || s == "align-left"
1184                  || s == "align-center" || s == "align-right") {
1185                         status.enabled(false);
1186                         return true;
1187                 }
1188                 return MathGridInset::getStatus(cur, cmd, status);
1189         }
1190         default:
1191                 return MathGridInset::getStatus(cur, cmd, status);
1192         }
1193 }
1194
1195
1196 /////////////////////////////////////////////////////////////////////
1197
1198 #include "math_arrayinset.h"
1199 #include "math_deliminset.h"
1200 #include "math_factory.h"
1201 #include "math_parser.h"
1202 #include "math_spaceinset.h"
1203 #include "ref_inset.h"
1204
1205 #include "bufferview_funcs.h"
1206 #include "lyxtext.h"
1207
1208 #include "frontends/LyXView.h"
1209 #include "frontends/Dialogs.h"
1210
1211 #include "support/lyxlib.h"
1212
1213
1214 // simply scrap this function if you want
1215 void MathHullInset::mutateToText()
1216 {
1217 #if 0
1218         // translate to latex
1219         ostringstream os;
1220         latex(NULL, os, false, false);
1221         string str = os.str();
1222
1223         // insert this text
1224         LyXText * lt = view_->getLyXText();
1225         string::const_iterator cit = str.begin();
1226         string::const_iterator end = str.end();
1227         for (; cit != end; ++cit)
1228                 view_->owner()->getIntl()->getTransManager().TranslateAndInsert(*cit, lt);
1229
1230         // remove ourselves
1231         //view_->owner()->dispatch(LFUN_ESCAPE);
1232 #endif
1233 }
1234
1235
1236 void MathHullInset::handleFont(LCursor & cur, string const & arg,
1237         string const & font)
1238 {
1239         // this whole function is a hack and won't work for incremental font
1240         // changes...
1241         recordUndo(cur);
1242         if (cur.inset().asMathInset()->name() == font)
1243                 cur.handleFont(font);
1244         else {
1245                 cur.handleNest(createMathInset(font));
1246                 cur.insert(arg);
1247         }
1248 }
1249
1250
1251 void MathHullInset::handleFont2(LCursor & cur, string const & arg)
1252 {
1253         recordUndo(cur);
1254         LyXFont font;
1255         bool b;
1256         bv_funcs::string2font(arg, font, b);
1257         if (font.color() != LColor::inherit) {
1258                 MathAtom at = MathAtom(new MathColorInset(true, font.color()));
1259                 cur.handleNest(at, 0);
1260         }
1261 }
1262
1263
1264 void MathHullInset::edit(LCursor & cur, bool left)
1265 {
1266         cur.push(*this);
1267         left ? idxFirst(cur) : idxLast(cur);
1268 }
1269
1270
1271 string const MathHullInset::editMessage() const
1272 {
1273         return _("Math editor mode");
1274 }
1275
1276
1277 void MathHullInset::revealCodes(LCursor & cur) const
1278 {
1279         if (!cur.inMathed())
1280                 return;
1281         ostringstream os;
1282         cur.info(os);
1283         cur.message(os.str());
1284 /*
1285         // write something to the minibuffer
1286         // translate to latex
1287         cur.markInsert(bv);
1288         ostringstream os;
1289         write(NULL, os);
1290         string str = os.str();
1291         cur.markErase(bv);
1292         string::size_type pos = 0;
1293         string res;
1294         for (string::iterator it = str.begin(); it != str.end(); ++it) {
1295                 if (*it == '\n')
1296                         res += ' ';
1297                 else if (*it == '\0') {
1298                         res += "  -X-  ";
1299                         pos = it - str.begin();
1300                 }
1301                 else
1302                         res += *it;
1303         }
1304         if (pos > 30)
1305                 res = res.substr(pos - 30);
1306         if (res.size() > 60)
1307                 res = res.substr(0, 60);
1308         cur.message(res);
1309 */
1310 }
1311
1312
1313 InsetBase::Code MathHullInset::lyxCode() const
1314 {
1315         return MATH_CODE;
1316 }
1317
1318
1319 /////////////////////////////////////////////////////////////////////
1320
1321
1322 #if 0
1323 bool MathHullInset::searchForward(BufferView * bv, string const & str,
1324                                      bool, bool)
1325 {
1326 #ifdef WITH_WARNINGS
1327 #warning completely broken
1328 #endif
1329         static MathHullInset * lastformula = 0;
1330         static CursorBase current = DocIterator(ibegin(nucleus()));
1331         static MathArray ar;
1332         static string laststr;
1333
1334         if (lastformula != this || laststr != str) {
1335                 //lyxerr << "reset lastformula to " << this << endl;
1336                 lastformula = this;
1337                 laststr = str;
1338                 current = ibegin(nucleus());
1339                 ar.clear();
1340                 mathed_parse_cell(ar, str);
1341         } else {
1342                 increment(current);
1343         }
1344         //lyxerr << "searching '" << str << "' in " << this << ar << endl;
1345
1346         for (DocIterator it = current; it != iend(nucleus()); increment(it)) {
1347                 CursorSlice & top = it.back();
1348                 MathArray const & a = top.asMathInset()->cell(top.idx_);
1349                 if (a.matchpart(ar, top.pos_)) {
1350                         bv->cursor().setSelection(it, ar.size());
1351                         current = it;
1352                         top.pos_ += ar.size();
1353                         bv->update();
1354                         return true;
1355                 }
1356         }
1357
1358         //lyxerr << "not found!" << endl;
1359         lastformula = 0;
1360         return false;
1361 }
1362 #endif
1363
1364
1365 void MathHullInset::write(Buffer const &, std::ostream & os) const
1366 {
1367         WriteStream wi(os, false, false);
1368         os << "Formula ";
1369         write(wi);
1370 }
1371
1372
1373 void MathHullInset::read(Buffer const &, LyXLex & lex)
1374 {
1375         MathAtom at;
1376         mathed_parse_normal(at, lex);
1377         operator=(*at->asHullInset());
1378 }
1379
1380
1381 int MathHullInset::plaintext(Buffer const &, ostream & os,
1382                         OutputParams const &) const
1383 {
1384         if (0 && display()) {
1385                 Dimension dim;
1386                 TextMetricsInfo mi;
1387                 metricsT(mi, dim);
1388                 TextPainter tpain(dim.width(), dim.height());
1389                 drawT(tpain, 0, dim.ascent());
1390                 tpain.show(os, 3);
1391                 // reset metrics cache to "real" values
1392                 //metrics();
1393                 return tpain.textheight();
1394         } else {
1395                 WriteStream wi(os, false, true);
1396                 wi << cell(0);
1397                 return wi.line();
1398         }
1399 }
1400
1401
1402 int MathHullInset::linuxdoc(Buffer const & buf, ostream & os,
1403                            OutputParams const & runparams) const
1404 {
1405         return docbook(buf, os, runparams);
1406 }
1407
1408
1409 int MathHullInset::docbook(Buffer const & buf, ostream & os,
1410                           OutputParams const & runparams) const
1411 {
1412         MathMLStream ms(os);
1413         int res = 0;
1414         string name;
1415         if (getType() == "simple")
1416                 name= "inlineequation";
1417         else
1418                 name = "informalequation";
1419
1420         string bname = name;
1421         if (!label(0).empty())
1422                 bname += " id=\"" + sgml::cleanID(buf, runparams, label(0)) + "\"";
1423         ms << MTag(bname.c_str());
1424
1425         ostringstream ls;
1426         if (runparams.flavor == OutputParams::XML) {
1427                 ms << MTag("alt role=\"tex\" ");
1428                 // Workaround for db2latex: db2latex always includes equations with
1429                 // \ensuremath{} or \begin{display}\end{display}
1430                 // so we strip LyX' math environment
1431                 WriteStream wi(ls, false, false);
1432                 MathGridInset::write(wi);
1433                 ms << subst(subst(ls.str(), "&", "&amp;"), "<", "&lt;");
1434                 ms << ETag("alt");
1435                 ms << MTag("math");
1436                 MathGridInset::mathmlize(ms);
1437                 ms << ETag("math");
1438         } else {
1439                 ms << MTag("alt role=\"tex\"");
1440                 res = latex(buf, ls, runparams);
1441                 ms << subst(subst(ls.str(), "&", "&amp;"), "<", "&lt;");
1442                 ms << ETag("alt");
1443         }
1444
1445         ms <<  "<graphic fileref=\"eqn/";
1446         if ( !label(0).empty())
1447                 ms << sgml::cleanID(buf, runparams, label(0));
1448         else
1449                 ms << sgml::uniqueID("anon");
1450
1451         if (runparams.flavor == OutputParams::XML)
1452                 ms << "\"/>";
1453         else
1454                 ms << "\">";
1455
1456         ms << ETag(name.c_str());
1457         return ms.line() + res;
1458 }