+}
+
+
+class InsetMathMacro::UpdateLocker
+{
+public:
+ explicit UpdateLocker(InsetMathMacro & mm) : mac(mm)
+ {
+ mac.d->isUpdating_ = true;
+ }
+ ~UpdateLocker() { mac.d->isUpdating_ = false; }
+private:
+ InsetMathMacro & mac;
+};
+/** Avoid wrong usage of UpdateLocker.
+ To avoid wrong usage:
+ UpdateLocker(...); // wrong
+ UpdateLocker locker(...); // right
+*/
+#define UpdateLocker(x) unnamed_UpdateLocker;
+// Tip gotten from Bobby Schmidt's column in C/C++ Users Journal
+
+
+void InsetMathMacro::updateRepresentation(Cursor * cur, MacroContext const & mc,
+ UpdateType utype, int nesting)
+{
+ // block recursive calls (bug 8999)
+ if (d->isUpdating_)
+ return;
+
+ UpdateLocker locker(*this);
+
+ // known macro?
+ if (d->macro_ == nullptr)
+ return;
+
+ // remember nesting level of this macro
+ d->nesting_ = nesting;
+
+ // update requires
+ d->required_ = d->macro_->required();
+
+ if (!d->needsUpdate_
+ // non-normal mode? We are done!
+ || (d->displayMode_ != DISPLAY_NORMAL))
+ return;
+
+ d->needsUpdate_ = false;
+
+ // get default values of macro
+ vector<docstring> const & defaults = d->macro_->defaults();
+
+ // create MathMacroArgumentValue objects pointing to the cells of the macro
+ vector<MathData> values(nargs());
+ for (size_t i = 0; i < nargs(); ++i) {
+ InsetArgumentProxy * proxy;
+ if (i < defaults.size())
+ proxy = new InsetArgumentProxy(this, i, defaults[i]);
+ else
+ proxy = new InsetArgumentProxy(this, i);
+ values[i].insert(0, MathAtom(proxy));
+ }
+ // expanding macro with the values
+ // Only update the argument macros if anything was expanded or the LyX
+ // representation part does not contain the macro itself, otherwise we
+ // would get an endless loop (bugs 9140 and 11595). UpdateLocker does
+ // not work in this case, since MacroData::expand() creates new
+ // InsetMathMacro objects, so this would be a different recursion path
+ // than the one protected by UpdateLocker.
+ docstring const & display = d->macro_->display();
+ docstring const latexname = from_ascii("\\") + macroName();
+ bool const ret = d->macro_->expand(values, d->expanded_);
+ d->expanded_.setBuffer(buffer());
+ if (ret && !support::contains(display, latexname)) {
+ if (utype == OutputUpdate && !d->expanded_.empty())
+ d->expanded_.updateMacros(cur, mc, utype, nesting);
+ }
+ // get definition for list edit mode
+ asArray(display.empty() ? d->macro_->definition() : display,
+ d->definition_, Parse::QUIET | Parse::MACRODEF);
+}
+
+
+void InsetMathMacro::draw(PainterInfo & pi, int x, int y) const
+{
+ Dimension const dim = dimension(*pi.base.bv);
+
+ int expx = x;
+ int expy = y;
+
+ if (d->displayMode_ == DISPLAY_INIT || d->displayMode_ == DISPLAY_INTERACTIVE_INIT) {
+ Changer dummy = pi.base.changeFontSet("lyxtex");
+ pi.pain.text(x, y, from_ascii("\\") + name(), pi.base.font);
+ } else if (d->displayMode_ == DISPLAY_UNFOLDED) {
+ Changer dummy = pi.base.changeFontSet("lyxtex");
+ pi.pain.text(x, y, from_ascii("\\"), pi.base.font);
+ x += mathed_string_width(pi.base.font, from_ascii("\\")) + 1;
+ cell(0).draw(pi, x, y);
+ } else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST
+ && d->editing_[pi.base.bv]) {
+ // Macro will be edited in a old-style list mode here:
+
+ CoordCache const & coords = pi.base.bv->coordCache();
+ FontInfo const & labelFont = sane_font;
+
+ // box needs one pixel
+ x += 1;
+
+ // get maximal font height
+ Dimension fontDim;
+ math_font_max_dim(pi.base.font, fontDim.asc, fontDim.des);
+
+ // draw label
+ docstring label = from_ascii("Macro \\") + name() + from_ascii(": ");
+ pi.pain.text(x, y, label, labelFont);
+ x += mathed_string_width(labelFont, label);
+
+ // draw definition
+ d->definition_.draw(pi, x, y);
+ Dimension const & defDim = coords.getArrays().dim(&d->definition_);
+ y += max(fontDim.des, defDim.des);
+
+ // draw parameters
+ docstring str = from_ascii("#9");
+ int strw1 = mathed_string_width(labelFont, from_ascii("#9"));
+ int strw2 = mathed_string_width(labelFont, from_ascii(": "));
+
+ for (idx_type i = 0; i < nargs(); ++i) {
+ // position of label
+ Dimension const & cdim = coords.getArrays().dim(&cell(i));
+ x = expx + 1;
+ y += max(fontDim.asc, cdim.asc) + 1;
+
+ // draw label
+ str[1] = '1' + i;
+ pi.pain.text(x, y, str, labelFont);
+ x += strw1;
+ pi.pain.text(x, y, from_ascii(":"), labelFont);
+ x += strw2;
+
+ // draw parameter
+ cell(i).draw(pi, x, y);
+
+ // next line
+ y += max(fontDim.des, cdim.des);
+ }
+
+ pi.pain.rectangle(expx, expy - dim.asc + 1, dim.wid - 1,
+ dim.height() - 2, Color_mathmacroframe);
+ } else {
+ // We should not be here, since the macro is linearized in this case.
+ LBUFERR(false);
+ }
+
+ // edit mode changed?
+ if (d->editing_[pi.base.bv] != editMode(pi.base.bv))
+ pi.base.bv->cursor().screenUpdateFlags(Update::SinglePar);
+}
+
+
+void InsetMathMacro::setDisplayMode(InsetMathMacro::DisplayMode mode, int appetite)
+{
+ if (d->displayMode_ != mode) {
+ // transfer name if changing from or to DISPLAY_UNFOLDED
+ if (mode == DISPLAY_UNFOLDED) {
+ cells_.resize(1);
+ asArray(d->name_, cell(0));
+ } else if (d->displayMode_ == DISPLAY_UNFOLDED) {
+ d->name_ = asString(cell(0));
+ cells_.resize(0);
+ }
+
+ d->displayMode_ = mode;
+ d->needsUpdate_ = true;
+ }
+
+ // the interactive init mode is non-greedy by default
+ if (appetite == -1)
+ d->appetite_ = (mode == DISPLAY_INTERACTIVE_INIT) ? 0 : 9;
+ else
+ d->appetite_ = size_t(appetite);
+}
+
+
+InsetMathMacro::DisplayMode InsetMathMacro::computeDisplayMode() const
+{
+ if (d->nextFoldMode_ && d->macro_ && !d->macro_->locked())
+ return DISPLAY_NORMAL;
+ else
+ return DISPLAY_UNFOLDED;
+}
+
+
+bool InsetMathMacro::validName() const
+{
+ docstring n = name();
+
+ if (n.empty())