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