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