]> git.lyx.org Git - features.git/blob - src/mathed/math_hullinset.C
4926e1456eeaf65ee99c3adcc6538477eac9a3bc
[features.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_hullinset.h"
14 #include "math_mathmlstream.h"
15 #include "math_streamstr.h"
16 #include "math_cursor.h"
17 #include "math_support.h"
18 #include "math_extern.h"
19 #include "math_charinset.h"
20 #include "textpainter.h"
21 #include "dispatchresult.h"
22 #include "debug.h"
23 #include "funcrequest.h"
24 #include "gettext.h"
25 #include "LaTeXFeatures.h"
26 #include "LColor.h"
27 #include "lyxrc.h"
28
29 #include "frontends/Alert.h"
30
31 #include "support/std_sstream.h"
32
33
34 using lyx::support::trim;
35
36 using std::endl;
37 using std::max;
38
39 using std::string;
40 using std::auto_ptr;
41 using std::istringstream;
42 using std::ostringstream;
43 using std::pair;
44
45
46 namespace {
47
48         int getCols(string const & type)
49         {
50                 if (type == "eqnarray")
51                         return 3;
52                 if (type == "align")
53                         return 2;
54                 if (type == "flalign")
55                         return 2;
56                 if (type == "alignat")
57                         return 2;
58                 if (type == "xalignat")
59                         return 2;
60                 if (type == "xxalignat")
61                         return 2;
62                 return 1;
63         }
64
65
66         // returns position of first relation operator in the array
67         // used for "intelligent splitting"
68         MathArray::size_type firstRelOp(MathArray const & ar)
69         {
70                 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
71                         if ((*it)->isRelOp())
72                                 return it - ar.begin();
73                 return ar.size();
74         }
75
76
77         char const * star(bool numbered)
78         {
79                 return numbered ? "" : "*";
80         }
81
82
83         int typecode(string const & s)
84         {
85                 if (s == "none")      return 0;
86                 if (s == "simple")    return 1;
87                 if (s == "equation")  return 2;
88                 if (s == "eqnarray")  return 3;
89                 if (s == "align")     return 4;
90                 if (s == "alignat")   return 5;
91                 if (s == "xalignat")  return 6;
92                 if (s == "xxalignat") return 7;
93                 if (s == "multline")  return 8;
94                 if (s == "gather")    return 9;
95                 if (s == "flalign")   return 10;
96                 lyxerr << "unknown hull type '" << s << "'" << endl;
97                 return 0;
98         }
99
100         bool smaller(string const & s, string const & t)
101         {
102                 return typecode(s) < typecode(t);
103         }
104
105
106 } // end anon namespace
107
108
109 MathHullInset::MathHullInset()
110         : MathGridInset(1, 1), type_("none"), nonum_(1), label_(1)
111 {
112         setDefaults();
113 }
114
115
116 MathHullInset::MathHullInset(string const & type)
117         : MathGridInset(getCols(type), 1), type_(type), nonum_(1), label_(1)
118 {
119         setDefaults();
120 }
121
122
123 auto_ptr<InsetBase> MathHullInset::clone() const
124 {
125         return auto_ptr<InsetBase>(new MathHullInset(*this));
126 }
127
128
129 MathInset::mode_type MathHullInset::currentMode() const
130 {
131         if (type_ == "none")
132                 return UNDECIDED_MODE;
133         // definitely math mode ...
134         return MATH_MODE;
135 }
136
137
138 bool MathHullInset::idxFirst(BufferView & bv) const
139 {
140         CursorSlice & cur = cursorTip(bv);
141         cur.idx() = 0;
142         cur.pos() = 0;
143         return true;
144 }
145
146
147 bool MathHullInset::idxLast(BufferView & bv) const
148 {
149         CursorSlice & cur = cursorTip(bv);
150         cur.idx() = nargs() - 1;
151         cur.pos() = cur.lastpos();
152         return true;
153 }
154
155
156 char MathHullInset::defaultColAlign(col_type col)
157 {
158         if (type_ == "eqnarray")
159                 return "rcl"[col];
160         if (typecode(type_) >= typecode("align"))
161                 return "rl"[col & 1];
162         return 'c';
163 }
164
165
166 int MathHullInset::defaultColSpace(col_type col)
167 {
168         if (type_ == "align" || type_ == "alignat")
169                 return 0;
170         if (type_ == "xalignat")
171                 return (col & 1) ? 20 : 0;
172         if (type_ == "xxalignat" || type_ == "flalign")
173                 return (col & 1) ? 40 : 0;
174         return 0;
175 }
176
177
178 char const * MathHullInset::standardFont() const
179 {
180         if (type_ == "none")
181                 return "lyxnochange";
182         return "mathnormal";
183 }
184
185
186 void MathHullInset::metrics(MetricsInfo & mi, Dimension & dim) const
187 {
188         FontSetChanger dummy1(mi.base, standardFont());
189         StyleChanger dummy2(mi.base, display() ? LM_ST_DISPLAY : LM_ST_TEXT);
190
191         // let the cells adjust themselves
192         MathGridInset::metrics(mi);
193
194         if (display()) {
195                 dim_.asc += 12;
196                 dim_.des += 12;
197         }
198
199         if (numberedType()) {
200                 FontSetChanger dummy(mi.base, "mathbf");
201                 int l = 0;
202                 for (row_type row = 0; row < nrows(); ++row)
203                         l = max(l, mathed_string_width(mi.base.font, nicelabel(row)));
204
205                 if (l)
206                         dim_.wid += 30 + l;
207         }
208
209         // make it at least as high as the current font
210         int asc = 0;
211         int des = 0;
212         math_font_max_dim(mi.base.font, asc, des);
213         dim_.asc = max(dim_.asc, asc);
214         dim_.des = max(dim_.des, des);
215
216         // for markers
217         metricsMarkers2();
218         dim = dim_;
219 }
220
221
222 void MathHullInset::draw(PainterInfo & pi, int x, int y) const
223 {
224         FontSetChanger dummy1(pi.base, standardFont());
225         StyleChanger dummy2(pi.base, display() ? LM_ST_DISPLAY : LM_ST_TEXT);
226         MathGridInset::draw(pi, x + 1, y);
227
228         if (numberedType()) {
229                 int const xx = x + colinfo_.back().offset_ + colinfo_.back().width_ + 20;
230                 for (row_type row = 0; row < nrows(); ++row) {
231                         int const yy = y + rowinfo_[row].offset_;
232                         FontSetChanger dummy(pi.base, "mathrm");
233                         drawStr(pi, pi.base.font, xx, yy, nicelabel(row));
234                 }
235         }
236
237         drawMarkers2(pi, x, y);
238 }
239
240
241 void MathHullInset::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
242 {
243         if (display()) {
244                 MathGridInset::metricsT(mi, dim);
245         } else {
246                 ostringstream os;
247                 WriteStream wi(os, false, true);
248                 write(wi);
249                 dim.wid = os.str().size();
250                 dim.asc = 1;
251                 dim.des = 0;
252         }
253 }
254
255
256 void MathHullInset::drawT(TextPainter & pain, int x, int y) const
257 {
258         if (display()) {
259                 MathGridInset::drawT(pain, x, y);
260         } else {
261                 ostringstream os;
262                 WriteStream wi(os, false, true);
263                 write(wi);
264                 pain.draw(x, y, os.str().c_str());
265         }
266 }
267
268
269 string MathHullInset::label(row_type row) const
270 {
271         row_type n = nrows();
272         BOOST_ASSERT(row < n);
273         return label_[row];
274 }
275
276
277 void MathHullInset::label(row_type row, string const & label)
278 {
279         //lyxerr << "setting label '" << label << "' for row " << row << endl;
280         label_[row] = label;
281 }
282
283
284 void MathHullInset::numbered(row_type row, bool num)
285 {
286         nonum_[row] = !num;
287 }
288
289
290 bool MathHullInset::numbered(row_type row) const
291 {
292         return !nonum_[row];
293 }
294
295
296 bool MathHullInset::ams() const
297 {
298         return
299                 type_ == "align" ||
300                 type_ == "flalign" ||
301                 type_ == "multline" ||
302                 type_ == "gather" ||
303                 type_ == "alignat" ||
304                 type_ == "xalignat" ||
305                 type_ == "xxalignat";
306 }
307
308
309 bool MathHullInset::display() const
310 {
311         return type_ != "simple" && type_ != "none";
312 }
313
314
315 void MathHullInset::getLabelList(Buffer const &,
316                                  std::vector<string> & labels) const
317 {
318         for (row_type row = 0; row < nrows(); ++row)
319                 if (!label_[row].empty() && nonum_[row] != 1)
320                         labels.push_back(label_[row]);
321 }
322
323
324 bool MathHullInset::numberedType() const
325 {
326         if (type_ == "none")
327                 return false;
328         if (type_ == "simple")
329                 return false;
330         if (type_ == "xxalignat")
331                 return false;
332         for (row_type row = 0; row < nrows(); ++row)
333                 if (!nonum_[row])
334                         return true;
335         return false;
336 }
337
338
339 void MathHullInset::validate(LaTeXFeatures & features) const
340 {
341         if (ams())
342                 features.require("amsmath");
343
344
345         // Validation is necessary only if not using AMS math.
346         // To be safe, we will always run mathedvalidate.
347         //if (features.amsstyle)
348         //  return;
349
350         features.require("boldsymbol");
351         //features.binom      = true;
352
353         MathGridInset::validate(features);
354 }
355
356
357 void MathHullInset::header_write(WriteStream & os) const
358 {
359         bool n = numberedType();
360
361         if (type_ == "none")
362                 ;
363
364         else if (type_ == "simple") {
365                 os << '$';
366                 if (cell(0).empty())
367                         os << ' ';
368         }
369
370         else if (type_ == "equation") {
371                 if (n)
372                         os << "\\begin{equation" << star(n) << "}\n";
373                 else
374                         os << "\\[\n";
375         }
376
377         else if (type_ == "eqnarray" || type_ == "align" || type_ == "flalign"
378                  || type_ == "gather" || type_ == "multline")
379                         os << "\\begin{" << type_ << star(n) << "}\n";
380
381         else if (type_ == "alignat" || type_ == "xalignat")
382                 os << "\\begin{" << type_ << star(n) << '}'
383                   << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
384
385         else if (type_ == "xxalignat")
386                 os << "\\begin{" << type_ << '}'
387                   << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
388
389         else
390                 os << "\\begin{unknown" << star(n) << '}';
391 }
392
393
394 void MathHullInset::footer_write(WriteStream & os) const
395 {
396         bool n = numberedType();
397
398         if (type_ == "none")
399                 os << "\n";
400
401         else if (type_ == "simple")
402                 os << '$';
403
404         else if (type_ == "equation")
405                 if (n)
406                         os << "\\end{equation" << star(n) << "}\n";
407                 else
408                         os << "\\]\n";
409
410         else if (type_ == "eqnarray" || type_ == "align" || type_ == "flalign"
411                  || type_ == "alignat" || type_ == "xalignat"
412                  || type_ == "gather" || type_ == "multline")
413                 os << "\\end{" << type_ << star(n) << "}\n";
414
415         else if (type_ == "xxalignat")
416                 os << "\\end{" << type_ << "}\n";
417
418         else
419                 os << "\\end{unknown" << star(n) << '}';
420 }
421
422
423 bool MathHullInset::colChangeOK() const
424 {
425         return
426                 type_ == "align" || type_ == "flalign" ||type_ == "alignat" ||
427                 type_ == "xalignat" || type_ == "xxalignat";
428 }
429
430
431 void MathHullInset::addRow(row_type row)
432 {
433         nonum_.insert(nonum_.begin() + row + 1, !numberedType());
434         label_.insert(label_.begin() + row + 1, string());
435         MathGridInset::addRow(row);
436 }
437
438
439 void MathHullInset::swapRow(row_type row)
440 {
441         if (nrows() == 1)
442                 return;
443         if (row + 1 == nrows())
444                 --row;
445         std::swap(nonum_[row], nonum_[row + 1]);
446         std::swap(label_[row], label_[row + 1]);
447         MathGridInset::swapRow(row);
448 }
449
450
451 void MathHullInset::delRow(row_type row)
452 {
453         if (nrows() <= 1)
454                 return;
455         MathGridInset::delRow(row);
456         nonum_.erase(nonum_.begin() + row);
457         label_.erase(label_.begin() + row);
458 }
459
460
461 void MathHullInset::addCol(col_type col)
462 {
463         if (colChangeOK())
464                 MathGridInset::addCol(col);
465         else
466                 lyxerr << "Can't change number of columns in '" << type_ << "'" << endl;
467 }
468
469
470 void MathHullInset::delCol(col_type col)
471 {
472         if (colChangeOK())
473                 MathGridInset::delCol(col);
474         else
475                 lyxerr << "Can't change number of columns in '" << type_ << "'" << endl;
476 }
477
478
479 string MathHullInset::nicelabel(row_type row) const
480 {
481         if (nonum_[row])
482                 return string();
483         if (label_[row].empty())
484                 return string("(#)");
485         return '(' + label_[row] + ')';
486 }
487
488
489 void MathHullInset::glueall()
490 {
491         MathArray ar;
492         for (idx_type i = 0; i < nargs(); ++i)
493                 ar.append(cell(i));
494         *this = MathHullInset("simple");
495         cell(0) = ar;
496         setDefaults();
497 }
498
499
500 string const & MathHullInset::getType() const
501 {
502         return type_;
503 }
504
505
506 void MathHullInset::setType(string const & type)
507 {
508         type_ = type;
509         setDefaults();
510 }
511
512
513
514 void MathHullInset::mutate(string const & newtype)
515 {
516         lyxerr << "mutating from '" << type_ << "' to '" << newtype << "'" << endl;
517
518         // we try to move along the chain
519         // none <-> simple <-> equation <-> eqnarray
520
521         if (newtype == "dump") {
522                 dump();
523         }
524
525         else if (newtype == type_) {
526                 // done
527         }
528
529         else if (type_ == "none") {
530                 setType("simple");
531                 numbered(0, false);
532                 mutate(newtype);
533         }
534
535         else if (type_ == "simple") {
536                 if (newtype == "none") {
537                         setType("none");
538                 } else {
539                         setType("equation");
540                         numbered(0, false);
541                         mutate(newtype);
542                 }
543         }
544
545         else if (type_ == "equation") {
546                 if (smaller(newtype, type_)) {
547                         setType("simple");
548                         mutate(newtype);
549                 } else if (newtype == "eqnarray") {
550                         MathGridInset::addCol(1);
551                         MathGridInset::addCol(1);
552
553                         // split it "nicely" on the firest relop
554                         pos_type pos = firstRelOp(cell(0));
555                         cell(1) = MathArray(cell(0).begin() + pos, cell(0).end());
556                         cell(0).erase(pos, cell(0).size());
557
558                         if (cell(1).size()) {
559                                 cell(2) = MathArray(cell(1).begin() + 1, cell(1).end());
560                                 cell(1).erase(1, cell(1).size());
561                         }
562                         setType("eqnarray");
563                         mutate(newtype);
564                 } else if (newtype == "multline" || newtype == "gather") {
565                         setType(newtype);
566                         numbered(0, false);
567                 } else {
568                         MathGridInset::addCol(1);
569                         // split it "nicely"
570                         pos_type pos = firstRelOp(cell(0));
571                         cell(1) = cell(0);
572                         cell(0).erase(pos, cell(0).size());
573                         cell(1).erase(0, pos);
574                         setType("align");
575                         mutate(newtype);
576                 }
577         }
578
579         else if (type_ == "eqnarray") {
580                 if (smaller(newtype, type_)) {
581                         // set correct (no)numbering
582                         bool allnonum = true;
583                         for (row_type row = 0; row < nrows(); ++row)
584                                 if (!nonum_[row])
585                                         allnonum = false;
586
587                         // set first non-empty label
588                         string label;
589                         for (row_type row = 0; row < nrows(); ++row) {
590                                 if (!label_[row].empty()) {
591                                         label = label_[row];
592                                         break;
593                                 }
594                         }
595
596                         glueall();
597                         nonum_[0] = allnonum;
598                         label_[0] = label;
599                         mutate(newtype);
600                 } else { // align & Co.
601                         for (row_type row = 0; row < nrows(); ++row) {
602                                 idx_type c = 3 * row + 1;
603                                 cell(c).append(cell(c + 1));
604                         }
605                         MathGridInset::delCol(2);
606                         setType("align");
607                         mutate(newtype);
608                 }
609         }
610
611         else if (type_ == "align") {
612                 if (smaller(newtype, type_)) {
613                         MathGridInset::addCol(1);
614                         setType("eqnarray");
615                         mutate(newtype);
616                 } else {
617                         setType(newtype);
618                 }
619         }
620
621         else if (type_ == "multline") {
622                 if (newtype == "gather" || newtype == "align" ||
623                     newtype == "xalignat" || newtype == "xxalignat" || newtype == "flalign")
624                         setType(newtype);
625                 else if (newtype == "eqnarray") {
626                         MathGridInset::addCol(1);
627                         MathGridInset::addCol(1);
628                         setType("eqnarray");
629                 } else {
630                         lyxerr << "mutation from '" << type_
631                                 << "' to '" << newtype << "' not implemented" << endl;
632                 }
633         }
634
635         else if (type_ == "gather") {
636                 if (newtype == "multline") {
637                         setType("multline");
638                 } else {
639                         lyxerr << "mutation from '" << type_
640                                 << "' to '" << newtype << "' not implemented" << endl;
641                 }
642         }
643
644         else {
645                 lyxerr << "mutation from '" << type_
646                                          << "' to '" << newtype << "' not implemented" << endl;
647         }
648 }
649
650
651 string MathHullInset::eolString(row_type row, bool fragile) const
652 {
653         string res;
654         if (numberedType()) {
655                 if (!label_[row].empty() && !nonum_[row])
656                         res += "\\label{" + label_[row] + '}';
657                 if (nonum_[row] && (type_ != "multline"))
658                         res += "\\nonumber ";
659         }
660         return res + MathGridInset::eolString(row, fragile);
661 }
662
663
664 void MathHullInset::write(WriteStream & os) const
665 {
666         header_write(os);
667         MathGridInset::write(os);
668         footer_write(os);
669 }
670
671
672 void MathHullInset::normalize(NormalStream & os) const
673 {
674         os << "[formula " << type_ << ' ';
675         MathGridInset::normalize(os);
676         os << "] ";
677 }
678
679
680 void MathHullInset::mathmlize(MathMLStream & os) const
681 {
682         MathGridInset::mathmlize(os);
683 }
684
685
686 void MathHullInset::infoize(std::ostream & os) const
687 {
688         os << "Type: " << type_;
689 }
690
691
692 void MathHullInset::check() const
693 {
694         BOOST_ASSERT(nonum_.size() == nrows());
695         BOOST_ASSERT(label_.size() == nrows());
696 }
697
698
699 void MathHullInset::doExtern(FuncRequest const & func, BufferView & bv)
700 {
701         CursorSlice & cur = cursorTip(bv);
702         string lang;
703         string extra;
704         istringstream iss(func.argument.c_str());
705         iss >> lang >> extra;
706         if (extra.empty())
707                 extra = "noextra";
708
709 #ifdef WITH_WARNINGS
710 #warning temporarily disabled
711         //if (selection()) {
712         //      MathArray ar;
713         //      selGet(ar);
714         //      lyxerr << "use selection: " << ar << endl;
715         //      insert(pipeThroughExtern(lang, extra, ar));
716         //      return;
717         //}
718 #endif
719
720         MathArray eq;
721         eq.push_back(MathAtom(new MathCharInset('=')));
722
723         // go to first item in line
724         cur.idx() -= cur.idx() % ncols();
725         cur.pos() = 0;
726
727         if (getType() == "simple") {
728                 size_type pos = cur.cell().find_last(eq);
729                 MathArray ar;
730                 if (mathcursor && mathcursor->selection()) {
731                         asArray(mathcursor->grabAndEraseSelection(bv), ar);
732                 } else if (pos == cur.cell().size()) {
733                         ar = cur.cell();
734                         lyxerr << "use whole cell: " << ar << endl;
735                 } else {
736                         ar = MathArray(cur.cell().begin() + pos + 1, cur.cell().end());
737                         lyxerr << "use partial cell form pos: " << pos << endl;
738                 }
739                 cur.cell().append(eq);
740                 cur.cell().append(pipeThroughExtern(lang, extra, ar));
741                 cur.pos() = cur.lastpos();
742                 return;
743         }
744
745         if (getType() == "equation") {
746                 lyxerr << "use equation inset" << endl;
747                 mutate("eqnarray");
748                 MathArray & ar = cur.cell();
749                 lyxerr << "use cell: " << ar << endl;
750                 ++cur.idx();
751                 cur.cell() = eq;
752                 ++cur.idx();
753                 cur.cell() = pipeThroughExtern(lang, extra, ar);
754                 // move to end of line
755                 cur.pos() = cur.lastpos();
756                 return;
757         }
758
759         {
760                 lyxerr << "use eqnarray" << endl;
761                 cur.idx() += 2 - cur.idx() % ncols();
762                 cur.pos() = 0;
763                 MathArray ar = cur.cell();
764                 lyxerr << "use cell: " << ar << endl;
765 #ifdef WITH_WARNINGS
766 #warning temporarily disabled
767 #endif
768                 addRow(cur.row());
769                 ++cur.idx();
770                 ++cur.idx();
771                 cur.cell() = eq;
772                 ++cur.idx();
773                 cur.cell() = pipeThroughExtern(lang, extra, ar);
774                 cur.pos() = cur.lastpos();
775         }
776 }
777
778
779 DispatchResult
780 MathHullInset::priv_dispatch(BufferView & bv, FuncRequest const & cmd)
781 {
782         CursorSlice & cur = cursorTip(bv);
783         switch (cmd.action) {
784
785                 case LFUN_BREAKLINE:
786                         if (type_ == "simple" || type_ == "equation") {
787                                 mutate("eqnarray");
788                                 cur.idx() = 1;
789                                 cur.pos() = 0;
790                                 return DispatchResult(true, FINISHED);
791                         }
792                         return MathGridInset::priv_dispatch(bv, cmd);
793
794                 case LFUN_MATH_NUMBER:
795                         //lyxerr << "toggling all numbers" << endl;
796                         if (display()) {
797                                 //recordUndo(bv, Undo::INSERT);
798                                 bool old = numberedType();
799                                 if (type_ == "multline")
800                                         numbered(nrows() - 1, !old);
801                                 else
802                                         for (row_type row = 0; row < nrows(); ++row)
803                                                 numbered(row, !old);
804                                 //bv->owner()->message(old ? _("No number") : _("Number"));
805                         }
806                         return DispatchResult(true, true);
807
808                 case LFUN_MATH_NONUMBER:
809                         if (display()) {
810                                 row_type r = (type_ == "multline") ? nrows() - 1 : cur.row();
811                                 //recordUndo(bv, Undo::INSERT);
812                                 bool old = numbered(r);
813                                 //bv->owner()->message(old ? _("No number") : _("Number"));
814                                 numbered(r, !old);
815                         }
816                         return DispatchResult(true, true);
817
818                 case LFUN_INSERT_LABEL: {
819                         row_type r = (type_ == "multline") ? nrows() - 1 : cur.row();
820                         string old_label = label(r);
821                         string new_label = cmd.argument;
822
823                         if (new_label.empty()) {
824                                 string const default_label =
825                                         (lyxrc.label_init_length >= 0) ? "eq:" : "";
826                                 pair<bool, string> const res = old_label.empty()
827                                         ? Alert::askForText(_("Enter new label to insert:"), default_label)
828                                         : Alert::askForText(_("Enter label:"), old_label);
829                                 if (!res.first)
830                                         return DispatchResult(false);
831                                 new_label = trim(res.second);
832                         }
833
834                         //if (new_label == old_label)
835                         //      break;  // Nothing to do
836
837                         if (!new_label.empty())
838                                 numbered(r, true);
839                         label(r, new_label);
840                         return DispatchResult(true, true);
841                 }
842
843                 case LFUN_MATH_EXTERN:
844                         doExtern(cmd, bv);
845                         return DispatchResult(true, FINISHED);
846
847                 case LFUN_MATH_MUTATE: {
848                         lyxerr << "Hull: MUTATE: " << cmd.argument << endl;
849                         row_type r = cur.row();
850                         col_type c = cur.col();
851                         mutate(cmd.argument);
852                         cur.idx() = r * ncols() + c;
853                         if (cur.idx() >= nargs())
854                                 cur.idx() = nargs() - 1;
855                         if (cur.pos() > cur.lastpos())
856                                 cur.pos() = cur.lastpos();
857                         return DispatchResult(true, FINISHED);
858                 }
859
860                 case LFUN_MATH_DISPLAY: {
861                         mutate(type_ == "simple" ? "equation" : "simple");
862                         cur.idx() = 0;
863                         cur.pos() = cur.lastpos();
864                         return DispatchResult(true, FINISHED);
865                 }
866
867                 default:
868                         return MathGridInset::priv_dispatch(bv, cmd);
869         }
870 }
871
872
873 string MathHullInset::fileInsetLabel() const
874 {
875         return "Formula";
876 }