]> git.lyx.org Git - features.git/blob - src/mathed/MathMacroTemplate.cpp
* Lazy MathData to avoid unneeded interpretation of macro definitions
[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_(from_ascii("newcommand"))
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         // valid macro?
167         MacroData const * macro = 0;
168         if (validName()) {
169                 macro = mi.macrocontext.get(name());
170
171                 // updateToContext() - avoids another lookup
172                 redefinition_ = macro != 0;
173         }
174
175         // create label "{#1}{#2}:="
176         label_.clear();
177         int i = 0;
178         for (; i < optionals_; ++i) {
179                 label_.push_back(MathAtom(new InsetMathChar('[')));
180                 label_.push_back(MathAtom(new InsetMathWrapper(&cell(1 + i))));
181                 label_.push_back(MathAtom(new InsetMathChar(']')));
182         }
183         for (; i < numargs_; ++i) {
184                 MathData arg;
185                 arg.push_back(MathAtom(new MathMacroArgument(i + 1)));
186                 label_.push_back(MathAtom(new InsetMathBrace(arg)));
187         }
188         label_.push_back(MathAtom(new InsetMathChar(':')));
189         label_.push_back(MathAtom(new InsetMathChar('=')));
190
191         // do metrics
192         if (macro)
193                 macro->lock();
194
195         Dimension dim0;
196         Dimension labeldim;
197         Dimension defdim;
198         Dimension dspdim;
199         
200         cell(0).metrics(mi, dim0);
201         label_.metrics(mi, labeldim);
202         cell(defIdx()).metrics(mi, defdim);
203         cell(displayIdx()).metrics(mi, dspdim);
204
205         if (macro)
206                 macro->unlock();
207
208         // calculate metrics taking all cells and labels into account
209         dim.wid = 2 + mathed_string_width(mi.base.font, from_ascii("\\")) +
210                 dim0.width() + 
211                 labeldim.width() +
212                 defdim.width() + 16 + dspdim.width() + 2;       
213
214         dim.asc = dim0.ascent();
215         dim.asc = max(dim.asc, labeldim.ascent());
216         dim.asc = max(dim.asc, defdim.ascent());
217         dim.asc = max(dim.asc, dspdim.ascent());
218
219         dim.des = dim0.descent();
220         dim.des = max(dim.des, labeldim.descent());
221         dim.des = max(dim.des, defdim.descent());
222         dim.des = max(dim.des, dspdim.descent());
223
224         // make the name cell vertically centered, and 5 pixel lines margin
225         int real_asc = dim.asc - dim0.ascent() / 2;
226         int real_des = dim.des + dim0.ascent() / 2;
227         dim.asc = max(real_asc, real_des) + dim0.ascent() / 2 + 5;
228         dim.des = max(real_asc, real_des) - dim0.ascent() / 2 + 5;
229
230         setDimCache(mi, dim);
231 }
232
233
234 void MathMacroTemplate::draw(PainterInfo & pi, int x, int y) const
235 {
236         FontSetChanger dummy1(pi.base, from_ascii("mathnormal"));
237         StyleChanger dummy2(pi.base, LM_ST_TEXT);
238
239         setPosCache(pi, x, y);
240         Dimension const dim = dimension(*pi.base.bv);
241
242         // create fonts
243         bool valid = validMacro();
244         FontInfo font = pi.base.font;
245         if (valid)
246                 font.setColor(Color_latex);
247         else
248                 font.setColor(Color_error);             
249
250         // draw outer frame
251         int const a = y - dim.asc + 1;
252         int const w = dim.wid - 2;
253         int const h = dim.height() - 2;
254         pi.pain.rectangle(x, a, w, h, Color_mathframe); 
255         x += 4;
256
257         // draw backslash
258         pi.pain.text(x, y, from_ascii("\\"), font);
259         x += mathed_string_width(font, from_ascii("\\"));
260
261         // draw name
262         PainterInfo namepi = pi;
263         namepi.base.font  = font;       
264         cell(0).draw(namepi, x, y);
265         x += cell(0).dimension(*pi.base.bv).width();
266
267         // draw label
268         label_.draw(pi, x, y);
269         x += label_.dimension(*pi.base.bv).width();
270  
271         // draw definition
272         cell(defIdx()).draw(pi, x + 2, y);
273         int const w1 = cell(defIdx()).dimension(*pi.base.bv).width();
274         pi.pain.rectangle(x, y - dim.ascent() + 3, w1 + 4, dim.height() - 6, Color_mathline);
275         x += w1 + 8;
276
277         // draw display
278         cell(displayIdx()).draw(pi, x + 2, y);
279         int const w2 = cell(displayIdx()).dimension(*pi.base.bv).width();
280         pi.pain.rectangle(x, y - dim.ascent() + 3, w2 + 4, dim.height() - 6, Color_mathline);
281 }
282
283
284 void MathMacroTemplate::removeArguments(Cursor & cur, int from, int to) {
285         for (DocIterator it = doc_iterator_begin(*this); it; it.forwardChar()) {
286                 if (!it.nextInset())
287                                                 continue;
288                 if (it.nextInset()->lyxCode() != MATHMACROARG_CODE)
289                                                 continue;
290                 MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
291                 int n = arg->number() - 1;
292                 if (from <= n && n <= to) {
293                         int cellSlice = cur.find(it.cell());
294                         if (cellSlice != -1 && cur[cellSlice].pos() > it.pos())
295                                         --cur[cellSlice].pos();
296
297                         it.cell().erase(it.pos());
298                 }
299         }
300 }
301
302
303 void MathMacroTemplate::shiftArguments(size_t from, int by) {
304         for (DocIterator it = doc_iterator_begin(*this); it; it.forwardChar()) {
305                 if (!it.nextInset())
306                                                 continue;
307                 if (it.nextInset()->lyxCode() != MATHMACROARG_CODE)
308                                                 continue;
309                 MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
310                 if (arg->number() >= from + 1)
311                         arg->setNumber(arg->number() + by);
312         }
313 }
314
315
316 // FIXME: factorize those functions here with a functional style, maybe using Boost's function
317 // objects?
318
319 void fixMacroInstancesAddRemove(Cursor const & from, docstring const & name, int n, bool insert) {
320         Cursor dit = from;
321
322         for (; dit; dit.forwardPos()) {
323                 // only until a macro is redefined
324                 if (dit.inset().lyxCode() == MATHMACRO_CODE) {
325                         MathMacroTemplate const & macroTemplate
326                         = static_cast<MathMacroTemplate const &>(dit.inset());
327                         if (macroTemplate.name() == name)
328                                 break;
329                 }
330
331                 // in front of macro instance?
332                 Inset * inset = dit.nextInset();
333                 if (inset) {
334                         InsetMath * insetMath = inset->asInsetMath();
335                         if (insetMath) {
336                                 MathMacro * macro = insetMath->asMacro();
337                                 if (macro && macro->name() == name && macro->folded()) {
338                                         // found macro instance
339                                         if (insert)
340                                                 macro->insertArgument(n);
341                                         else
342                                                 macro->removeArgument(n);
343                                 }
344                         }
345                 }
346         }
347 }
348
349
350 void fixMacroInstancesOptional(Cursor const & from, docstring const & name, int optionals) {
351         Cursor dit = from;
352
353         for (; dit; dit.forwardPos()) {
354                 // only until a macro is redefined
355                 if (dit.inset().lyxCode() == MATHMACRO_CODE) {
356                         MathMacroTemplate const & macroTemplate
357                         = static_cast<MathMacroTemplate const &>(dit.inset());
358                         if (macroTemplate.name() == name)
359                                 break;
360                 }
361
362                 // in front of macro instance?
363                 Inset * inset = dit.nextInset();
364                 if (inset) {
365                         InsetMath * insetMath = inset->asInsetMath();
366                         if (insetMath) {
367                                 MathMacro * macro = insetMath->asMacro();
368                                 if (macro && macro->name() == name && macro->folded()) {
369                                         // found macro instance
370                                         macro->setOptionals(optionals);
371                                 }
372                         }
373                 }
374         }
375 }
376
377
378 template<class F>
379 void fixMacroInstancesFunctional(Cursor const & from, 
380         docstring const & name, F & fix) {
381         Cursor dit = from;
382
383         for (; dit; dit.forwardPos()) {
384                 // only until a macro is redefined
385                 if (dit.inset().lyxCode() == MATHMACRO_CODE) {
386                         MathMacroTemplate const & macroTemplate
387                         = static_cast<MathMacroTemplate const &>(dit.inset());
388                         if (macroTemplate.name() == name)
389                                 break;
390                 }
391
392                 // in front of macro instance?
393                 Inset * inset = dit.nextInset();
394                 if (inset) {
395                         InsetMath * insetMath = inset->asInsetMath();
396                         if (insetMath) {
397                                 MathMacro * macro = insetMath->asMacro();
398                                 if (macro && macro->name() == name && macro->folded())
399                                         F(macro);
400                         }
401                 }
402         }
403 }
404
405
406 void MathMacroTemplate::insertParameter(Cursor & cur, int pos, bool greedy) 
407 {
408         if (pos <= numargs_ && pos >= optionals_ && numargs_ < 9) {
409                 ++numargs_;
410                 shiftArguments(pos, 1);
411
412                 // append example #n
413                 cell(defIdx()).push_back(MathAtom(new MathMacroArgument(pos + 1)));
414                 if (!cell(displayIdx()).empty())
415                         cell(displayIdx()).push_back(MathAtom(new MathMacroArgument(pos + 1)));
416
417                 if (!greedy) {
418                         Cursor dit = cur;
419                         dit.leaveInset(*this);
420                         // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
421                         dit.top().forwardPos();
422                         
423                         // fix macro instances
424                         fixMacroInstancesAddRemove(dit, name(), pos, true);
425                 }
426         }
427 }
428
429
430 void MathMacroTemplate::removeParameter(Cursor & cur, int pos, bool greedy)
431 {
432         if (pos < numargs_ && pos >= 0) {
433                 --numargs_;
434                 removeArguments(cur, pos, pos);
435                 shiftArguments(pos + 1, -1);
436
437                 // removed optional parameter?
438                 if (pos < optionals_) {
439                         --optionals_;
440                         optionalValues_[pos] = cell(optIdx(pos));
441                         cells_.erase(cells_.begin() + optIdx(pos));
442
443                         // fix cursor
444                         int macroSlice = cur.find(this);
445                         if (macroSlice != -1) {
446                                 if (cur[macroSlice].idx() == optIdx(pos)) {
447                                         cur.cutOff(macroSlice);
448                                         cur[macroSlice].idx() = 1;
449                                         cur[macroSlice].pos() = 0;
450                                 } else if (cur[macroSlice].idx() > optIdx(pos))
451                                         --cur[macroSlice].idx();
452                         }
453                 }
454
455                 if (!greedy) {
456                         // fix macro instances
457                         //boost::function<void(MathMacro *)> fix = _1->insertArgument(n);
458                         //fixMacroInstancesFunctional(dit, name(), fix);
459                         Cursor dit = cur;
460                         dit.leaveInset(*this);
461                         // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
462                         dit.top().forwardPos();
463                         fixMacroInstancesAddRemove(dit, name(), pos, false);
464                 }
465         }
466 }
467
468
469 void MathMacroTemplate::makeOptional(Cursor & cur) {
470         if (numargs_ > 0 && optionals_ < numargs_) {
471                 ++optionals_;
472                 cells_.insert(cells_.begin() + optIdx(optionals_ - 1), optionalValues_[optionals_ - 1]);
473                 // fix cursor
474                 int macroSlice = cur.find(this);
475                 if (macroSlice != -1 && cur[macroSlice].idx() >= optIdx(optionals_ - 1))
476                         ++cur[macroSlice].idx();
477
478                 // fix macro instances
479                 Cursor dit = cur;
480                 dit.leaveInset(*this);
481                 // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
482                 dit.top().forwardPos();
483                 fixMacroInstancesOptional(dit, name(), optionals_);
484         }
485 }
486
487
488 void MathMacroTemplate::makeNonOptional(Cursor & cur) {
489         if (numargs_ > 0 && optionals_ > 0) {
490                 --optionals_;
491                 
492                 // store default value for later if the use changes his mind
493                 optionalValues_[optionals_] = cell(optIdx(optionals_));
494                 cells_.erase(cells_.begin() + optIdx(optionals_));
495
496                 // fix cursor
497                 int macroSlice = cur.find(this);
498                 if (macroSlice != -1) {
499                         if (cur[macroSlice].idx() > optIdx(optionals_))
500                                 --cur[macroSlice].idx();
501                         else if (cur[macroSlice].idx() == optIdx(optionals_)) {
502                                 cur.cutOff(macroSlice);
503                                 cur[macroSlice].idx() = optIdx(optionals_);
504                                 cur[macroSlice].pos() = 0;
505                         }
506                 }
507
508                 // fix macro instances
509                 Cursor dit = cur;
510                 dit.leaveInset(*this);
511                 // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
512                 dit.top().forwardPos();
513                 fixMacroInstancesOptional(dit, name(), optionals_);
514         }
515 }
516
517
518 void MathMacroTemplate::doDispatch(Cursor & cur, FuncRequest & cmd)
519 {
520         string const arg = to_utf8(cmd.argument());
521         switch (cmd.action) {
522
523         case LFUN_MATH_MACRO_ADD_PARAM: 
524                 if (numargs_ < 9) {
525                         cur.recordUndoFullDocument();
526                         size_t pos = numargs_;
527                         if (arg.size() != 0)
528                                 pos = (size_t)convert<int>(arg) - 1; // it is checked for >=0 in getStatus
529                         insertParameter(cur, pos);
530                 }
531                 break;
532
533
534         case LFUN_MATH_MACRO_REMOVE_PARAM: 
535                 if (numargs_ > 0) {
536                         cur.recordUndoFullDocument();
537                         size_t pos = numargs_ - 1;
538                         if (arg.size() != 0)
539                                 pos = (size_t)convert<int>(arg) - 1; // it is checked for >=0 in getStatus
540                         removeParameter(cur, pos);
541                 }
542                 break;
543
544         case LFUN_MATH_MACRO_APPEND_GREEDY_PARAM:
545                 if (numargs_ < 9) {
546                         cur.recordUndoFullDocument();
547                         insertParameter(cur, numargs_, true);
548                 }
549                 break;
550
551         case LFUN_MATH_MACRO_REMOVE_GREEDY_PARAM:
552                 if (numargs_ > 0) {
553                         cur.recordUndoFullDocument();
554                         removeParameter(cur, numargs_ - 1, true);
555                 }
556                 break;
557
558         case LFUN_MATH_MACRO_MAKE_OPTIONAL:
559                 cur.recordUndoFullDocument();
560                 makeOptional(cur);
561                 break;
562
563         case LFUN_MATH_MACRO_MAKE_NONOPTIONAL:
564                 cur.recordUndoFullDocument();
565                 makeNonOptional(cur);
566                 break;
567
568         case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM:
569                 if (numargs_ < 9) {
570                         cur.recordUndoFullDocument();
571                         insertParameter(cur, optionals_);
572                         makeOptional(cur);
573                 }
574                 break;
575
576         case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM:
577                 if (optionals_ > 0) {
578                         cur.recordUndoFullDocument();
579                         removeParameter(cur, optionals_ - 1);
580                 } break;
581
582         case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
583                 if (numargs_ == optionals_) {
584                         cur.recordUndoFullDocument();
585                         insertParameter(cur, 0, true);
586                         makeOptional(cur);
587                 }
588                 break;
589
590         default:
591                 InsetMathNest::doDispatch(cur, cmd);
592                 break;
593         }
594 }
595
596
597 bool MathMacroTemplate::getStatus(Cursor & /*cur*/, FuncRequest const & cmd,
598         FuncStatus & flag) const
599 {
600         bool ret = true;
601         string const arg = to_utf8(cmd.argument());
602         switch (cmd.action) {
603                 case LFUN_MATH_MACRO_ADD_PARAM: {
604                         int num = numargs_ + 1;
605                         if (arg.size() != 0)
606                                 num = convert<int>(arg);
607                         bool on = (num >= optionals_ 
608                                    && numargs_ < 9 && num <= numargs_ + 1);
609                         flag.enabled(on);
610                         break;
611                 }
612
613                 case LFUN_MATH_MACRO_APPEND_GREEDY_PARAM:
614                         flag.enabled(numargs_ < 9);
615                         break;
616
617                 case LFUN_MATH_MACRO_REMOVE_PARAM: {
618                         int num = numargs_;
619                         if (arg.size() != 0)
620                                 num = convert<int>(arg);
621                         flag.enabled(num >= 1 && num <= numargs_);
622                         break;
623                 }
624
625                 case LFUN_MATH_MACRO_MAKE_OPTIONAL:
626                         flag.enabled(numargs_ > 0 
627                                      && optionals_ < numargs_ 
628                                      && type_ != MacroTypeDef);
629                         break;
630
631                 case LFUN_MATH_MACRO_MAKE_NONOPTIONAL:
632                         flag.enabled(optionals_ > 0 
633                                      && type_ != MacroTypeDef);
634                         break;
635
636                 case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM:
637                         flag.enabled(numargs_ < 9);
638                         break;
639
640                 case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM:
641                         flag.enabled(optionals_ > 0);
642                         break;
643
644                 case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
645                         flag.enabled(numargs_ == 0 
646                                      && type_ != MacroTypeDef);
647                         break;
648
649                 default:
650                         ret = false;
651                         break;
652         }
653         return ret;
654 }
655
656
657 void MathMacroTemplate::read(Buffer const &, Lexer & lex)
658 {
659         MathData ar;
660         mathed_parse_cell(ar, lex.getStream());
661         if (ar.size() != 1 || !ar[0]->asMacroTemplate()) {
662                 lyxerr << "Cannot read macro from '" << ar << "'" << endl;
663                 lyxerr << "Read: " << to_utf8(asString(ar)) << endl;
664                 return;
665         }
666         operator=( *(ar[0]->asMacroTemplate()) );
667 }
668
669
670 void MathMacroTemplate::write(Buffer const &, ostream & os) const
671 {
672         odocstringstream oss;
673         WriteStream wi(oss, false, false);
674         oss << "FormulaMacro\n";
675         write(wi);
676         os << to_utf8(oss.str());
677 }
678
679
680 void MathMacroTemplate::write(WriteStream & os) const
681 {
682         write(os, false);
683 }
684
685
686 void MathMacroTemplate::write(WriteStream & os, bool overwriteRedefinition) const
687 {
688         if (type_ == MacroTypeDef) {
689                 os << "\\def\\" << name().c_str();
690                 for (int i = 1; i <= numargs_; ++i)
691                         os << '#' << i;
692         } else {
693                 // newcommand or renewcommand
694                 if (redefinition_ && !overwriteRedefinition)
695                         os << "\\renewcommand";
696                 else
697                         os << "\\newcommand";
698                 os << "{\\" << name().c_str() << '}';
699                 if (numargs_ > 0)
700                         os << '[' << numargs_ << ']';
701                 
702                 // optional values
703                 if (os.latex()) {
704                         // in latex only one optional possible, simulate the others
705                         if (optionals_ >= 1) {
706                                 docstring optValue = asString(cell(optIdx(0)));
707                                 if (optValue.find(']') != docstring::npos)
708                                         os << "[{" << cell(optIdx(0)) << "}]";
709                                 else
710                                         os << "[" << cell(optIdx(0)) << "]";
711                         }
712                 } else {
713                         // in lyx we handle all optionals as real optionals
714                         for (int i = 0; i < optionals_; ++i) {
715                                 docstring optValue = asString(cell(optIdx(i)));
716                                 if (optValue.find(']') != docstring::npos)
717                                         os << "[{" << cell(optIdx(i)) << "}]";
718                                 else
719                                         os << "[" << cell(optIdx(i)) << "]";
720                         }
721                 }
722         }
723
724         os << "{" << cell(defIdx()) << "}";
725
726         if (os.latex()) {
727                 // writing .tex. done.
728                 os << "\n";
729         } else {
730                 // writing .lyx, write special .tex export only if necessary
731                 if (!cell(displayIdx()).empty())
732                         os << "\n{" << cell(displayIdx()) << '}';
733         }
734 }
735
736
737 int MathMacroTemplate::plaintext(Buffer const & buf, odocstream & os,
738                                  OutputParams const &) const
739 {
740         static docstring const str = '[' + buf.B_("math macro") + ']';
741
742         os << str;
743         return str.size();
744 }
745
746
747 bool MathMacroTemplate::validName() const
748 {
749         docstring n = name();
750
751         // empty name?
752         if (n.size() == 0)
753                 return false;
754
755         // converting back and force doesn't swallow anything?
756         /*MathData ma;
757         asArray(n, ma);
758         if (asString(ma) != n)
759                 return false;*/
760
761         // valid characters?
762         for (size_t i = 0; i < n.size(); ++i) {
763                 if (!(n[i] >= 'a' && n[i] <= 'z') &&
764                                 !(n[i] >= 'A' && n[i] <= 'Z')) 
765                         return false;
766         }
767
768         return true;
769 }
770
771
772 bool MathMacroTemplate::validMacro() const
773 {
774         return validName();
775 }
776
777
778 bool MathMacroTemplate::fixNameAndCheckIfValid()
779 {
780         // check all the characters/insets in the name cell
781         size_t i = 0;
782         MathData & data = cell(0);
783         while (i < data.size()) {
784                 InsetMathChar const * cinset = data[i]->asCharInset();
785                 if (cinset) {
786                         // valid character in [a-zA-Z]?
787                         char_type c = cinset->getChar();
788                         if ((c >= 'a' && c <= 'z')
789                             || (c >= 'A' && c <= 'Z')) {
790                                 ++i;
791                                 continue;
792                         }
793                 }
794                 
795                 // throw cell away
796                 data.erase(i);
797         }
798
799         // now it should be valid if anything in the name survived
800         return data.size() > 0;
801 }
802
803
804 void MathMacroTemplate::getDefaults(vector<docstring> & defaults) const
805 {
806         defaults.resize(numargs_);
807         for (int i = 0; i < optionals_; ++i)
808                 defaults[i] = asString(cell(optIdx(i)));        
809 }
810
811
812 docstring MathMacroTemplate::definition() const
813 {
814         return asString(cell(defIdx()));
815 }
816
817
818 docstring MathMacroTemplate::displayDefinition() const
819 {
820         return asString(cell(displayIdx()));
821 }
822
823
824 size_t MathMacroTemplate::numArgs() const
825 {
826         return numargs_;
827 }
828
829
830 size_t MathMacroTemplate::numOptionals() const
831 {
832         return optionals_;
833 }
834
835 } // namespace lyx