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