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