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