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