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