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