]> git.lyx.org Git - features.git/blob - src/mathed/MathMacroTemplate.cpp
* proper labels under the holes in macro templates during editing
[features.git] / src / mathed / MathMacroTemplate.cpp
1 /**
2  * \file MathMacroTemplate.cpp
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 "MathMacroTemplate.h"
14
15 #include "DocIterator.h"
16 #include "InsetMathBrace.h"
17 #include "InsetMathChar.h"
18 #include "InsetMathSqrt.h"
19 #include "MathMacro.h"
20 #include "MathMacroArgument.h"
21 #include "MathStream.h"
22 #include "MathParser.h"
23 #include "MathSupport.h"
24 #include "MathMacroArgument.h"
25
26 #include "Buffer.h"
27 #include "BufferView.h"
28 #include "Color.h"
29 #include "Cursor.h"
30 #include "support/debug.h"
31 #include "DispatchResult.h"
32 #include "FuncRequest.h"
33 #include "FuncStatus.h"
34 #include "support/gettext.h"
35 #include "Lexer.h"
36 #include "Undo.h"
37
38 #include "frontends/FontMetrics.h"
39 #include "frontends/Painter.h"
40
41 #include "support/convert.h"
42 #include "support/docstream.h"
43 #include "support/lstrings.h"
44
45 #include "support/debug.h"
46
47 #include <sstream>
48
49 using namespace std;
50
51 namespace lyx {
52
53 using support::bformat;
54
55 //////////////////////////////////////////////////////////////////////
56
57 class InsetLabelBox : public InsetMathNest {
58 public:
59         ///
60         InsetLabelBox(MathAtom const & atom, docstring label, 
61                                                                 MathMacroTemplate const & parent, bool frame = false);
62         InsetLabelBox(docstring label, MathMacroTemplate const & parent, 
63                                                                 bool frame = false);
64         ///
65         void metrics(MetricsInfo & mi, Dimension & dim) const;
66         ///
67         void draw(PainterInfo &, int x, int y) const;
68         
69 private:
70         ///
71         MathMacroTemplate const & parent_;
72         ///
73         Inset * clone() const;
74         ///
75         docstring const label_;
76         ///
77         bool frame_;
78         ///
79         mutable Dimension cellDim_;
80 };
81
82
83 InsetLabelBox::InsetLabelBox(MathAtom const & atom, docstring label, 
84                                                                                                                  MathMacroTemplate const & parent, bool frame)
85 :       InsetMathNest(1), parent_(parent), label_(label), frame_(frame)
86 {
87         cell(0).insert(0, atom);
88 }
89
90 InsetLabelBox::InsetLabelBox(docstring label,
91                                                                                                                  MathMacroTemplate const & parent, bool frame)
92 :       InsetMathNest(1), parent_(parent), label_(label), frame_(frame)
93 {
94 }
95         
96
97 Inset * InsetLabelBox::clone() const 
98 {
99         return new InsetLabelBox(*this);
100 }
101
102
103 void InsetLabelBox::metrics(MetricsInfo & mi, Dimension & dim) const 
104 {
105         // kernel
106         cell(0).metrics(mi, dim);
107         cellDim_ = dim;
108
109         // frame
110         if (frame_) {
111                 dim.wid += 6;
112                 dim.asc += 5;
113                 dim.des += 5;
114         }
115                 
116         // adjust to common height in main metrics phase
117         if (!parent_.premetrics()) {
118                 dim.asc = max(dim.asc, parent_.commonLabelBoxAscent());
119                 dim.des = max(dim.des, parent_.commonLabelBoxDescent());
120         }
121         
122         // label
123         if (label_.length() > 0) {
124                 // grey
125                 FontInfo font = sane_font;
126                 font.setSize(FONT_SIZE_TINY);
127                 font.setColor(Color_mathmacrolabel);
128                 
129                 // make space for label and box
130                 int lwid = mathed_string_width(font, label_);
131                 int maxasc;
132                 int maxdes;
133                 math_font_max_dim(font, maxasc, maxdes);                
134                 
135                 dim.wid = max(dim.wid, lwid + 2);
136
137                 // space for the label
138                 if (!parent_.premetrics())
139                         dim.des += maxasc + maxdes + 1;
140         }
141
142         setDimCache(mi, dim);
143 }
144
145
146 void InsetLabelBox::draw(PainterInfo & pi, int x, int y) const 
147 {
148         Dimension const dim = dimension(*pi.base.bv);
149         
150         // kernel
151         cell(0).draw(pi, x + (dim.wid - cellDim_.wid) / 2, y);
152         
153         // label
154         if (label_.length() > 0) {
155                 // grey
156                 FontInfo font = sane_font;
157                 font.setSize(FONT_SIZE_TINY);
158                 font.setColor(Color_mathmacrolabel);
159                 
160                 // make space for label and box
161                 int lwid = mathed_string_width(font, label_);
162                 int maxasc;
163                 int maxdes;
164                 math_font_max_dim(font, maxasc, maxdes);
165                 
166                 if (lwid < dim.wid)
167                         pi.pain.text(x + (dim.wid - lwid) / 2, y + dim.des - maxdes, label_, font);
168                 else
169                         pi.pain.text(x, y + dim.des - maxdes, label_, font);
170         }
171         
172         // draw frame
173         int boxHeight = parent_.commonLabelBoxAscent() + parent_.commonLabelBoxDescent();
174         if (frame_) {
175                 pi.pain.rectangle(x + 1, y - dim.ascent() + 1, 
176                                                                                         dim.wid - 2, boxHeight - 2, 
177                                                                                         Color_mathline);
178         }
179 }
180
181         
182 //////////////////////////////////////////////////////////////////////
183
184 class InsetMathWrapper : public InsetMath {
185 public:
186         ///
187         InsetMathWrapper(MathData const * value) : value_(value) {}
188         ///
189         void metrics(MetricsInfo & mi, Dimension & dim) const;
190         ///
191         void draw(PainterInfo &, int x, int y) const;
192         
193 private:
194         ///
195         Inset * clone() const;
196         ///
197         MathData const * value_;
198 };
199
200
201 Inset * InsetMathWrapper::clone() const 
202 {
203         return new InsetMathWrapper(*this);
204 }
205
206
207 void InsetMathWrapper::metrics(MetricsInfo & mi, Dimension & dim) const 
208 {
209         value_->metrics(mi, dim);
210         //metricsMarkers2(dim);
211 }
212
213
214 void InsetMathWrapper::draw(PainterInfo & pi, int x, int y) const 
215 {
216         value_->draw(pi, x, y);
217         //drawMarkers(pi, x, y);
218 }
219
220
221 ///////////////////////////////////////////////////////////////////////
222
223 class InsetNameWrapper : public InsetMathWrapper {
224 public:
225         ///
226         InsetNameWrapper(MathData const * value, MathMacroTemplate const & parent);
227         ///
228         void metrics(MetricsInfo & mi, Dimension & dim) const;
229         ///
230         void draw(PainterInfo &, int x, int y) const;
231         
232 private:
233         ///
234         MathMacroTemplate const & parent_;
235         ///
236         Inset * clone() const;
237 };
238
239
240 InsetNameWrapper::InsetNameWrapper(MathData const * value, 
241                                                                                                                                          MathMacroTemplate const & parent)
242         :       InsetMathWrapper(value), parent_(parent)
243 {
244 }
245
246
247 Inset * InsetNameWrapper::clone() const 
248 {
249         return new InsetNameWrapper(*this);
250 }
251
252
253 void InsetNameWrapper::metrics(MetricsInfo & mi, Dimension & dim) const 
254 {
255         InsetMathWrapper::metrics(mi, dim);
256         dim.wid += mathed_string_width(mi.base.font, from_ascii("\\"));
257 }
258
259
260 void InsetNameWrapper::draw(PainterInfo & pi, int x, int y) const 
261 {
262         // create fonts
263         PainterInfo namepi = pi;
264         if (parent_.validMacro())
265                 namepi.base.font.setColor(Color_latex);
266         else
267                 namepi.base.font.setColor(Color_error);         
268         
269         // draw backslash
270         pi.pain.text(x, y, from_ascii("\\"), namepi.base.font);
271         x += mathed_string_width(namepi.base.font, from_ascii("\\"));
272         
273         // draw name
274         InsetMathWrapper::draw(namepi, x, y);
275 }
276
277         
278 ///////////////////////////////////////////////////////////////////////
279         
280         
281 MathMacroTemplate::MathMacroTemplate()
282         : InsetMathNest(3), numargs_(0), optionals_(0), 
283           type_(MacroTypeNewcommand), editing_(false), lookOutdated_(true)
284 {
285         initMath();
286 }
287
288
289 MathMacroTemplate::MathMacroTemplate(docstring const & name, int numargs,
290         int optionals, MacroType type, 
291         vector<MathData> const & optionalValues, 
292         MathData const & def, MathData const & display)
293         : InsetMathNest(optionals + 3), numargs_(numargs), 
294           optionals_(optionals), optionalValues_(optionalValues),
295           type_(type), editing_(false), lookOutdated_(true)
296 {
297         initMath();
298
299         if (numargs_ > 9)
300                 lyxerr << "MathMacroTemplate::MathMacroTemplate: wrong # of arguments: "
301                         << numargs_ << endl;
302         
303         asArray(name, cell(0));
304         optionalValues_.resize(9);
305         for (int i = 0; i < optionals_; ++i) 
306                 cell(optIdx(i)) = optionalValues_[i];
307         cell(defIdx()) = def;
308         cell(displayIdx()) = display;
309
310         updateLook();
311 }
312
313
314 MathMacroTemplate::MathMacroTemplate(docstring const & str)
315         : InsetMathNest(3), numargs_(0), optionals_(0),
316         type_(MacroTypeNewcommand), editing_(false), lookOutdated_(true)
317 {
318         initMath();
319
320         MathData ar;
321         mathed_parse_cell(ar, str);
322         if (ar.size() != 1 || !ar[0]->asMacroTemplate()) {
323                 lyxerr << "Cannot read macro from '" << ar << "'" << endl;
324                 asArray(from_ascii("invalidmacro"), cell(0));
325                 // FIXME: The macro template does not make sense after this.
326                 // The whole parsing should not be in a constructor which
327                 // has no chance to report failure.
328                 return;
329         }
330         operator=( *(ar[0]->asMacroTemplate()) );
331
332         updateLook();
333 }
334
335
336 Inset * MathMacroTemplate::clone() const
337 {
338         return new MathMacroTemplate(*this);
339 }
340
341
342 docstring MathMacroTemplate::name() const
343 {
344         return asString(cell(0));
345 }
346
347
348 void MathMacroTemplate::updateToContext(MacroContext const & mc) const
349 {
350         redefinition_ = mc.get(name()) != 0;
351 }
352
353         
354 void MathMacroTemplate::updateLook() const
355 {
356         lookOutdated_ = true;
357 }
358
359 void MathMacroTemplate::createLook() const
360 {
361         look_.clear();
362
363         // \foo
364         look_.push_back(MathAtom(new InsetLabelBox(
365                  editing_ ? _("name") : docstring(),
366                  *this,
367                  false)));
368         MathData & nameData = look_[look_.size() - 1].nucleus()->cell(0);
369         nameData.push_back(MathAtom(new InsetNameWrapper(&cell(0), *this)));
370         
371         // [#1][#2]
372         int i = 0;
373         if (optionals_ > 0) {
374                 look_.push_back(MathAtom(new InsetLabelBox(
375                         editing_ ? _("optional") : docstring(),
376                         *this,
377                         false)));
378                 MathData & optData = look_[look_.size() - 1].nucleus()->cell(0);
379         
380                 for (; i < optionals_; ++i) {
381                         optData.push_back(MathAtom(new InsetMathChar('[')));
382                         optData.push_back(MathAtom(new InsetMathWrapper(&cell(1 + i))));
383                         optData.push_back(MathAtom(new InsetMathChar(']')));
384                 }
385         }
386         
387         // {#3}{#4}
388         for (; i < numargs_; ++i) {
389                 MathData arg;
390                 arg.push_back(MathAtom(new MathMacroArgument(i + 1)));
391                 look_.push_back(MathAtom(new InsetMathBrace(arg)));
392         }
393         
394         // :=
395         look_.push_back(MathAtom(new InsetMathChar(':')));
396         look_.push_back(MathAtom(new InsetMathChar('=')));
397
398         // definition
399         look_.push_back(
400                 MathAtom(new InsetLabelBox(
401                                 MathAtom(new InsetMathWrapper(&cell(defIdx()))),
402                                 editing_ ? from_ascii("TeX") : docstring(),
403                                 *this,
404                                 true)));
405
406         // display
407         if (editing_ || !cell(displayIdx()).empty()) {
408                 look_.push_back(
409                         MathAtom(new InsetLabelBox(
410                                 MathAtom(new InsetMathWrapper(&cell(displayIdx()))),
411                                         editing_ ? from_ascii("LyX") : docstring(),
412                                         *this,
413                                         true)));
414         }
415 }
416         
417
418 void MathMacroTemplate::metrics(MetricsInfo & mi, Dimension & dim) const
419 {
420         FontSetChanger dummy1(mi.base, from_ascii("mathnormal"));
421         StyleChanger dummy2(mi.base, LM_ST_TEXT);
422         
423         // valid macro?
424         MacroData const * macro = 0;
425         if (validName()) {
426                 macro = mi.macrocontext.get(name());
427
428                 // updateToContext() - avoids another lookup
429                 redefinition_ = macro != 0;
430         }
431         
432         // new edit mode?
433         bool realEditing = editing(mi.base.bv);
434         if (editing_ != realEditing) {
435                 editing_ = realEditing;
436                 lookOutdated_ = true;
437         }
438         
439         // update look?
440         if (lookOutdated_) {
441                 lookOutdated_ = false;
442                 createLook();
443         }
444         
445         /// metrics for inset contents
446         if (macro)
447                 macro->lock();          
448         
449         // first phase, premetric:
450         premetrics_ = true;
451         look_.metrics(mi, dim);
452         labelBoxAscent_ = dim.asc;
453         labelBoxDescent_ = dim.des;
454         
455         // second phase, main metric:
456         premetrics_ = false;
457         look_.metrics(mi, dim);
458                 
459         // metrics for invisible display box
460         if (!editing_ && cell(displayIdx()).empty()) {
461                 Dimension ddim;
462                 cell(displayIdx()).metrics(mi, ddim);
463         }
464         
465         if (macro)
466                 macro->unlock();
467         
468         dim.wid += 6;
469         dim.des += 2;
470         dim.asc += 2;
471         
472         setDimCache(mi, dim);
473 }
474
475
476 void MathMacroTemplate::draw(PainterInfo & pi, int x, int y) const
477 {
478         FontSetChanger dummy1(pi.base, from_ascii("mathnormal"));
479         StyleChanger dummy2(pi.base, LM_ST_TEXT);
480         
481         setPosCache(pi, x, y);
482         Dimension const dim = dimension(*pi.base.bv);
483
484         // draw outer frame
485         int const a = y - dim.asc + 1;
486         int const w = dim.wid - 2;
487         int const h = dim.height() - 2;
488         pi.pain.rectangle(x, a, w, h, Color_mathframe); 
489         
490         // just to be sure: set some dummy values for coord cache
491         for (idx_type i = 0; i < nargs(); ++i) {
492                 cell(i).setXY(*pi.base.bv, x, y);
493         }
494         
495         // draw contents
496         look_.draw(pi, x + 3, y);
497 }
498
499         
500 void MathMacroTemplate::edit(Cursor & cur, bool left)
501 {
502         updateLook();
503         cur.updateFlags(Update::Force);
504         InsetMathNest::edit(cur, left);
505 }
506         
507         
508 Inset * MathMacroTemplate::editXY(Cursor & cur, int x, int y)
509 {
510         Inset * ret = InsetMathNest::editXY(cur, x, y);
511 /*      if (!editing_ && editing(cur.bv())) {
512                 
513                 cur.updateFlags(Update::Force);
514         }*/
515         return ret;
516 }
517         
518         
519 bool MathMacroTemplate::notifyCursorLeaves(Cursor & cur)
520 {
521         updateLook();
522         cur.updateFlags(Update::Force);
523         return InsetMathNest::notifyCursorLeaves(cur);
524 }
525         
526
527 void MathMacroTemplate::removeArguments(Cursor & cur, int from, int to) {
528         for (DocIterator it = doc_iterator_begin(*this); it; it.forwardChar()) {
529                 if (!it.nextInset())
530                                                 continue;
531                 if (it.nextInset()->lyxCode() != MATHMACROARG_CODE)
532                                                 continue;
533                 MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
534                 int n = arg->number() - 1;
535                 if (from <= n && n <= to) {
536                         int cellSlice = cur.find(it.cell());
537                         if (cellSlice != -1 && cur[cellSlice].pos() > it.pos())
538                                         --cur[cellSlice].pos();
539
540                         it.cell().erase(it.pos());
541                 }
542         }
543         
544         updateLook();
545 }
546
547
548 void MathMacroTemplate::shiftArguments(size_t from, int by) {
549         for (DocIterator it = doc_iterator_begin(*this); it; it.forwardChar()) {
550                 if (!it.nextInset())
551                                                 continue;
552                 if (it.nextInset()->lyxCode() != MATHMACROARG_CODE)
553                                                 continue;
554                 MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
555                 if (arg->number() >= from + 1)
556                         arg->setNumber(arg->number() + by);
557         }
558
559         updateLook();
560 }
561
562
563 // FIXME: factorize those functions here with a functional style, maybe using Boost's function
564 // objects?
565
566 void fixMacroInstancesAddRemove(Cursor const & from, docstring const & name, int n, bool insert) {
567         Cursor dit = from;
568
569         for (; dit; dit.forwardPos()) {
570                 // only until a macro is redefined
571                 if (dit.inset().lyxCode() == MATHMACRO_CODE) {
572                         MathMacroTemplate const & macroTemplate
573                         = static_cast<MathMacroTemplate const &>(dit.inset());
574                         if (macroTemplate.name() == name)
575                                 break;
576                 }
577
578                 // in front of macro instance?
579                 Inset * inset = dit.nextInset();
580                 if (inset) {
581                         InsetMath * insetMath = inset->asInsetMath();
582                         if (insetMath) {
583                                 MathMacro * macro = insetMath->asMacro();
584                                 if (macro && macro->name() == name && macro->folded()) {
585                                         // found macro instance
586                                         if (insert)
587                                                 macro->insertArgument(n);
588                                         else
589                                                 macro->removeArgument(n);
590                                 }
591                         }
592                 }
593         }
594 }
595
596
597 void fixMacroInstancesOptional(Cursor const & from, docstring const & name, int optionals) {
598         Cursor dit = from;
599
600         for (; dit; dit.forwardPos()) {
601                 // only until a macro is redefined
602                 if (dit.inset().lyxCode() == MATHMACRO_CODE) {
603                         MathMacroTemplate const & macroTemplate
604                         = static_cast<MathMacroTemplate const &>(dit.inset());
605                         if (macroTemplate.name() == name)
606                                 break;
607                 }
608
609                 // in front of macro instance?
610                 Inset * inset = dit.nextInset();
611                 if (inset) {
612                         InsetMath * insetMath = inset->asInsetMath();
613                         if (insetMath) {
614                                 MathMacro * macro = insetMath->asMacro();
615                                 if (macro && macro->name() == name && macro->folded()) {
616                                         // found macro instance
617                                         macro->setOptionals(optionals);
618                                 }
619                         }
620                 }
621         }
622 }
623
624
625 template<class F>
626 void fixMacroInstancesFunctional(Cursor const & from, 
627         docstring const & name, F & fix) {
628         Cursor dit = from;
629
630         for (; dit; dit.forwardPos()) {
631                 // only until a macro is redefined
632                 if (dit.inset().lyxCode() == MATHMACRO_CODE) {
633                         MathMacroTemplate const & macroTemplate
634                         = static_cast<MathMacroTemplate const &>(dit.inset());
635                         if (macroTemplate.name() == name)
636                                 break;
637                 }
638
639                 // in front of macro instance?
640                 Inset * inset = dit.nextInset();
641                 if (inset) {
642                         InsetMath * insetMath = inset->asInsetMath();
643                         if (insetMath) {
644                                 MathMacro * macro = insetMath->asMacro();
645                                 if (macro && macro->name() == name && macro->folded())
646                                         F(macro);
647                         }
648                 }
649         }
650 }
651
652
653 void MathMacroTemplate::insertParameter(Cursor & cur, int pos, bool greedy) 
654 {
655         if (pos <= numargs_ && pos >= optionals_ && numargs_ < 9) {
656                 ++numargs_;
657                 shiftArguments(pos, 1);
658
659                 // append example #n
660                 cell(defIdx()).push_back(MathAtom(new MathMacroArgument(pos + 1)));
661                 if (!cell(displayIdx()).empty())
662                         cell(displayIdx()).push_back(MathAtom(new MathMacroArgument(pos + 1)));
663
664                 if (!greedy) {
665                         Cursor dit = cur;
666                         dit.leaveInset(*this);
667                         // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
668                         dit.top().forwardPos();
669                         
670                         // fix macro instances
671                         fixMacroInstancesAddRemove(dit, name(), pos, true);
672                 }
673         }
674
675         updateLook();
676 }
677
678
679 void MathMacroTemplate::removeParameter(Cursor & cur, int pos, bool greedy)
680 {
681         if (pos < numargs_ && pos >= 0) {
682                 --numargs_;
683                 removeArguments(cur, pos, pos);
684                 shiftArguments(pos + 1, -1);
685
686                 // removed optional parameter?
687                 if (pos < optionals_) {
688                         --optionals_;
689                         optionalValues_[pos] = cell(optIdx(pos));
690                         cells_.erase(cells_.begin() + optIdx(pos));
691
692                         // fix cursor
693                         int macroSlice = cur.find(this);
694                         if (macroSlice != -1) {
695                                 if (cur[macroSlice].idx() == optIdx(pos)) {
696                                         cur.cutOff(macroSlice);
697                                         cur[macroSlice].idx() = 1;
698                                         cur[macroSlice].pos() = 0;
699                                 } else if (cur[macroSlice].idx() > optIdx(pos))
700                                         --cur[macroSlice].idx();
701                         }
702                 }
703
704                 if (!greedy) {
705                         // fix macro instances
706                         //boost::function<void(MathMacro *)> fix = _1->insertArgument(n);
707                         //fixMacroInstancesFunctional(dit, name(), fix);
708                         Cursor dit = cur;
709                         dit.leaveInset(*this);
710                         // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
711                         dit.top().forwardPos();
712                         fixMacroInstancesAddRemove(dit, name(), pos, false);
713                 }
714         }
715
716         updateLook();
717 }
718
719
720 void MathMacroTemplate::makeOptional(Cursor & cur) {
721         if (numargs_ > 0 && optionals_ < numargs_) {
722                 ++optionals_;
723                 cells_.insert(cells_.begin() + optIdx(optionals_ - 1), optionalValues_[optionals_ - 1]);
724                 // fix cursor
725                 int macroSlice = cur.find(this);
726                 if (macroSlice != -1 && cur[macroSlice].idx() >= optIdx(optionals_ - 1))
727                         ++cur[macroSlice].idx();
728
729                 // fix macro instances
730                 Cursor dit = cur;
731                 dit.leaveInset(*this);
732                 // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
733                 dit.top().forwardPos();
734                 fixMacroInstancesOptional(dit, name(), optionals_);
735         }
736
737         updateLook();
738 }
739
740
741 void MathMacroTemplate::makeNonOptional(Cursor & cur) {
742         if (numargs_ > 0 && optionals_ > 0) {
743                 --optionals_;
744                 
745                 // store default value for later if the use changes his mind
746                 optionalValues_[optionals_] = cell(optIdx(optionals_));
747                 cells_.erase(cells_.begin() + optIdx(optionals_));
748
749                 // fix cursor
750                 int macroSlice = cur.find(this);
751                 if (macroSlice != -1) {
752                         if (cur[macroSlice].idx() > optIdx(optionals_))
753                                 --cur[macroSlice].idx();
754                         else if (cur[macroSlice].idx() == optIdx(optionals_)) {
755                                 cur.cutOff(macroSlice);
756                                 cur[macroSlice].idx() = optIdx(optionals_);
757                                 cur[macroSlice].pos() = 0;
758                         }
759                 }
760
761                 // fix macro instances
762                 Cursor dit = cur;
763                 dit.leaveInset(*this);
764                 // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
765                 dit.top().forwardPos();
766                 fixMacroInstancesOptional(dit, name(), optionals_);
767         }
768
769         updateLook();
770 }
771
772
773 void MathMacroTemplate::doDispatch(Cursor & cur, FuncRequest & cmd)
774 {
775         string const arg = to_utf8(cmd.argument());
776         switch (cmd.action) {
777
778         case LFUN_MATH_MACRO_ADD_PARAM: 
779                 if (numargs_ < 9) {
780                         cur.recordUndoFullDocument();
781                         size_t pos = numargs_;
782                         if (arg.size() != 0)
783                                 pos = (size_t)convert<int>(arg) - 1; // it is checked for >=0 in getStatus
784                         insertParameter(cur, pos);
785                 }
786                 break;
787
788
789         case LFUN_MATH_MACRO_REMOVE_PARAM: 
790                 if (numargs_ > 0) {
791                         cur.recordUndoFullDocument();
792                         size_t pos = numargs_ - 1;
793                         if (arg.size() != 0)
794                                 pos = (size_t)convert<int>(arg) - 1; // it is checked for >=0 in getStatus
795                         removeParameter(cur, pos);
796                 }
797                 break;
798
799         case LFUN_MATH_MACRO_APPEND_GREEDY_PARAM:
800                 if (numargs_ < 9) {
801                         cur.recordUndoFullDocument();
802                         insertParameter(cur, numargs_, true);
803                 }
804                 break;
805
806         case LFUN_MATH_MACRO_REMOVE_GREEDY_PARAM:
807                 if (numargs_ > 0) {
808                         cur.recordUndoFullDocument();
809                         removeParameter(cur, numargs_ - 1, true);
810                 }
811                 break;
812
813         case LFUN_MATH_MACRO_MAKE_OPTIONAL:
814                 cur.recordUndoFullDocument();
815                 makeOptional(cur);
816                 break;
817
818         case LFUN_MATH_MACRO_MAKE_NONOPTIONAL:
819                 cur.recordUndoFullDocument();
820                 makeNonOptional(cur);
821                 break;
822
823         case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM:
824                 if (numargs_ < 9) {
825                         cur.recordUndoFullDocument();
826                         insertParameter(cur, optionals_);
827                         makeOptional(cur);
828                 }
829                 break;
830
831         case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM:
832                 if (optionals_ > 0) {
833                         cur.recordUndoFullDocument();
834                         removeParameter(cur, optionals_ - 1);
835                 } break;
836
837         case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
838                 if (numargs_ == optionals_) {
839                         cur.recordUndoFullDocument();
840                         insertParameter(cur, 0, true);
841                         makeOptional(cur);
842                 }
843                 break;
844
845         default:
846                 InsetMathNest::doDispatch(cur, cmd);
847                 break;
848         }
849 }
850
851
852 bool MathMacroTemplate::getStatus(Cursor & /*cur*/, FuncRequest const & cmd,
853         FuncStatus & flag) const
854 {
855         bool ret = true;
856         string const arg = to_utf8(cmd.argument());
857         switch (cmd.action) {
858                 case LFUN_MATH_MACRO_ADD_PARAM: {
859                         int num = numargs_ + 1;
860                         if (arg.size() != 0)
861                                 num = convert<int>(arg);
862                         bool on = (num >= optionals_ 
863                                    && numargs_ < 9 && num <= numargs_ + 1);
864                         flag.enabled(on);
865                         break;
866                 }
867
868                 case LFUN_MATH_MACRO_APPEND_GREEDY_PARAM:
869                         flag.enabled(numargs_ < 9);
870                         break;
871
872                 case LFUN_MATH_MACRO_REMOVE_PARAM: {
873                         int num = numargs_;
874                         if (arg.size() != 0)
875                                 num = convert<int>(arg);
876                         flag.enabled(num >= 1 && num <= numargs_);
877                         break;
878                 }
879
880                 case LFUN_MATH_MACRO_MAKE_OPTIONAL:
881                         flag.enabled(numargs_ > 0 
882                                      && optionals_ < numargs_ 
883                                      && type_ != MacroTypeDef);
884                         break;
885
886                 case LFUN_MATH_MACRO_MAKE_NONOPTIONAL:
887                         flag.enabled(optionals_ > 0 
888                                      && type_ != MacroTypeDef);
889                         break;
890
891                 case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM:
892                         flag.enabled(numargs_ < 9);
893                         break;
894
895                 case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM:
896                         flag.enabled(optionals_ > 0);
897                         break;
898
899                 case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
900                         flag.enabled(numargs_ == 0 
901                                      && type_ != MacroTypeDef);
902                         break;
903
904                 case LFUN_IN_MATHMACROTEMPLATE:
905                         flag.enabled();
906                         break;
907
908                 default:
909                         ret = false;
910                         break;
911         }
912         return ret;
913 }
914
915
916 void MathMacroTemplate::read(Buffer const &, Lexer & lex)
917 {
918         MathData ar;
919         mathed_parse_cell(ar, lex.getStream());
920         if (ar.size() != 1 || !ar[0]->asMacroTemplate()) {
921                 lyxerr << "Cannot read macro from '" << ar << "'" << endl;
922                 lyxerr << "Read: " << to_utf8(asString(ar)) << endl;
923                 return;
924         }
925         operator=( *(ar[0]->asMacroTemplate()) );
926         
927         updateLook();
928 }
929
930
931 void MathMacroTemplate::write(Buffer const &, ostream & os) const
932 {
933         odocstringstream oss;
934         WriteStream wi(oss, false, false);
935         oss << "FormulaMacro\n";
936         write(wi);
937         os << to_utf8(oss.str());
938 }
939
940
941 void MathMacroTemplate::write(WriteStream & os) const
942 {
943         write(os, false);
944 }
945
946
947 void MathMacroTemplate::write(WriteStream & os, bool overwriteRedefinition) const
948 {
949         if (type_ == MacroTypeDef) {
950                 os << "\\def\\" << name().c_str();
951                 for (int i = 1; i <= numargs_; ++i)
952                         os << '#' << i;
953         } else {
954                 // newcommand or renewcommand
955                 if (redefinition_ && !overwriteRedefinition)
956                         os << "\\renewcommand";
957                 else
958                         os << "\\newcommand";
959                 os << "{\\" << name().c_str() << '}';
960                 if (numargs_ > 0)
961                         os << '[' << numargs_ << ']';
962                 
963                 // optional values
964                 if (os.latex()) {
965                         // in latex only one optional possible, simulate the others
966                         if (optionals_ >= 1) {
967                                 docstring optValue = asString(cell(optIdx(0)));
968                                 if (optValue.find(']') != docstring::npos)
969                                         os << "[{" << cell(optIdx(0)) << "}]";
970                                 else
971                                         os << "[" << cell(optIdx(0)) << "]";
972                         }
973                 } else {
974                         // in lyx we handle all optionals as real optionals
975                         for (int i = 0; i < optionals_; ++i) {
976                                 docstring optValue = asString(cell(optIdx(i)));
977                                 if (optValue.find(']') != docstring::npos)
978                                         os << "[{" << cell(optIdx(i)) << "}]";
979                                 else
980                                         os << "[" << cell(optIdx(i)) << "]";
981                         }
982                 }
983         }
984
985         os << "{" << cell(defIdx()) << "}";
986
987         if (os.latex()) {
988                 // writing .tex. done.
989                 os << "\n";
990         } else {
991                 // writing .lyx, write special .tex export only if necessary
992                 if (!cell(displayIdx()).empty())
993                         os << "\n{" << cell(displayIdx()) << '}';
994         }
995 }
996
997
998 int MathMacroTemplate::plaintext(Buffer const & buf, odocstream & os,
999                                  OutputParams const &) const
1000 {
1001         static docstring const str = '[' + buf.B_("math macro") + ']';
1002
1003         os << str;
1004         return str.size();
1005 }
1006
1007
1008 bool MathMacroTemplate::validName() const
1009 {
1010         docstring n = name();
1011
1012         // empty name?
1013         if (n.size() == 0)
1014                 return false;
1015
1016         // converting back and force doesn't swallow anything?
1017         /*MathData ma;
1018         asArray(n, ma);
1019         if (asString(ma) != n)
1020                 return false;*/
1021
1022         // valid characters?
1023         for (size_t i = 0; i < n.size(); ++i) {
1024                 if (!(n[i] >= 'a' && n[i] <= 'z') &&
1025                                 !(n[i] >= 'A' && n[i] <= 'Z')) 
1026                         return false;
1027         }
1028
1029         return true;
1030 }
1031
1032
1033 bool MathMacroTemplate::validMacro() const
1034 {
1035         return validName();
1036 }
1037
1038
1039 bool MathMacroTemplate::fixNameAndCheckIfValid()
1040 {
1041         // check all the characters/insets in the name cell
1042         size_t i = 0;
1043         MathData & data = cell(0);
1044         while (i < data.size()) {
1045                 InsetMathChar const * cinset = data[i]->asCharInset();
1046                 if (cinset) {
1047                         // valid character in [a-zA-Z]?
1048                         char_type c = cinset->getChar();
1049                         if ((c >= 'a' && c <= 'z')
1050                             || (c >= 'A' && c <= 'Z')) {
1051                                 ++i;
1052                                 continue;
1053                         }
1054                 }
1055                 
1056                 // throw cell away
1057                 data.erase(i);
1058         }
1059
1060         // now it should be valid if anything in the name survived
1061         return data.size() > 0;
1062 }
1063
1064
1065 void MathMacroTemplate::getDefaults(vector<docstring> & defaults) const
1066 {
1067         defaults.resize(numargs_);
1068         for (int i = 0; i < optionals_; ++i)
1069                 defaults[i] = asString(cell(optIdx(i)));        
1070 }
1071
1072
1073 docstring MathMacroTemplate::definition() const
1074 {
1075         return asString(cell(defIdx()));
1076 }
1077
1078
1079 docstring MathMacroTemplate::displayDefinition() const
1080 {
1081         return asString(cell(displayIdx()));
1082 }
1083
1084
1085 size_t MathMacroTemplate::numArgs() const
1086 {
1087         return numargs_;
1088 }
1089
1090
1091 size_t MathMacroTemplate::numOptionals() const
1092 {
1093         return optionals_;
1094 }
1095
1096         
1097 void MathMacroTemplate::infoize(odocstream & os) const
1098 {
1099         os << "Math Macro: \\" << name();
1100 }
1101
1102 } // namespace lyx