2 * \file MathMacroTemplate.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
7 * \author Stefan Schimanski
9 * Full author contact details are available in file CREDITS.
14 #include "MathMacroTemplate.h"
16 #include "DocIterator.h"
17 #include "LaTeXFeatures.h"
18 #include "InsetMathBrace.h"
19 #include "InsetMathChar.h"
20 #include "InsetMathSqrt.h"
21 #include "MathMacro.h"
22 #include "MathMacroArgument.h"
23 #include "MathStream.h"
24 #include "MathParser.h"
25 #include "MathSupport.h"
26 #include "MathMacroArgument.h"
29 #include "BufferView.h"
32 #include "DispatchResult.h"
33 #include "FuncRequest.h"
34 #include "FuncStatus.h"
38 #include "frontends/Painter.h"
40 #include "support/lassert.h"
41 #include "support/convert.h"
42 #include "support/debug.h"
43 #include "support/gettext.h"
44 #include "support/docstream.h"
45 #include "support/lstrings.h"
53 using support::bformat
;
55 //////////////////////////////////////////////////////////////////////
57 class InsetLabelBox
: public InsetMathNest
{
60 InsetLabelBox(Buffer
* buf
, MathAtom
const & atom
, docstring label
,
61 MathMacroTemplate
const & parent
, bool frame
= false);
62 InsetLabelBox(Buffer
* buf
, docstring label
, MathMacroTemplate
const & parent
,
65 void metrics(MetricsInfo
& mi
, Dimension
& dim
) const;
67 void draw(PainterInfo
&, int x
, int y
) const;
71 MathMacroTemplate
const & parent_
;
73 Inset
* clone() const;
75 docstring
const label_
;
81 InsetLabelBox::InsetLabelBox(Buffer
* buf
, MathAtom
const & atom
, docstring label
,
82 MathMacroTemplate
const & parent
, bool frame
)
83 : InsetMathNest(buf
, 1), parent_(parent
), label_(label
), frame_(frame
)
85 cell(0).insert(0, atom
);
89 InsetLabelBox::InsetLabelBox(Buffer
* buf
, docstring label
,
90 MathMacroTemplate
const & parent
, bool frame
)
91 : InsetMathNest(buf
, 1), parent_(parent
), label_(label
), frame_(frame
)
96 Inset
* InsetLabelBox::clone() const
98 return new InsetLabelBox(*this);
102 void InsetLabelBox::metrics(MetricsInfo
& mi
, Dimension
& dim
) const
105 cell(0).metrics(mi
, dim
);
114 // adjust to common height in main metrics phase
115 if (!parent_
.premetrics()) {
116 dim
.asc
= max(dim
.asc
, parent_
.commonLabelBoxAscent());
117 dim
.des
= max(dim
.des
, parent_
.commonLabelBoxDescent());
121 if (parent_
.editing(mi
.base
.bv
) && label_
.length() > 0) {
123 FontInfo font
= sane_font
;
124 font
.setSize(FONT_SIZE_TINY
);
125 font
.setColor(Color_mathmacrolabel
);
127 // make space for label and box
128 int lwid
= mathed_string_width(font
, label_
);
131 math_font_max_dim(font
, maxasc
, maxdes
);
133 dim
.wid
= max(dim
.wid
, lwid
+ 2);
135 // space for the label
136 if (!parent_
.premetrics())
137 dim
.des
+= maxasc
+ maxdes
+ 1;
142 void InsetLabelBox::draw(PainterInfo
& pi
, int x
, int y
) const
144 Dimension
const dim
= dimension(*pi
.base
.bv
);
145 Dimension
const cdim
= cell(0).dimension(*pi
.base
.bv
);
148 cell(0).draw(pi
, x
+ (dim
.wid
- cdim
.wid
) / 2, y
);
151 if (parent_
.editing(pi
.base
.bv
) && label_
.length() > 0) {
153 FontInfo font
= sane_font
;
154 font
.setSize(FONT_SIZE_TINY
);
155 font
.setColor(Color_mathmacrolabel
);
157 // make space for label and box
158 int lwid
= mathed_string_width(font
, label_
);
161 math_font_max_dim(font
, maxasc
, maxdes
);
164 pi
.pain
.text(x
+ (dim
.wid
- lwid
) / 2, y
+ dim
.des
- maxdes
, label_
, font
);
166 pi
.pain
.text(x
, y
+ dim
.des
- maxdes
, label_
, font
);
170 int boxHeight
= parent_
.commonLabelBoxAscent() + parent_
.commonLabelBoxDescent();
172 pi
.pain
.rectangle(x
+ 1, y
- dim
.ascent() + 1,
173 dim
.wid
- 2, boxHeight
- 2,
179 //////////////////////////////////////////////////////////////////////
181 class DisplayLabelBox
: public InsetLabelBox
{
184 DisplayLabelBox(Buffer
* buf
, MathAtom
const & atom
, docstring label
,
185 MathMacroTemplate
const & parent
);
188 void metrics(MetricsInfo
& mi
, Dimension
& dim
) const;
190 void draw(PainterInfo
&, int x
, int y
) const;
194 Inset
* clone() const;
198 DisplayLabelBox::DisplayLabelBox(Buffer
* buf
, MathAtom
const & atom
,
200 MathMacroTemplate
const & parent
)
201 : InsetLabelBox(buf
, atom
, label
, parent
, true)
207 Inset
* DisplayLabelBox::clone() const
209 return new DisplayLabelBox(*this);
213 void DisplayLabelBox::metrics(MetricsInfo
& mi
, Dimension
& dim
) const
215 InsetLabelBox::metrics(mi
, dim
);
216 if (!parent_
.editing(mi
.base
.bv
)
217 && parent_
.cell(parent_
.displayIdx()).empty()) {
225 void DisplayLabelBox::draw(PainterInfo
& pi
, int x
, int y
) const
227 if (parent_
.editing(pi
.base
.bv
)
228 || !parent_
.cell(parent_
.displayIdx()).empty()) {
229 InsetLabelBox::draw(pi
, x
, y
);
231 bool enabled
= pi
.pain
.isDrawingEnabled();
232 pi
.pain
.setDrawingEnabled(false);
233 InsetLabelBox::draw(pi
, x
, y
);
234 pi
.pain
.setDrawingEnabled(enabled
);
239 //////////////////////////////////////////////////////////////////////
241 class InsetMathWrapper
: public InsetMath
{
244 InsetMathWrapper(MathData
const * value
) : value_(value
) {}
246 void metrics(MetricsInfo
& mi
, Dimension
& dim
) const;
248 void draw(PainterInfo
&, int x
, int y
) const;
252 Inset
* clone() const;
254 MathData
const * value_
;
258 Inset
* InsetMathWrapper::clone() const
260 return new InsetMathWrapper(*this);
264 void InsetMathWrapper::metrics(MetricsInfo
& mi
, Dimension
& dim
) const
266 value_
->metrics(mi
, dim
);
267 //metricsMarkers2(dim);
271 void InsetMathWrapper::draw(PainterInfo
& pi
, int x
, int y
) const
273 value_
->draw(pi
, x
, y
);
274 //drawMarkers(pi, x, y);
278 ///////////////////////////////////////////////////////////////////////
279 class InsetColoredCell
: public InsetMathNest
{
282 InsetColoredCell(Buffer
* buf
, ColorCode min
, ColorCode max
);
284 InsetColoredCell(Buffer
* buf
, ColorCode min
, ColorCode max
, MathAtom
const & atom
);
286 void draw(PainterInfo
&, int x
, int y
) const;
288 void metrics(MetricsInfo
& mi
, Dimension
& dim
) const;
292 Inset
* clone() const;
300 InsetColoredCell::InsetColoredCell(Buffer
* buf
, ColorCode min
, ColorCode max
)
301 : InsetMathNest(buf
, 1), min_(min
), max_(max
)
306 InsetColoredCell::InsetColoredCell(Buffer
* buf
, ColorCode min
, ColorCode max
, MathAtom
const & atom
)
307 : InsetMathNest(buf
, 1), min_(min
), max_(max
)
309 cell(0).insert(0, atom
);
313 Inset
* InsetColoredCell::clone() const
315 return new InsetColoredCell(*this);
319 void InsetColoredCell::metrics(MetricsInfo
& mi
, Dimension
& dim
) const
321 cell(0).metrics(mi
, dim
);
325 void InsetColoredCell::draw(PainterInfo
& pi
, int x
, int y
) const
327 pi
.pain
.enterMonochromeMode(min_
, max_
);
328 cell(0).draw(pi
, x
, y
);
329 pi
.pain
.leaveMonochromeMode();
333 ///////////////////////////////////////////////////////////////////////
335 class InsetNameWrapper
: public InsetMathWrapper
{
338 InsetNameWrapper(MathData
const * value
, MathMacroTemplate
const & parent
);
340 void metrics(MetricsInfo
& mi
, Dimension
& dim
) const;
342 void draw(PainterInfo
&, int x
, int y
) const;
346 MathMacroTemplate
const & parent_
;
348 Inset
* clone() const;
352 InsetNameWrapper::InsetNameWrapper(MathData
const * value
,
353 MathMacroTemplate
const & parent
)
354 : InsetMathWrapper(value
), parent_(parent
)
359 Inset
* InsetNameWrapper::clone() const
361 return new InsetNameWrapper(*this);
365 void InsetNameWrapper::metrics(MetricsInfo
& mi
, Dimension
& dim
) const
367 InsetMathWrapper::metrics(mi
, dim
);
368 dim
.wid
+= mathed_string_width(mi
.base
.font
, from_ascii("\\"));
372 void InsetNameWrapper::draw(PainterInfo
& pi
, int x
, int y
) const
375 PainterInfo namepi
= pi
;
376 if (parent_
.validMacro())
377 namepi
.base
.font
.setColor(Color_latex
);
379 namepi
.base
.font
.setColor(Color_error
);
382 pi
.pain
.text(x
, y
, from_ascii("\\"), namepi
.base
.font
);
383 x
+= mathed_string_width(namepi
.base
.font
, from_ascii("\\"));
386 InsetMathWrapper::draw(namepi
, x
, y
);
390 ///////////////////////////////////////////////////////////////////////
393 MathMacroTemplate::MathMacroTemplate(Buffer
* buf
)
394 : InsetMathNest(buf
, 3), numargs_(0), argsInLook_(0), optionals_(0),
395 type_(MacroTypeNewcommand
), lookOutdated_(true)
401 MathMacroTemplate::MathMacroTemplate(Buffer
* buf
, docstring
const & name
, int numargs
,
402 int optionals
, MacroType type
, vector
<MathData
> const & optionalValues
,
403 MathData
const & def
, MathData
const & display
)
404 : InsetMathNest(buf
, 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(Buffer
* buf
, docstring
const & str
)
426 : InsetMathNest(buf
, 3), numargs_(0), optionals_(0),
427 type_(MacroTypeNewcommand
), lookOutdated_(true)
433 mathed_parse_cell(ar
, str
, Parse::NORMAL
);
434 if (ar
.size() != 1 || !ar
[0]->asMacroTemplate()) {
435 lyxerr
<< "Cannot read macro from '" << ar
<< "'" << endl
;
436 asArray(from_ascii("invalidmacro"), cell(0));
437 // FIXME: The macro template does not make sense after this.
438 // The whole parsing should not be in a constructor which
439 // has no chance to report failure.
442 operator=( *(ar
[0]->asMacroTemplate()) );
448 Inset
* MathMacroTemplate::clone() const
450 MathMacroTemplate
* inset
= new MathMacroTemplate(*this);
451 // the parent pointers of the proxy insets above will point to
452 // to the old template. Hence, the look must be updated.
458 docstring
MathMacroTemplate::name() const
460 return asString(cell(0));
464 void MathMacroTemplate::updateToContext(MacroContext
const & mc
) const
466 redefinition_
= mc
.get(name()) != 0;
470 void MathMacroTemplate::updateLook() const
472 lookOutdated_
= true;
476 void MathMacroTemplate::createLook(int args
) const
482 look_
.push_back(MathAtom(
483 new InsetLabelBox(buffer_
, _("Name"), *this, false)));
484 MathData
& nameData
= look_
[look_
.size() - 1].nucleus()->cell(0);
485 nameData
.push_back(MathAtom(new InsetNameWrapper(&cell(0), *this)));
489 if (optionals_
> 0) {
490 look_
.push_back(MathAtom(
491 new InsetLabelBox(buffer_
, _("optional"), *this, false)));
493 MathData
* optData
= &look_
[look_
.size() - 1].nucleus()->cell(0);
494 for (; i
< optionals_
; ++i
) {
495 // color it light grey, if it is to be removed when the cursor leaves
496 if (i
== argsInLook_
) {
497 optData
->push_back(MathAtom(
498 new InsetColoredCell(buffer_
, Color_mathbg
, Color_mathmacrooldarg
)));
499 optData
= &(*optData
)[optData
->size() - 1].nucleus()->cell(0);
502 optData
->push_back(MathAtom(new InsetMathChar('[')));
503 optData
->push_back(MathAtom(new InsetMathWrapper(&cell(1 + i
))));
504 optData
->push_back(MathAtom(new InsetMathChar(']')));
509 for (; i
< numargs_
; ++i
) {
511 arg
.push_back(MathAtom(new MathMacroArgument(i
+ 1)));
512 if (i
>= argsInLook_
) {
513 look_
.push_back(MathAtom(new InsetColoredCell(buffer_
,
514 Color_mathbg
, Color_mathmacrooldarg
,
515 MathAtom(new InsetMathBrace(arg
)))));
517 look_
.push_back(MathAtom(new InsetMathBrace(arg
)));
519 for (; i
< argsInLook_
; ++i
) {
521 arg
.push_back(MathAtom(new MathMacroArgument(i
+ 1)));
522 look_
.push_back(MathAtom(new InsetColoredCell(buffer_
,
523 Color_mathbg
, Color_mathmacronewarg
,
524 MathAtom(new InsetMathBrace(arg
)))));
528 look_
.push_back(MathAtom(new InsetMathChar(':')));
529 look_
.push_back(MathAtom(new InsetMathChar('=')));
532 look_
.push_back(MathAtom(
533 new InsetLabelBox(buffer_
, MathAtom(
534 new InsetMathWrapper(&cell(defIdx()))), _("TeX"), *this, true)));
537 look_
.push_back(MathAtom(
538 new DisplayLabelBox(buffer_
, MathAtom(
539 new InsetMathWrapper(&cell(displayIdx()))), _("LyX"), *this)));
543 void MathMacroTemplate::metrics(MetricsInfo
& mi
, Dimension
& dim
) const
545 FontSetChanger
dummy1(mi
.base
, from_ascii("mathnormal"));
546 StyleChanger
dummy2(mi
.base
, LM_ST_TEXT
);
549 MacroData
const * macro
= 0;
551 macro
= mi
.macrocontext
.get(name());
553 // updateToContext() - avoids another lookup
554 redefinition_
= macro
!= 0;
558 int argsInDef
= maxArgumentInDefinition();
559 if (lookOutdated_
|| argsInDef
!= argsInLook_
) {
560 lookOutdated_
= false;
561 createLook(argsInDef
);
564 /// metrics for inset contents
568 // first phase, premetric:
570 look_
.metrics(mi
, dim
);
571 labelBoxAscent_
= dim
.asc
;
572 labelBoxDescent_
= dim
.des
;
574 // second phase, main metric:
576 look_
.metrics(mi
, dim
);
585 setDimCache(mi
, dim
);
589 void MathMacroTemplate::draw(PainterInfo
& pi
, int x
, int y
) const
591 FontSetChanger
dummy1(pi
.base
, from_ascii("mathnormal"));
592 StyleChanger
dummy2(pi
.base
, LM_ST_TEXT
);
594 setPosCache(pi
, x
, y
);
595 Dimension
const dim
= dimension(*pi
.base
.bv
);
598 int const a
= y
- dim
.asc
+ 1;
599 int const w
= dim
.wid
- 2;
600 int const h
= dim
.height() - 2;
601 pi
.pain
.rectangle(x
, a
, w
, h
, Color_mathframe
);
603 // just to be sure: set some dummy values for coord cache
604 for (idx_type i
= 0; i
< nargs(); ++i
)
605 cell(i
).setXY(*pi
.base
.bv
, x
, y
);
608 look_
.draw(pi
, x
+ 3, y
);
612 void MathMacroTemplate::edit(Cursor
& cur
, bool front
, EntryDirection entry_from
)
615 cur
.updateFlags(Update::SinglePar
);
616 InsetMathNest::edit(cur
, front
, entry_from
);
620 bool MathMacroTemplate::notifyCursorLeaves(Cursor
const & old
, Cursor
& cur
)
622 // find this in cursor old
623 Cursor insetCur
= old
;
624 int scriptSlice
= insetCur
.find(this);
625 LASSERT(scriptSlice
!= -1, /**/);
626 insetCur
.cutOff(scriptSlice
);
628 commitEditChanges(insetCur
);
630 cur
.updateFlags(Update::Force
);
631 return InsetMathNest::notifyCursorLeaves(old
, cur
);
635 void MathMacroTemplate::removeArguments(Cursor
& cur
, int from
, int to
)
637 for (DocIterator it
= doc_iterator_begin(&buffer(), this); it
; it
.forwardChar()) {
640 if (it
.nextInset()->lyxCode() != MATH_MACROARG_CODE
)
642 MathMacroArgument
* arg
= static_cast<MathMacroArgument
*>(it
.nextInset());
643 int n
= arg
->number() - 1;
644 if (from
<= n
&& n
<= to
) {
645 int cellSlice
= cur
.find(it
.cell());
646 if (cellSlice
!= -1 && cur
[cellSlice
].pos() > it
.pos())
647 --cur
[cellSlice
].pos();
649 it
.cell().erase(it
.pos());
657 void MathMacroTemplate::shiftArguments(size_t from
, int by
)
659 for (DocIterator it
= doc_iterator_begin(&buffer(), this); it
; it
.forwardChar()) {
662 if (it
.nextInset()->lyxCode() != MATH_MACROARG_CODE
)
664 MathMacroArgument
* arg
= static_cast<MathMacroArgument
*>(it
.nextInset());
665 if (arg
->number() >= int(from
) + 1)
666 arg
->setNumber(arg
->number() + by
);
673 int MathMacroTemplate::maxArgumentInDefinition() const
675 // We don't have a buffer when pasting from the clipboard (bug 6014).
676 Buffer
const * macro_buffer
= this->isBufferValid() ? &buffer() : 0;
678 DocIterator it
= doc_iterator_begin(macro_buffer
, this);
680 for (; it
; it
.forwardChar()) {
683 if (it
.nextInset()->lyxCode() != MATH_MACROARG_CODE
)
685 MathMacroArgument
* arg
= static_cast<MathMacroArgument
*>(it
.nextInset());
686 maxArg
= std::max(int(arg
->number()), maxArg
);
692 void MathMacroTemplate::insertMissingArguments(int maxArg
)
694 bool found
[9] = { false, false, false, false, false, false, false, false, false };
695 idx_type idx
= cell(displayIdx()).empty() ? defIdx() : displayIdx();
697 // search for #n macros arguments
698 DocIterator it
= doc_iterator_begin(&buffer(), this);
700 for (; it
&& it
[0].idx() == idx
; it
.forwardChar()) {
703 if (it
.nextInset()->lyxCode() != MATH_MACROARG_CODE
)
705 MathMacroArgument
* arg
= static_cast<MathMacroArgument
*>(it
.nextInset());
706 found
[arg
->number() - 1] = true;
710 for (int i
= 0; i
< maxArg
; ++i
) {
714 cell(idx
).push_back(MathAtom(new MathMacroArgument(i
+ 1)));
719 void MathMacroTemplate::changeArity(Cursor
& cur
, int newNumArg
)
721 // remove parameter which do not appear anymore in the definition
722 for (int i
= numargs_
; i
> newNumArg
; --i
)
723 removeParameter(cur
, numargs_
- 1, false);
725 // add missing parameter
726 for (int i
= numargs_
; i
< newNumArg
; ++i
)
727 insertParameter(cur
, numargs_
, false, false);
731 void MathMacroTemplate::commitEditChanges(Cursor
& cur
)
733 int argsInDef
= maxArgumentInDefinition();
734 if (argsInDef
!= numargs_
) {
735 cur
.recordUndoFullDocument();
736 changeArity(cur
, argsInDef
);
738 insertMissingArguments(argsInDef
);
742 // FIXME: factorize those functions here with a functional style, maybe using Boost's function
745 void fixMacroInstancesAddRemove(Cursor
const & from
, docstring
const & name
, int n
, bool insert
) {
748 for (; dit
; dit
.forwardPos()) {
749 // only until a macro is redefined
750 if (dit
.inset().lyxCode() == MATHMACRO_CODE
) {
751 MathMacroTemplate
const & macroTemplate
752 = static_cast<MathMacroTemplate
const &>(dit
.inset());
753 if (macroTemplate
.name() == name
)
757 // in front of macro instance?
758 Inset
* inset
= dit
.nextInset();
761 InsetMath
* insetMath
= inset
->asInsetMath();
765 MathMacro
* macro
= insetMath
->asMacro();
766 if (macro
&& macro
->name() == name
&& macro
->folded()) {
767 // found macro instance
769 macro
->insertArgument(n
);
771 macro
->removeArgument(n
);
777 void fixMacroInstancesOptional(Cursor
const & from
, docstring
const & name
, int optionals
) {
780 for (; dit
; dit
.forwardPos()) {
781 // only until a macro is redefined
782 if (dit
.inset().lyxCode() == MATHMACRO_CODE
) {
783 MathMacroTemplate
const & macroTemplate
784 = static_cast<MathMacroTemplate
const &>(dit
.inset());
785 if (macroTemplate
.name() == name
)
789 // in front of macro instance?
790 Inset
* inset
= dit
.nextInset();
793 InsetMath
* insetMath
= inset
->asInsetMath();
796 MathMacro
* macro
= insetMath
->asMacro();
797 if (macro
&& macro
->name() == name
&& macro
->folded()) {
798 // found macro instance
799 macro
->setOptionals(optionals
);
806 void fixMacroInstancesFunctional(Cursor
const & from
,
807 docstring
const & name
, F
& fix
) {
810 for (; dit
; dit
.forwardPos()) {
811 // only until a macro is redefined
812 if (dit
.inset().lyxCode() == MATHMACRO_CODE
) {
813 MathMacroTemplate
const & macroTemplate
814 = static_cast<MathMacroTemplate
const &>(dit
.inset());
815 if (macroTemplate
.name() == name
)
819 // in front of macro instance?
820 Inset
* inset
= dit
.nextInset();
823 InsetMath
* insetMath
= inset
->asInsetMath();
826 MathMacro
* macro
= insetMath
->asMacro();
827 if (macro
&& macro
->name() == name
&& macro
->folded())
833 void MathMacroTemplate::insertParameter(Cursor
& cur
, int pos
, bool greedy
, bool addarg
)
835 if (pos
<= numargs_
&& pos
>= optionals_
&& numargs_
< 9) {
840 shiftArguments(pos
, 1);
842 cell(defIdx()).push_back(MathAtom(new MathMacroArgument(pos
+ 1)));
843 if (!cell(displayIdx()).empty())
844 cell(displayIdx()).push_back(MathAtom(new MathMacroArgument(pos
+ 1)));
849 dit
.leaveInset(*this);
850 // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
851 dit
.top().forwardPos();
853 // fix macro instances
854 fixMacroInstancesAddRemove(dit
, name(), pos
, true);
862 void MathMacroTemplate::removeParameter(Cursor
& cur
, int pos
, bool greedy
)
864 if (pos
< numargs_
&& pos
>= 0) {
866 removeArguments(cur
, pos
, pos
);
867 shiftArguments(pos
+ 1, -1);
869 // removed optional parameter?
870 if (pos
< optionals_
) {
872 optionalValues_
[pos
] = cell(optIdx(pos
));
873 cells_
.erase(cells_
.begin() + optIdx(pos
));
876 int macroSlice
= cur
.find(this);
877 if (macroSlice
!= -1) {
878 if (cur
[macroSlice
].idx() == optIdx(pos
)) {
879 cur
.cutOff(macroSlice
);
880 cur
[macroSlice
].idx() = 1;
881 cur
[macroSlice
].pos() = 0;
882 } else if (cur
[macroSlice
].idx() > optIdx(pos
))
883 --cur
[macroSlice
].idx();
888 // fix macro instances
889 //boost::function<void(MathMacro *)> fix = _1->insertArgument(n);
890 //fixMacroInstancesFunctional(dit, name(), fix);
892 dit
.leaveInset(*this);
893 // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
894 dit
.top().forwardPos();
895 fixMacroInstancesAddRemove(dit
, name(), pos
, false);
903 void MathMacroTemplate::makeOptional(Cursor
& cur
) {
904 if (numargs_
> 0 && optionals_
< numargs_
) {
906 cells_
.insert(cells_
.begin() + optIdx(optionals_
- 1), optionalValues_
[optionals_
- 1]);
908 int macroSlice
= cur
.find(this);
909 if (macroSlice
!= -1 && cur
[macroSlice
].idx() >= optIdx(optionals_
- 1))
910 ++cur
[macroSlice
].idx();
912 // fix macro instances
914 dit
.leaveInset(*this);
915 // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
916 dit
.top().forwardPos();
917 fixMacroInstancesOptional(dit
, name(), optionals_
);
924 void MathMacroTemplate::makeNonOptional(Cursor
& cur
) {
925 if (numargs_
> 0 && optionals_
> 0) {
928 // store default value for later if the user changes his mind
929 optionalValues_
[optionals_
] = cell(optIdx(optionals_
));
930 cells_
.erase(cells_
.begin() + optIdx(optionals_
));
933 int macroSlice
= cur
.find(this);
934 if (macroSlice
!= -1) {
935 if (cur
[macroSlice
].idx() > optIdx(optionals_
))
936 --cur
[macroSlice
].idx();
937 else if (cur
[macroSlice
].idx() == optIdx(optionals_
)) {
938 cur
.cutOff(macroSlice
);
939 cur
[macroSlice
].idx() = optIdx(optionals_
);
940 cur
[macroSlice
].pos() = 0;
944 // fix macro instances
946 dit
.leaveInset(*this);
947 // TODO: this was dit.forwardPosNoDescend before. Check that this is the same
948 dit
.top().forwardPos();
949 fixMacroInstancesOptional(dit
, name(), optionals_
);
956 void MathMacroTemplate::doDispatch(Cursor
& cur
, FuncRequest
& cmd
)
958 string
const arg
= to_utf8(cmd
.argument());
959 switch (cmd
.action
) {
961 case LFUN_MATH_MACRO_ADD_PARAM
:
963 commitEditChanges(cur
);
964 cur
.recordUndoFullDocument();
965 size_t pos
= numargs_
;
967 pos
= (size_t)convert
<int>(arg
) - 1; // it is checked for >=0 in getStatus
968 insertParameter(cur
, pos
);
973 case LFUN_MATH_MACRO_REMOVE_PARAM
:
975 commitEditChanges(cur
);
976 cur
.recordUndoFullDocument();
977 size_t pos
= numargs_
- 1;
979 pos
= (size_t)convert
<int>(arg
) - 1; // it is checked for >=0 in getStatus
980 removeParameter(cur
, pos
);
984 case LFUN_MATH_MACRO_APPEND_GREEDY_PARAM
:
986 commitEditChanges(cur
);
987 cur
.recordUndoFullDocument();
988 insertParameter(cur
, numargs_
, true);
992 case LFUN_MATH_MACRO_REMOVE_GREEDY_PARAM
:
994 commitEditChanges(cur
);
995 cur
.recordUndoFullDocument();
996 removeParameter(cur
, numargs_
- 1, true);
1000 case LFUN_MATH_MACRO_MAKE_OPTIONAL
:
1001 commitEditChanges(cur
);
1002 cur
.recordUndoFullDocument();
1006 case LFUN_MATH_MACRO_MAKE_NONOPTIONAL
:
1007 commitEditChanges(cur
);
1008 cur
.recordUndoFullDocument();
1009 makeNonOptional(cur
);
1012 case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM
:
1014 commitEditChanges(cur
);
1015 cur
.recordUndoFullDocument();
1016 insertParameter(cur
, optionals_
);
1021 case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM
:
1022 if (optionals_
> 0) {
1023 commitEditChanges(cur
);
1024 cur
.recordUndoFullDocument();
1025 removeParameter(cur
, optionals_
- 1);
1028 case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM
:
1029 if (numargs_
== optionals_
) {
1030 commitEditChanges(cur
);
1031 cur
.recordUndoFullDocument();
1032 insertParameter(cur
, 0, true);
1038 InsetMathNest::doDispatch(cur
, cmd
);
1044 bool MathMacroTemplate::getStatus(Cursor
& /*cur*/, FuncRequest
const & cmd
,
1045 FuncStatus
& flag
) const
1048 string
const arg
= to_utf8(cmd
.argument());
1049 switch (cmd
.action
) {
1050 case LFUN_MATH_MACRO_ADD_PARAM
: {
1051 int num
= numargs_
+ 1;
1052 if (arg
.size() != 0)
1053 num
= convert
<int>(arg
);
1054 bool on
= (num
>= optionals_
1055 && numargs_
< 9 && num
<= numargs_
+ 1);
1056 flag
.setEnabled(on
);
1060 case LFUN_MATH_MACRO_APPEND_GREEDY_PARAM
:
1061 flag
.setEnabled(numargs_
< 9);
1064 case LFUN_MATH_MACRO_REMOVE_PARAM
: {
1066 if (arg
.size() != 0)
1067 num
= convert
<int>(arg
);
1068 flag
.setEnabled(num
>= 1 && num
<= numargs_
);
1072 case LFUN_MATH_MACRO_MAKE_OPTIONAL
:
1073 flag
.setEnabled(numargs_
> 0
1074 && optionals_
< numargs_
1075 && type_
!= MacroTypeDef
);
1078 case LFUN_MATH_MACRO_MAKE_NONOPTIONAL
:
1079 flag
.setEnabled(optionals_
> 0
1080 && type_
!= MacroTypeDef
);
1083 case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM
:
1084 flag
.setEnabled(numargs_
< 9);
1087 case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM
:
1088 flag
.setEnabled(optionals_
> 0);
1091 case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM
:
1092 flag
.setEnabled(numargs_
== 0
1093 && type_
!= MacroTypeDef
);
1096 case LFUN_IN_MATHMACROTEMPLATE
:
1097 flag
.setEnabled(true);
1108 void MathMacroTemplate::read(Lexer
& lex
)
1110 MathData
ar(buffer_
);
1111 mathed_parse_cell(ar
, lex
.getStream(), Parse::TRACKMACRO
);
1112 if (ar
.size() != 1 || !ar
[0]->asMacroTemplate()) {
1113 lyxerr
<< "Cannot read macro from '" << ar
<< "'" << endl
;
1114 lyxerr
<< "Read: " << to_utf8(asString(ar
)) << endl
;
1117 operator=( *(ar
[0]->asMacroTemplate()) );
1123 void MathMacroTemplate::write(ostream
& os
) const
1125 odocstringstream oss
;
1126 WriteStream
wi(oss
, false, false, WriteStream::wsDefault
);
1127 oss
<< "FormulaMacro\n";
1129 os
<< to_utf8(oss
.str());
1133 void MathMacroTemplate::write(WriteStream
& os
) const
1139 void MathMacroTemplate::write(WriteStream
& os
, bool overwriteRedefinition
) const
1142 if (optionals_
> 0) {
1143 // macros with optionals use the xargs package, e.g.:
1144 // \newcommandx{\foo}[2][usedefault, addprefix=\global,1=default]{#1,#2}
1145 // \long is implicit by xargs
1146 if (redefinition_
&& !overwriteRedefinition
)
1147 os
<< "\\renewcommandx";
1149 os
<< "\\newcommandx";
1151 os
<< "\\" << name()
1152 << "[" << numargs_
<< "]"
1153 << "[usedefault, addprefix=\\global";
1154 for (int i
= 0; i
< optionals_
; ++i
) {
1155 docstring optValue
= asString(cell(optIdx(i
)));
1156 if (optValue
.find(']') != docstring::npos
1157 || optValue
.find(',') != docstring::npos
)
1158 os
<< ", " << i
+ 1 << "="
1159 << "{" << cell(optIdx(i
)) << "}";
1161 os
<< ", " << i
+ 1 << "="
1166 // Macros without optionals use standard _global_ \def macros:
1167 // \global\def\long\foo#1#2{#1,#2}
1168 // We use the \long prefix as this is the equivalent to \newcommand.
1169 // We cannot use \newcommand directly because \global does not work with it.
1170 os
<< "\\global\\long\\def\\" << name();
1171 docstring param
= from_ascii("#0");
1172 for (int i
= 1; i
<= numargs_
; ++i
) {
1178 // in LyX output we use some pseudo syntax which is implementation
1179 // independent, e.g.
1180 // \newcommand{\foo}[2][default]{#1,#2}
1181 if (redefinition_
&& !overwriteRedefinition
)
1182 os
<< "\\renewcommand";
1184 os
<< "\\newcommand";
1185 os
<< "{\\" << name() << '}';
1187 os
<< '[' << numargs_
<< ']';
1189 for (int i
= 0; i
< optionals_
; ++i
) {
1190 docstring optValue
= asString(cell(optIdx(i
)));
1191 if (optValue
.find(']') != docstring::npos
)
1192 os
<< "[{" << cell(optIdx(i
)) << "}]";
1194 os
<< "[" << cell(optIdx(i
)) << "]";
1198 os
<< "{" << cell(defIdx()) << "}";
1201 // writing .tex. done.
1204 // writing .lyx, write special .tex export only if necessary
1205 if (!cell(displayIdx()).empty())
1206 os
<< "\n{" << cell(displayIdx()) << '}';
1211 int MathMacroTemplate::plaintext(odocstream
& os
,
1212 OutputParams
const &) const
1214 static docstring
const str
= '[' + buffer().B_("math macro") + ']';
1221 bool MathMacroTemplate::validName() const
1223 docstring n
= name();
1229 // converting back and force doesn't swallow anything?
1232 if (asString(ma) != n)
1235 // valid characters?
1236 for (size_t i
= 0; i
< n
.size(); ++i
) {
1237 if (!(n
[i
] >= 'a' && n
[i
] <= 'z')
1238 && !(n
[i
] >= 'A' && n
[i
] <= 'Z')
1247 bool MathMacroTemplate::validMacro() const
1253 bool MathMacroTemplate::fixNameAndCheckIfValid()
1255 // check all the characters/insets in the name cell
1257 MathData
& data
= cell(0);
1258 while (i
< data
.size()) {
1259 InsetMathChar
const * cinset
= data
[i
]->asCharInset();
1261 // valid character in [a-zA-Z]?
1262 char_type c
= cinset
->getChar();
1263 if ((c
>= 'a' && c
<= 'z')
1264 || (c
>= 'A' && c
<= 'Z')) {
1274 // now it should be valid if anything in the name survived
1275 return data
.size() > 0;
1279 void MathMacroTemplate::validate(LaTeXFeatures
& features
) const
1281 // we need global optional macro arguments. They are not available
1282 // with \def, and \newcommand does not support global macros. So we
1283 // are bound to xargs also for the single-optional-parameter case.
1285 features
.require("xargs");
1288 void MathMacroTemplate::getDefaults(vector
<docstring
> & defaults
) const
1290 defaults
.resize(numargs_
);
1291 for (int i
= 0; i
< optionals_
; ++i
)
1292 defaults
[i
] = asString(cell(optIdx(i
)));
1296 docstring
MathMacroTemplate::definition() const
1298 return asString(cell(defIdx()));
1302 docstring
MathMacroTemplate::displayDefinition() const
1304 return asString(cell(displayIdx()));
1308 size_t MathMacroTemplate::numArgs() const
1314 size_t MathMacroTemplate::numOptionals() const
1320 void MathMacroTemplate::infoize(odocstream
& os
) const
1322 os
<< "Math Macro: \\" << name();
1326 docstring
MathMacroTemplate::contextMenu(BufferView
const &, int, int) const
1328 return from_ascii("context-math-macro-definition");