2 * \file MathMacroTemplate.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
13 #include "MathMacroTemplate.h"
15 #include "DocIterator.h"
16 #include "LaTeXFeatures.h"
17 #include "InsetMathBrace.h"
18 #include "InsetMathChar.h"
19 #include "InsetMathSqrt.h"
20 #include "MathMacro.h"
21 #include "MathMacroArgument.h"
22 #include "MathStream.h"
23 #include "MathParser.h"
24 #include "MathSupport.h"
25 #include "MathMacroArgument.h"
28 #include "BufferView.h"
31 #include "DispatchResult.h"
32 #include "FuncRequest.h"
33 #include "FuncStatus.h"
37 #include "frontends/Painter.h"
39 #include "support/lassert.h"
40 #include "support/convert.h"
41 #include "support/debug.h"
42 #include "support/gettext.h"
43 #include "support/docstream.h"
44 #include "support/lstrings.h"
52 using support::bformat;
54 //////////////////////////////////////////////////////////////////////
56 class InsetLabelBox : public InsetMathNest {
59 InsetLabelBox(MathAtom const & atom, docstring label,
60 MathMacroTemplate const & parent, bool frame = false);
61 InsetLabelBox(docstring label, MathMacroTemplate const & parent,
64 void metrics(MetricsInfo & mi, Dimension & dim) const;
66 void draw(PainterInfo &, int x, int y) const;
70 MathMacroTemplate const & parent_;
72 Inset * clone() const;
74 docstring const label_;
80 InsetLabelBox::InsetLabelBox(MathAtom const & atom, docstring label,
81 MathMacroTemplate const & parent, bool frame)
82 : InsetMathNest(1), parent_(parent), label_(label), frame_(frame)
84 cell(0).insert(0, atom);
88 InsetLabelBox::InsetLabelBox(docstring label,
89 MathMacroTemplate const & parent, bool frame)
90 : InsetMathNest(1), parent_(parent), label_(label), frame_(frame)
95 Inset * InsetLabelBox::clone() const
97 return new InsetLabelBox(*this);
101 void InsetLabelBox::metrics(MetricsInfo & mi, Dimension & dim) const
104 cell(0).metrics(mi, dim);
113 // adjust to common height in main metrics phase
114 if (!parent_.premetrics()) {
115 dim.asc = max(dim.asc, parent_.commonLabelBoxAscent());
116 dim.des = max(dim.des, parent_.commonLabelBoxDescent());
120 if (parent_.editing(mi.base.bv) && label_.length() > 0) {
122 FontInfo font = sane_font;
123 font.setSize(FONT_SIZE_TINY);
124 font.setColor(Color_mathmacrolabel);
126 // make space for label and box
127 int lwid = mathed_string_width(font, label_);
130 math_font_max_dim(font, maxasc, maxdes);
132 dim.wid = max(dim.wid, lwid + 2);
134 // space for the label
135 if (!parent_.premetrics())
136 dim.des += maxasc + maxdes + 1;
141 void InsetLabelBox::draw(PainterInfo & pi, int x, int y) const
143 Dimension const dim = dimension(*pi.base.bv);
144 Dimension const cdim = cell(0).dimension(*pi.base.bv);
147 cell(0).draw(pi, x + (dim.wid - cdim.wid) / 2, y);
150 if (parent_.editing(pi.base.bv) && label_.length() > 0) {
152 FontInfo font = sane_font;
153 font.setSize(FONT_SIZE_TINY);
154 font.setColor(Color_mathmacrolabel);
156 // make space for label and box
157 int lwid = mathed_string_width(font, label_);
160 math_font_max_dim(font, maxasc, maxdes);
163 pi.pain.text(x + (dim.wid - lwid) / 2, y + dim.des - maxdes, label_, font);
165 pi.pain.text(x, y + dim.des - maxdes, label_, font);
169 int boxHeight = parent_.commonLabelBoxAscent() + parent_.commonLabelBoxDescent();
171 pi.pain.rectangle(x + 1, y - dim.ascent() + 1,
172 dim.wid - 2, boxHeight - 2,
178 //////////////////////////////////////////////////////////////////////
180 class DisplayLabelBox : public InsetLabelBox {
183 DisplayLabelBox(MathAtom const & atom, docstring label,
184 MathMacroTemplate const & parent);
187 void metrics(MetricsInfo & mi, Dimension & dim) const;
189 void draw(PainterInfo &, int x, int y) const;
193 Inset * clone() const;
197 DisplayLabelBox::DisplayLabelBox(MathAtom const & atom,
199 MathMacroTemplate const & parent)
200 : InsetLabelBox(atom, label, parent, true)
206 Inset * DisplayLabelBox::clone() const
208 return new DisplayLabelBox(*this);
212 void DisplayLabelBox::metrics(MetricsInfo & mi, Dimension & dim) const
214 InsetLabelBox::metrics(mi, dim);
215 if (!parent_.editing(mi.base.bv)
216 && parent_.cell(parent_.displayIdx()).empty()) {
224 void DisplayLabelBox::draw(PainterInfo & pi, int x, int y) const
226 if (parent_.editing(pi.base.bv)
227 || !parent_.cell(parent_.displayIdx()).empty()) {
228 InsetLabelBox::draw(pi, x, y);
230 bool enabled = pi.pain.isDrawingEnabled();
231 pi.pain.setDrawingEnabled(false);
232 InsetLabelBox::draw(pi, x, y);
233 pi.pain.setDrawingEnabled(enabled);
238 //////////////////////////////////////////////////////////////////////
240 class InsetMathWrapper : public InsetMath {
243 InsetMathWrapper(MathData const * value) : value_(value) {}
245 void metrics(MetricsInfo & mi, Dimension & dim) const;
247 void draw(PainterInfo &, int x, int y) const;
251 Inset * clone() const;
253 MathData const * value_;
257 Inset * InsetMathWrapper::clone() const
259 return new InsetMathWrapper(*this);
263 void InsetMathWrapper::metrics(MetricsInfo & mi, Dimension & dim) const
265 value_->metrics(mi, dim);
266 //metricsMarkers2(dim);
270 void InsetMathWrapper::draw(PainterInfo & pi, int x, int y) const
272 value_->draw(pi, x, y);
273 //drawMarkers(pi, x, y);
277 ///////////////////////////////////////////////////////////////////////
278 class InsetColoredCell : public InsetMathNest {
281 InsetColoredCell(ColorCode min, ColorCode max);
283 InsetColoredCell(ColorCode min, ColorCode max, MathAtom const & atom);
285 void draw(PainterInfo &, int x, int y) const;
287 void metrics(MetricsInfo & mi, Dimension & dim) const;
291 Inset * clone() const;
299 InsetColoredCell::InsetColoredCell(ColorCode min, ColorCode max)
300 : InsetMathNest(1), min_(min), max_(max)
305 InsetColoredCell::InsetColoredCell(ColorCode min, ColorCode max, MathAtom const & atom)
306 : InsetMathNest(1), min_(min), max_(max)
308 cell(0).insert(0, atom);
312 Inset * InsetColoredCell::clone() const
314 return new InsetColoredCell(*this);
318 void InsetColoredCell::metrics(MetricsInfo & mi, Dimension & dim) const
320 cell(0).metrics(mi, dim);
324 void InsetColoredCell::draw(PainterInfo & pi, int x, int y) const
326 pi.pain.enterMonochromeMode(min_, max_);
327 cell(0).draw(pi, x, y);
328 pi.pain.leaveMonochromeMode();
332 ///////////////////////////////////////////////////////////////////////
334 class InsetNameWrapper : public InsetMathWrapper {
337 InsetNameWrapper(MathData const * value, MathMacroTemplate const & parent);
339 void metrics(MetricsInfo & mi, Dimension & dim) const;
341 void draw(PainterInfo &, int x, int y) const;
345 MathMacroTemplate const & parent_;
347 Inset * clone() const;
351 InsetNameWrapper::InsetNameWrapper(MathData const * value,
352 MathMacroTemplate const & parent)
353 : InsetMathWrapper(value), parent_(parent)
358 Inset * InsetNameWrapper::clone() const
360 return new InsetNameWrapper(*this);
364 void InsetNameWrapper::metrics(MetricsInfo & mi, Dimension & dim) const
366 InsetMathWrapper::metrics(mi, dim);
367 dim.wid += mathed_string_width(mi.base.font, from_ascii("\\"));
371 void InsetNameWrapper::draw(PainterInfo & pi, int x, int y) const
374 PainterInfo namepi = pi;
375 if (parent_.validMacro())
376 namepi.base.font.setColor(Color_latex);
378 namepi.base.font.setColor(Color_error);
381 pi.pain.text(x, y, from_ascii("\\"), namepi.base.font);
382 x += mathed_string_width(namepi.base.font, from_ascii("\\"));
385 InsetMathWrapper::draw(namepi, x, y);
389 ///////////////////////////////////////////////////////////////////////
392 MathMacroTemplate::MathMacroTemplate()
393 : InsetMathNest(3), numargs_(0), argsInLook_(0), optionals_(0),
394 type_(MacroTypeNewcommand), lookOutdated_(true)
400 MathMacroTemplate::MathMacroTemplate(docstring const & name, int numargs,
401 int optionals, MacroType type,
402 vector<MathData> const & optionalValues,
403 MathData const & def, MathData const & display)
404 : InsetMathNest(optionals + 3), numargs_(numargs), argsInLook_(numargs),
405 optionals_(optionals), optionalValues_(optionalValues),
406 type_(type), lookOutdated_(true)
411 lyxerr << "MathMacroTemplate::MathMacroTemplate: wrong # of arguments: "
414 asArray(name, cell(0));
415 optionalValues_.resize(9);
416 for (int i = 0; i < optionals_; ++i)
417 cell(optIdx(i)) = optionalValues_[i];
418 cell(defIdx()) = def;
419 cell(displayIdx()) = display;
425 MathMacroTemplate::MathMacroTemplate(docstring const & str)
426 : InsetMathNest(3), numargs_(0), optionals_(0),
427 type_(MacroTypeNewcommand), lookOutdated_(true)
432 mathed_parse_cell(ar, str);
433 if (ar.size() != 1 || !ar[0]->asMacroTemplate()) {
434 lyxerr << "Cannot read macro from '" << ar << "'" << endl;
435 asArray(from_ascii("invalidmacro"), cell(0));
436 // FIXME: The macro template does not make sense after this.
437 // The whole parsing should not be in a constructor which
438 // has no chance to report failure.
441 operator=( *(ar[0]->asMacroTemplate()) );
447 Inset * MathMacroTemplate::clone() const
449 MathMacroTemplate * inset = new MathMacroTemplate(*this);
450 // the parent pointers of the proxy insets above will point to
451 // to the old template. Hence, the look must be updated.
457 docstring MathMacroTemplate::name() const
459 return asString(cell(0));
463 void MathMacroTemplate::updateToContext(MacroContext const & mc) const
465 redefinition_ = mc.get(name()) != 0;
469 void MathMacroTemplate::updateLook() const
471 lookOutdated_ = true;
475 void MathMacroTemplate::createLook(int args) const
481 look_.push_back(MathAtom(new InsetLabelBox(_("Name"), *this, false)));
482 MathData & nameData = look_[look_.size() - 1].nucleus()->cell(0);
483 nameData.push_back(MathAtom(new InsetNameWrapper(&cell(0), *this)));
487 if (optionals_ > 0) {
488 look_.push_back(MathAtom(new InsetLabelBox(_("optional"), *this, false)));
490 MathData * optData = &look_[look_.size() - 1].nucleus()->cell(0);
491 for (; i < optionals_; ++i) {
492 // color it light grey, if it is to be removed when the cursor leaves
493 if (i == argsInLook_) {
494 optData->push_back(MathAtom(
495 new InsetColoredCell(Color_mathbg, Color_mathmacrooldarg)));
496 optData = &(*optData)[optData->size() - 1].nucleus()->cell(0);
499 optData->push_back(MathAtom(new InsetMathChar('[')));
500 optData->push_back(MathAtom(new InsetMathWrapper(&cell(1 + i))));
501 optData->push_back(MathAtom(new InsetMathChar(']')));
506 for (; i < numargs_; ++i) {
508 arg.push_back(MathAtom(new MathMacroArgument(i + 1)));
509 if (i >= argsInLook_) {
510 look_.push_back(MathAtom(new InsetColoredCell(
511 Color_mathbg, Color_mathmacrooldarg,
512 MathAtom(new InsetMathBrace(arg)))));
514 look_.push_back(MathAtom(new InsetMathBrace(arg)));
516 for (; i < argsInLook_; ++i) {
518 arg.push_back(MathAtom(new MathMacroArgument(i + 1)));
519 look_.push_back(MathAtom(new InsetColoredCell(
520 Color_mathbg, Color_mathmacronewarg,
521 MathAtom(new InsetMathBrace(arg)))));
525 look_.push_back(MathAtom(new InsetMathChar(':')));
526 look_.push_back(MathAtom(new InsetMathChar('=')));
529 look_.push_back(MathAtom(
530 new InsetLabelBox(MathAtom(
531 new InsetMathWrapper(&cell(defIdx()))), _("TeX"), *this, true)));
534 look_.push_back(MathAtom(
535 new DisplayLabelBox(MathAtom(
536 new InsetMathWrapper(&cell(displayIdx()))), _("LyX"), *this)));
540 void MathMacroTemplate::metrics(MetricsInfo & mi, Dimension & dim) const
542 FontSetChanger dummy1(mi.base, from_ascii("mathnormal"));
543 StyleChanger dummy2(mi.base, LM_ST_TEXT);
546 MacroData const * macro = 0;
548 macro = mi.macrocontext.get(name());
550 // updateToContext() - avoids another lookup
551 redefinition_ = macro != 0;
555 int argsInDef = maxArgumentInDefinition();
556 if (lookOutdated_ || argsInDef != argsInLook_) {
557 lookOutdated_ = false;
558 createLook(argsInDef);
561 /// metrics for inset contents
565 // first phase, premetric:
567 look_.metrics(mi, dim);
568 labelBoxAscent_ = dim.asc;
569 labelBoxDescent_ = dim.des;
571 // second phase, main metric:
573 look_.metrics(mi, dim);
582 setDimCache(mi, dim);
586 void MathMacroTemplate::draw(PainterInfo & pi, int x, int y) const
588 FontSetChanger dummy1(pi.base, from_ascii("mathnormal"));
589 StyleChanger dummy2(pi.base, LM_ST_TEXT);
591 setPosCache(pi, x, y);
592 Dimension const dim = dimension(*pi.base.bv);
595 int const a = y - dim.asc + 1;
596 int const w = dim.wid - 2;
597 int const h = dim.height() - 2;
598 pi.pain.rectangle(x, a, w, h, Color_mathframe);
600 // just to be sure: set some dummy values for coord cache
601 for (idx_type i = 0; i < nargs(); ++i)
602 cell(i).setXY(*pi.base.bv, x, y);
605 look_.draw(pi, x + 3, y);
609 void MathMacroTemplate::edit(Cursor & cur, bool front, EntryDirection entry_from)
612 cur.updateFlags(Update::SinglePar);
613 InsetMathNest::edit(cur, front, entry_from);
617 bool MathMacroTemplate::notifyCursorLeaves(Cursor const & old, Cursor & cur)
619 // find this in cursor old
620 Cursor insetCur = old;
621 int scriptSlice = insetCur.find(this);
622 LASSERT(scriptSlice != -1, /**/);
623 insetCur.cutOff(scriptSlice);
625 commitEditChanges(insetCur);
627 cur.updateFlags(Update::Force);
628 return InsetMathNest::notifyCursorLeaves(old, cur);
632 void MathMacroTemplate::removeArguments(Cursor & cur, int from, int to) {
633 for (DocIterator it = doc_iterator_begin(*this); it; it.forwardChar()) {
636 if (it.nextInset()->lyxCode() != MATHMACROARG_CODE)
638 MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
639 int n = arg->number() - 1;
640 if (from <= n && n <= to) {
641 int cellSlice = cur.find(it.cell());
642 if (cellSlice != -1 && cur[cellSlice].pos() > it.pos())
643 --cur[cellSlice].pos();
645 it.cell().erase(it.pos());
653 void MathMacroTemplate::shiftArguments(size_t from, int by) {
654 for (DocIterator it = doc_iterator_begin(*this); it; it.forwardChar()) {
657 if (it.nextInset()->lyxCode() != MATHMACROARG_CODE)
659 MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
660 if (arg->number() >= int(from) + 1)
661 arg->setNumber(arg->number() + by);
668 int MathMacroTemplate::maxArgumentInDefinition() const
671 MathMacroTemplate * nonConst = const_cast<MathMacroTemplate *>(this);
672 DocIterator it = doc_iterator_begin(*nonConst);
674 for (; it; it.forwardChar()) {
677 if (it.nextInset()->lyxCode() != MATHMACROARG_CODE)
679 MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
680 maxArg = std::max(int(arg->number()), maxArg);
686 void MathMacroTemplate::insertMissingArguments(int maxArg)
688 bool found[9] = { false, false, false, false, false, false, false, false, false };
689 idx_type idx = cell(displayIdx()).empty() ? defIdx() : displayIdx();
691 // search for #n macros arguments
692 DocIterator it = doc_iterator_begin(*this);
694 for (; it && it[0].idx() == idx; it.forwardChar()) {
697 if (it.nextInset()->lyxCode() != MATHMACROARG_CODE)
699 MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
700 found[arg->number() - 1] = true;
704 for (int i = 0; i < maxArg; ++i) {
708 cell(idx).push_back(MathAtom(new MathMacroArgument(i + 1)));
713 void MathMacroTemplate::changeArity(Cursor & cur, int newNumArg)
715 // remove parameter which do not appear anymore in the definition
716 for (int i = numargs_; i > newNumArg; --i)
717 removeParameter(cur, numargs_ - 1, false);
719 // add missing parameter
720 for (int i = numargs_; i < newNumArg; ++i)
721 insertParameter(cur, numargs_, false, false);
725 void MathMacroTemplate::commitEditChanges(Cursor & cur)
727 int argsInDef = maxArgumentInDefinition();
728 if (argsInDef != numargs_) {
729 cur.recordUndoFullDocument();
730 changeArity(cur, argsInDef);
732 insertMissingArguments(argsInDef);
736 // FIXME: factorize those functions here with a functional style, maybe using Boost's function
739 void fixMacroInstancesAddRemove(Cursor const & from, docstring const & name, int n, bool insert) {
742 for (; dit; dit.forwardPos()) {
743 // only until a macro is redefined
744 if (dit.inset().lyxCode() == MATHMACRO_CODE) {
745 MathMacroTemplate const & macroTemplate
746 = static_cast<MathMacroTemplate const &>(dit.inset());
747 if (macroTemplate.name() == name)
751 // in front of macro instance?
752 Inset * inset = dit.nextInset();
755 InsetMath * insetMath = inset->asInsetMath();
759 MathMacro * macro = insetMath->asMacro();
760 if (macro && macro->name() == name && macro->folded()) {
761 // found macro instance
763 macro->insertArgument(n);
765 macro->removeArgument(n);
771 void fixMacroInstancesOptional(Cursor const & from, docstring const & name, int optionals) {
774 for (; dit; dit.forwardPos()) {
775 // only until a macro is redefined
776 if (dit.inset().lyxCode() == MATHMACRO_CODE) {
777 MathMacroTemplate const & macroTemplate
778 = static_cast<MathMacroTemplate const &>(dit.inset());
779 if (macroTemplate.name() == name)
783 // in front of macro instance?
784 Inset * inset = dit.nextInset();
787 InsetMath * insetMath = inset->asInsetMath();
790 MathMacro * macro = insetMath->asMacro();
791 if (macro && macro->name() == name && macro->folded()) {
792 // found macro instance
793 macro->setOptionals(optionals);
800 void fixMacroInstancesFunctional(Cursor const & from,
801 docstring const & name, F & fix) {
804 for (; dit; dit.forwardPos()) {
805 // only until a macro is redefined
806 if (dit.inset().lyxCode() == MATHMACRO_CODE) {
807 MathMacroTemplate const & macroTemplate
808 = static_cast<MathMacroTemplate const &>(dit.inset());
809 if (macroTemplate.name() == name)
813 // in front of macro instance?
814 Inset * inset = dit.nextInset();
817 InsetMath * insetMath = inset->asInsetMath();
820 MathMacro * macro = insetMath->asMacro();
821 if (macro && macro->name() == name && macro->folded())
827 void MathMacroTemplate::insertParameter(Cursor & cur, int pos, bool greedy, bool addarg)
829 if (pos <= numargs_ && pos >= optionals_ && numargs_ < 9) {
834 shiftArguments(pos, 1);
836 cell(defIdx()).push_back(MathAtom(new MathMacroArgument(pos + 1)));
837 if (!cell(displayIdx()).empty())
838 cell(displayIdx()).push_back(MathAtom(new MathMacroArgument(pos + 1)));
843 dit.leaveInset(*this);
844 // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
845 dit.top().forwardPos();
847 // fix macro instances
848 fixMacroInstancesAddRemove(dit, name(), pos, true);
856 void MathMacroTemplate::removeParameter(Cursor & cur, int pos, bool greedy)
858 if (pos < numargs_ && pos >= 0) {
860 removeArguments(cur, pos, pos);
861 shiftArguments(pos + 1, -1);
863 // removed optional parameter?
864 if (pos < optionals_) {
866 optionalValues_[pos] = cell(optIdx(pos));
867 cells_.erase(cells_.begin() + optIdx(pos));
870 int macroSlice = cur.find(this);
871 if (macroSlice != -1) {
872 if (cur[macroSlice].idx() == optIdx(pos)) {
873 cur.cutOff(macroSlice);
874 cur[macroSlice].idx() = 1;
875 cur[macroSlice].pos() = 0;
876 } else if (cur[macroSlice].idx() > optIdx(pos))
877 --cur[macroSlice].idx();
882 // fix macro instances
883 //boost::function<void(MathMacro *)> fix = _1->insertArgument(n);
884 //fixMacroInstancesFunctional(dit, name(), fix);
886 dit.leaveInset(*this);
887 // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
888 dit.top().forwardPos();
889 fixMacroInstancesAddRemove(dit, name(), pos, false);
897 void MathMacroTemplate::makeOptional(Cursor & cur) {
898 if (numargs_ > 0 && optionals_ < numargs_) {
900 cells_.insert(cells_.begin() + optIdx(optionals_ - 1), optionalValues_[optionals_ - 1]);
902 int macroSlice = cur.find(this);
903 if (macroSlice != -1 && cur[macroSlice].idx() >= optIdx(optionals_ - 1))
904 ++cur[macroSlice].idx();
906 // fix macro instances
908 dit.leaveInset(*this);
909 // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
910 dit.top().forwardPos();
911 fixMacroInstancesOptional(dit, name(), optionals_);
918 void MathMacroTemplate::makeNonOptional(Cursor & cur) {
919 if (numargs_ > 0 && optionals_ > 0) {
922 // store default value for later if the user changes his mind
923 optionalValues_[optionals_] = cell(optIdx(optionals_));
924 cells_.erase(cells_.begin() + optIdx(optionals_));
927 int macroSlice = cur.find(this);
928 if (macroSlice != -1) {
929 if (cur[macroSlice].idx() > optIdx(optionals_))
930 --cur[macroSlice].idx();
931 else if (cur[macroSlice].idx() == optIdx(optionals_)) {
932 cur.cutOff(macroSlice);
933 cur[macroSlice].idx() = optIdx(optionals_);
934 cur[macroSlice].pos() = 0;
938 // fix macro instances
940 dit.leaveInset(*this);
941 // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
942 dit.top().forwardPos();
943 fixMacroInstancesOptional(dit, name(), optionals_);
950 void MathMacroTemplate::doDispatch(Cursor & cur, FuncRequest & cmd)
952 string const arg = to_utf8(cmd.argument());
953 switch (cmd.action) {
955 case LFUN_MATH_MACRO_ADD_PARAM:
957 commitEditChanges(cur);
958 cur.recordUndoFullDocument();
959 size_t pos = numargs_;
961 pos = (size_t)convert<int>(arg) - 1; // it is checked for >=0 in getStatus
962 insertParameter(cur, pos);
967 case LFUN_MATH_MACRO_REMOVE_PARAM:
969 commitEditChanges(cur);
970 cur.recordUndoFullDocument();
971 size_t pos = numargs_ - 1;
973 pos = (size_t)convert<int>(arg) - 1; // it is checked for >=0 in getStatus
974 removeParameter(cur, pos);
978 case LFUN_MATH_MACRO_APPEND_GREEDY_PARAM:
980 commitEditChanges(cur);
981 cur.recordUndoFullDocument();
982 insertParameter(cur, numargs_, true);
986 case LFUN_MATH_MACRO_REMOVE_GREEDY_PARAM:
988 commitEditChanges(cur);
989 cur.recordUndoFullDocument();
990 removeParameter(cur, numargs_ - 1, true);
994 case LFUN_MATH_MACRO_MAKE_OPTIONAL:
995 commitEditChanges(cur);
996 cur.recordUndoFullDocument();
1000 case LFUN_MATH_MACRO_MAKE_NONOPTIONAL:
1001 commitEditChanges(cur);
1002 cur.recordUndoFullDocument();
1003 makeNonOptional(cur);
1006 case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM:
1008 commitEditChanges(cur);
1009 cur.recordUndoFullDocument();
1010 insertParameter(cur, optionals_);
1015 case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM:
1016 if (optionals_ > 0) {
1017 commitEditChanges(cur);
1018 cur.recordUndoFullDocument();
1019 removeParameter(cur, optionals_ - 1);
1022 case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
1023 if (numargs_ == optionals_) {
1024 commitEditChanges(cur);
1025 cur.recordUndoFullDocument();
1026 insertParameter(cur, 0, true);
1032 InsetMathNest::doDispatch(cur, cmd);
1038 bool MathMacroTemplate::getStatus(Cursor & /*cur*/, FuncRequest const & cmd,
1039 FuncStatus & flag) const
1042 string const arg = to_utf8(cmd.argument());
1043 switch (cmd.action) {
1044 case LFUN_MATH_MACRO_ADD_PARAM: {
1045 int num = numargs_ + 1;
1046 if (arg.size() != 0)
1047 num = convert<int>(arg);
1048 bool on = (num >= optionals_
1049 && numargs_ < 9 && num <= numargs_ + 1);
1050 flag.setEnabled(on);
1054 case LFUN_MATH_MACRO_APPEND_GREEDY_PARAM:
1055 flag.setEnabled(numargs_ < 9);
1058 case LFUN_MATH_MACRO_REMOVE_PARAM: {
1060 if (arg.size() != 0)
1061 num = convert<int>(arg);
1062 flag.setEnabled(num >= 1 && num <= numargs_);
1066 case LFUN_MATH_MACRO_MAKE_OPTIONAL:
1067 flag.setEnabled(numargs_ > 0
1068 && optionals_ < numargs_
1069 && type_ != MacroTypeDef);
1072 case LFUN_MATH_MACRO_MAKE_NONOPTIONAL:
1073 flag.setEnabled(optionals_ > 0
1074 && type_ != MacroTypeDef);
1077 case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM:
1078 flag.setEnabled(numargs_ < 9);
1081 case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM:
1082 flag.setEnabled(optionals_ > 0);
1085 case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
1086 flag.setEnabled(numargs_ == 0
1087 && type_ != MacroTypeDef);
1090 case LFUN_IN_MATHMACROTEMPLATE:
1091 flag.setEnabled(true);
1102 void MathMacroTemplate::read(Lexer & lex)
1105 mathed_parse_cell(ar, lex.getStream());
1106 if (ar.size() != 1 || !ar[0]->asMacroTemplate()) {
1107 lyxerr << "Cannot read macro from '" << ar << "'" << endl;
1108 lyxerr << "Read: " << to_utf8(asString(ar)) << endl;
1111 operator=( *(ar[0]->asMacroTemplate()) );
1117 void MathMacroTemplate::write(ostream & os) const
1119 odocstringstream oss;
1120 WriteStream wi(oss, false, false);
1121 oss << "FormulaMacro\n";
1123 os << to_utf8(oss.str());
1127 void MathMacroTemplate::write(WriteStream & os) const
1133 void MathMacroTemplate::write(WriteStream & os, bool overwriteRedefinition) const
1136 if (optionals_ > 0) {
1137 // macros with optionals use the xargs package, e.g.:
1138 // \newcommandx{\foo}[2][usedefault, addprefix=\global,1=default]{#1,#2}
1139 if (redefinition_ && !overwriteRedefinition)
1140 os << "\\renewcommandx";
1142 os << "\\newcommandx";
1144 os << "\\" << name().c_str()
1145 << "[" << numargs_ << "]"
1146 << "[usedefault, addprefix=\\global";
1147 for (int i = 0; i < optionals_; ++i) {
1148 docstring optValue = asString(cell(optIdx(i)));
1149 if (optValue.find(']') != docstring::npos
1150 || optValue.find(',') != docstring::npos)
1151 os << ", " << i + 1 << "="
1152 << "{" << cell(optIdx(i)) << "}";
1154 os << ", " << i + 1 << "="
1159 // macros without optionals use standard _global_ \def macros:
1160 // \global\def\foo#1#2{#1,#2}
1161 os << "\\global\\def\\" << name().c_str();
1162 docstring param = from_ascii("#0");
1163 for (int i = 1; i <= numargs_; ++i) {
1169 // in LyX output we use some pseudo syntax which is implementation
1170 // independent, e.g.
1171 // \newcommand{\foo}[2][default}{#1,#2}
1172 if (redefinition_ && !overwriteRedefinition)
1173 os << "\\renewcommand";
1175 os << "\\newcommand";
1176 os << "{\\" << name().c_str() << '}';
1178 os << '[' << numargs_ << ']';
1180 for (int i = 0; i < optionals_; ++i) {
1181 docstring optValue = asString(cell(optIdx(i)));
1182 if (optValue.find(']') != docstring::npos)
1183 os << "[{" << cell(optIdx(i)) << "}]";
1185 os << "[" << cell(optIdx(i)) << "]";
1189 os << "{" << cell(defIdx()) << "}";
1192 // writing .tex. done.
1195 // writing .lyx, write special .tex export only if necessary
1196 if (!cell(displayIdx()).empty())
1197 os << "\n{" << cell(displayIdx()) << '}';
1202 int MathMacroTemplate::plaintext(odocstream & os,
1203 OutputParams const &) const
1205 static docstring const str = '[' + buffer().B_("math macro") + ']';
1212 bool MathMacroTemplate::validName() const
1214 docstring n = name();
1220 // converting back and force doesn't swallow anything?
1223 if (asString(ma) != n)
1226 // valid characters?
1227 for (size_t i = 0; i < n.size(); ++i) {
1228 if (!(n[i] >= 'a' && n[i] <= 'z')
1229 && !(n[i] >= 'A' && n[i] <= 'Z')
1238 bool MathMacroTemplate::validMacro() const
1244 bool MathMacroTemplate::fixNameAndCheckIfValid()
1246 // check all the characters/insets in the name cell
1248 MathData & data = cell(0);
1249 while (i < data.size()) {
1250 InsetMathChar const * cinset = data[i]->asCharInset();
1252 // valid character in [a-zA-Z]?
1253 char_type c = cinset->getChar();
1254 if ((c >= 'a' && c <= 'z')
1255 || (c >= 'A' && c <= 'Z')) {
1265 // now it should be valid if anything in the name survived
1266 return data.size() > 0;
1270 void MathMacroTemplate::validate(LaTeXFeatures & features) const
1272 // we need global optional macro arguments. They are not available
1273 // with \def, and \newcommand does not support global macros. So we
1274 // are bound to xargs also for the single-optional-parameter case.
1276 features.require("xargs");
1279 void MathMacroTemplate::getDefaults(vector<docstring> & defaults) const
1281 defaults.resize(numargs_);
1282 for (int i = 0; i < optionals_; ++i)
1283 defaults[i] = asString(cell(optIdx(i)));
1287 docstring MathMacroTemplate::definition() const
1289 return asString(cell(defIdx()));
1293 docstring MathMacroTemplate::displayDefinition() const
1295 return asString(cell(displayIdx()));
1299 size_t MathMacroTemplate::numArgs() const
1305 size_t MathMacroTemplate::numOptionals() const
1311 void MathMacroTemplate::infoize(odocstream & os) const
1313 os << "Math Macro: \\" << name();
1317 docstring MathMacroTemplate::contextMenu(BufferView const &, int, int) const
1319 return from_ascii("context-math-macro-definition");