+bool MathMacroTemplate::notifyCursorLeaves(Cursor const & old, Cursor & cur)
+{
+ commitEditChanges(cur, old);
+ updateLook();
+ cur.screenUpdateFlags(Update::Force);
+ return InsetMathNest::notifyCursorLeaves(old, cur);
+}
+
+
+void MathMacroTemplate::removeArguments(Cursor & cur,
+ DocIterator const & /*inset_pos*/, int from, int to)
+{
+ DocIterator it = doc_iterator_begin(&buffer(), this);
+ for (; it; it.forwardChar()) {
+ if (!it.nextInset())
+ continue;
+ if (it.nextInset()->lyxCode() != MATH_MACROARG_CODE)
+ continue;
+ MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
+ int n = arg->number() - 1;
+ if (from <= n && n <= to) {
+ int cellSlice = cur.find(it.cell());
+ if (cellSlice != -1 && cur[cellSlice].pos() > it.pos())
+ --cur[cellSlice].pos();
+
+ it.cell().erase(it.pos());
+ }
+ }
+
+ updateLook();
+}
+
+
+void MathMacroTemplate::shiftArguments(size_t from, int by)
+{
+ for (DocIterator it = doc_iterator_begin(&buffer(), this); it; it.forwardChar()) {
+ if (!it.nextInset())
+ continue;
+ if (it.nextInset()->lyxCode() != MATH_MACROARG_CODE)
+ continue;
+ MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
+ if (arg->number() >= int(from) + 1)
+ arg->setNumber(arg->number() + by);
+ }
+
+ updateLook();
+}
+
+
+int MathMacroTemplate::maxArgumentInDefinition() const
+{
+ // We don't have a buffer when pasting from the clipboard (bug 6014).
+ Buffer const * macro_buffer = isBufferLoaded() ? &buffer() : 0;
+ int maxArg = 0;
+ DocIterator it = doc_iterator_begin(macro_buffer, this);
+ it.idx() = defIdx();
+ for (; it; it.forwardChar()) {
+ if (!it.nextInset())
+ continue;
+ if (it.nextInset()->lyxCode() != MATH_MACROARG_CODE)
+ continue;
+ MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
+ maxArg = std::max(int(arg->number()), maxArg);
+ }
+ return maxArg;
+}
+
+
+void MathMacroTemplate::insertMissingArguments(int maxArg)
+{
+ bool found[9] = { false, false, false, false, false, false, false, false, false };
+ idx_type idx = cell(displayIdx()).empty() ? defIdx() : displayIdx();
+
+ // search for #n macros arguments
+ DocIterator it = doc_iterator_begin(&buffer(), this);
+ it.idx() = idx;
+ for (; it && it[0].idx() == idx; it.forwardChar()) {
+ if (!it.nextInset())
+ continue;
+ if (it.nextInset()->lyxCode() != MATH_MACROARG_CODE)
+ continue;
+ MathMacroArgument * arg = static_cast<MathMacroArgument*>(it.nextInset());
+ found[arg->number() - 1] = true;
+ }
+
+ // add missing ones
+ for (int i = 0; i < maxArg; ++i) {
+ if (found[i])
+ continue;
+
+ cell(idx).push_back(MathAtom(new MathMacroArgument(i + 1)));
+ }
+}
+
+
+void MathMacroTemplate::changeArity(Cursor & cur,
+ DocIterator const & inset_pos, int newNumArg)
+{
+ // remove parameter which do not appear anymore in the definition
+ for (int i = numargs_; i > newNumArg; --i)
+ removeParameter(cur, inset_pos, numargs_ - 1, true);
+
+ // add missing parameter
+ for (int i = numargs_; i < newNumArg; ++i)
+ insertParameter(cur, inset_pos, numargs_, true, false);
+}
+
+
+///
+class AddRemoveMacroInstanceFix
+{
+public:
+ ///
+ AddRemoveMacroInstanceFix(int n, bool insert) : n_(n), insert_(insert) {}
+ ///
+ void operator()(MathMacro * macro)
+ {
+ if (macro->folded()) {
+ if (insert_)
+ macro->insertArgument(n_);
+ else
+ macro->removeArgument(n_);
+ }
+ }
+
+private:
+ ///
+ int n_;
+ ///
+ bool insert_;
+};
+
+
+///
+class OptionalsMacroInstanceFix
+{
+public:
+ ///
+ OptionalsMacroInstanceFix(int optionals) : optionals_(optionals) {}
+ ///
+ void operator()(MathMacro * macro)
+ {
+ macro->setOptionals(optionals_);
+ }
+
+private:
+ ///
+ int optionals_;
+};
+
+
+///
+class NullMacroInstanceFix
+{
+public:
+ ///
+ void operator()(MathMacro * ) {}
+};
+
+
+template<class F>
+void fixMacroInstances(Cursor & cur, DocIterator const & inset_pos,
+ docstring const & name, F & fix)
+{
+ // goto position behind macro template
+ DocIterator dit = inset_pos;
+ dit.pop_back();
+ dit.top().forwardPos();
+
+ // remember hull to trigger preview reload
+ DocIterator hull(dit.buffer());
+ bool preview_reload_needed = false;
+ set<DocIterator> preview_hulls;
+
+ // iterate over all positions until macro is redefined
+ for (; dit; dit.forwardPos()) {
+ // left the outer hull?
+ if (!hull.empty() && dit.depth() == hull.depth()) {
+ // schedule reload of the preview if necessary
+ if (preview_reload_needed) {
+ preview_hulls.insert(hull);
+ preview_reload_needed = false;
+ }
+ hull.clear();
+ }
+
+ // only until a macro is redefined
+ if (dit.inset().lyxCode() == MATHMACRO_CODE) {
+ MathMacroTemplate const & macroTemplate
+ = static_cast<MathMacroTemplate const &>(dit.inset());
+ if (macroTemplate.name() == name)
+ break;
+ }
+
+ // in front of macro instance?
+ Inset * inset = dit.nextInset();
+ if (!inset)
+ continue;
+ InsetMath * insetMath = inset->asInsetMath();
+ if (!insetMath)
+ continue;
+
+ // in front of outer hull?
+ InsetMathHull * inset_hull = insetMath->asHullInset();
+ if (inset_hull && hull.empty()) {
+ // remember this for later preview reload
+ hull = dit;
+ }
+
+ MathMacro * macro = insetMath->asMacro();
+ if (macro && macro->name() == name && macro->folded()) {
+ fix(macro);
+ if (RenderPreview::status() == LyXRC::PREVIEW_ON)
+ preview_reload_needed = true;
+ }
+ }
+
+ if (!preview_hulls.empty()) {
+ // reload the scheduled previews
+ set<DocIterator>::const_iterator sit = preview_hulls.begin();
+ set<DocIterator>::const_iterator end = preview_hulls.end();
+ for (; sit != end; ++sit) {
+ InsetMathHull * inset_hull =
+ sit->nextInset()->asInsetMath()->asHullInset();
+ LASSERT(inset_hull, /**/);
+ inset_hull->reloadPreview(*sit);
+ }
+ cur.screenUpdateFlags(Update::Force);
+ }
+}
+
+
+void MathMacroTemplate::commitEditChanges(Cursor & cur,
+ DocIterator const & inset_pos)
+{
+ int args_in_def = maxArgumentInDefinition();
+ if (args_in_def != numargs_) {
+ cur.recordUndoFullDocument();
+ changeArity(cur, inset_pos, args_in_def);
+ }
+ insertMissingArguments(args_in_def);
+
+ // make sure the preview are up to date
+ NullMacroInstanceFix fix;
+ fixMacroInstances(cur, inset_pos, name(), fix);
+}
+
+
+void MathMacroTemplate::insertParameter(Cursor & cur,
+ DocIterator const & inset_pos, int pos, bool greedy, bool addarg)
+{
+ if (pos <= numargs_ && pos >= optionals_ && numargs_ < 9) {
+ ++numargs_;
+
+ // append example #n
+ if (addarg) {
+ shiftArguments(pos, 1);
+
+ cell(defIdx()).push_back(MathAtom(new MathMacroArgument(pos + 1)));
+ if (!cell(displayIdx()).empty())
+ cell(displayIdx()).push_back(MathAtom(new MathMacroArgument(pos + 1)));
+ }
+
+ if (!greedy) {
+ // fix macro instances
+ AddRemoveMacroInstanceFix fix(pos, true);
+ fixMacroInstances(cur, inset_pos, name(), fix);
+ }
+ }
+
+ updateLook();
+}
+
+
+void MathMacroTemplate::removeParameter(Cursor & cur,
+ DocIterator const & inset_pos, int pos, bool greedy)
+{
+ if (pos < numargs_ && pos >= 0) {
+ --numargs_;
+ removeArguments(cur, inset_pos, pos, pos);
+ shiftArguments(pos + 1, -1);
+
+ // removed optional parameter?
+ if (pos < optionals_) {
+ --optionals_;
+ optionalValues_[pos] = cell(optIdx(pos));
+ cells_.erase(cells_.begin() + optIdx(pos));
+
+ // fix cursor
+ int macroSlice = cur.find(this);
+ if (macroSlice != -1) {
+ if (cur[macroSlice].idx() == optIdx(pos)) {
+ cur.cutOff(macroSlice);
+ cur[macroSlice].idx() = 1;
+ cur[macroSlice].pos() = 0;
+ } else if (cur[macroSlice].idx() > optIdx(pos))
+ --cur[macroSlice].idx();
+ }
+ }
+
+ if (!greedy) {
+ // fix macro instances
+ AddRemoveMacroInstanceFix fix(pos, false);
+ fixMacroInstances(cur, inset_pos, name(), fix);
+ }
+ }
+
+ updateLook();
+}
+
+
+void MathMacroTemplate::makeOptional(Cursor & cur,
+ DocIterator const & inset_pos)
+{
+ if (numargs_ > 0 && optionals_ < numargs_) {
+ ++optionals_;
+ cells_.insert(cells_.begin() + optIdx(optionals_ - 1), optionalValues_[optionals_ - 1]);
+ // fix cursor
+ int macroSlice = cur.find(this);
+ if (macroSlice != -1 && cur[macroSlice].idx() >= optIdx(optionals_ - 1))
+ ++cur[macroSlice].idx();
+
+ // fix macro instances
+ OptionalsMacroInstanceFix fix(optionals_);
+ fixMacroInstances(cur, inset_pos, name(), fix);
+ }
+
+ updateLook();
+}
+
+
+void MathMacroTemplate::makeNonOptional(Cursor & cur,
+ DocIterator const & inset_pos)
+{
+ if (numargs_ > 0 && optionals_ > 0) {
+ --optionals_;
+
+ // store default value for later if the user changes his mind
+ optionalValues_[optionals_] = cell(optIdx(optionals_));
+ cells_.erase(cells_.begin() + optIdx(optionals_));
+
+ // fix cursor
+ int macroSlice = cur.find(this);
+ if (macroSlice != -1) {
+ if (cur[macroSlice].idx() > optIdx(optionals_))
+ --cur[macroSlice].idx();
+ else if (cur[macroSlice].idx() == optIdx(optionals_)) {
+ cur.cutOff(macroSlice);
+ cur[macroSlice].idx() = optIdx(optionals_);
+ cur[macroSlice].pos() = 0;
+ }
+ }
+
+ // fix macro instances
+ OptionalsMacroInstanceFix fix(optionals_);
+ fixMacroInstances(cur, inset_pos, name(), fix);
+ }
+
+ updateLook();
+}
+
+
+void MathMacroTemplate::doDispatch(Cursor & cur, FuncRequest & cmd)
+{
+ string const arg = to_utf8(cmd.argument());
+ switch (cmd.action()) {
+
+ case LFUN_MATH_MACRO_ADD_PARAM:
+ if (numargs_ < 9) {
+ commitEditChanges(cur, cur);
+ cur.recordUndoFullDocument();
+ size_t pos = numargs_;
+ if (arg.size() != 0)
+ pos = (size_t)convert<int>(arg) - 1; // it is checked for >=0 in getStatus
+ insertParameter(cur, cur, pos);
+ }
+ break;
+
+
+ case LFUN_MATH_MACRO_REMOVE_PARAM:
+ if (numargs_ > 0) {
+ commitEditChanges(cur, cur);
+ cur.recordUndoFullDocument();
+ size_t pos = numargs_ - 1;
+ if (arg.size() != 0)
+ pos = (size_t)convert<int>(arg) - 1; // it is checked for >=0 in getStatus
+ removeParameter(cur, cur, pos);
+ }
+ break;
+
+ case LFUN_MATH_MACRO_APPEND_GREEDY_PARAM:
+ if (numargs_ < 9) {
+ commitEditChanges(cur, cur);
+ cur.recordUndoFullDocument();
+ insertParameter(cur, cur, numargs_, true);
+ }
+ break;
+
+ case LFUN_MATH_MACRO_REMOVE_GREEDY_PARAM:
+ if (numargs_ > 0) {
+ commitEditChanges(cur, cur);
+ cur.recordUndoFullDocument();
+ removeParameter(cur, cur, numargs_ - 1, true);
+ }
+ break;
+
+ case LFUN_MATH_MACRO_MAKE_OPTIONAL:
+ commitEditChanges(cur, cur);
+ cur.recordUndoFullDocument();
+ makeOptional(cur, cur);
+ break;
+
+ case LFUN_MATH_MACRO_MAKE_NONOPTIONAL:
+ commitEditChanges(cur, cur);
+ cur.recordUndoFullDocument();
+ makeNonOptional(cur, cur);
+ break;
+
+ case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM:
+ if (numargs_ < 9) {
+ commitEditChanges(cur, cur);
+ cur.recordUndoFullDocument();
+ insertParameter(cur, cur, optionals_);
+ makeOptional(cur, cur);
+ }
+ break;
+
+ case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM:
+ if (optionals_ > 0) {
+ commitEditChanges(cur, cur);
+ cur.recordUndoFullDocument();
+ removeParameter(cur, cur, optionals_ - 1);
+ } break;
+
+ case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
+ if (numargs_ == optionals_) {
+ commitEditChanges(cur, cur);
+ cur.recordUndoFullDocument();
+ insertParameter(cur, cur, 0, true);
+ makeOptional(cur, cur);
+ }
+ break;
+
+ default:
+ InsetMathNest::doDispatch(cur, cmd);
+ break;
+ }
+}
+
+
+bool MathMacroTemplate::getStatus(Cursor & /*cur*/, FuncRequest const & cmd,
+ FuncStatus & flag) const
+{
+ bool ret = true;
+ string const arg = to_utf8(cmd.argument());
+ switch (cmd.action()) {
+ case LFUN_MATH_MACRO_ADD_PARAM: {
+ int num = numargs_ + 1;
+ if (arg.size() != 0)
+ num = convert<int>(arg);
+ bool on = (num >= optionals_
+ && numargs_ < 9 && num <= numargs_ + 1);
+ flag.setEnabled(on);
+ break;
+ }
+
+ case LFUN_MATH_MACRO_APPEND_GREEDY_PARAM:
+ flag.setEnabled(numargs_ < 9);
+ break;
+
+ case LFUN_MATH_MACRO_REMOVE_GREEDY_PARAM:
+ case LFUN_MATH_MACRO_REMOVE_PARAM: {
+ int num = numargs_;
+ if (arg.size() != 0)
+ num = convert<int>(arg);
+ flag.setEnabled(num >= 1 && num <= numargs_);
+ break;
+ }
+
+ case LFUN_MATH_MACRO_MAKE_OPTIONAL:
+ flag.setEnabled(numargs_ > 0
+ && optionals_ < numargs_
+ && type_ != MacroTypeDef);
+ break;
+
+ case LFUN_MATH_MACRO_MAKE_NONOPTIONAL:
+ flag.setEnabled(optionals_ > 0
+ && type_ != MacroTypeDef);
+ break;
+
+ case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM:
+ flag.setEnabled(numargs_ < 9);
+ break;
+
+ case LFUN_MATH_MACRO_REMOVE_OPTIONAL_PARAM:
+ flag.setEnabled(optionals_ > 0);
+ break;
+
+ case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
+ flag.setEnabled(numargs_ == 0
+ && type_ != MacroTypeDef);
+ break;
+
+ case LFUN_IN_MATHMACROTEMPLATE:
+ flag.setEnabled(true);
+ break;
+
+ default:
+ ret = false;
+ break;
+ }
+ return ret;
+}
+
+
+void MathMacroTemplate::read(Lexer & lex)
+{
+ MathData ar(buffer_);
+ mathed_parse_cell(ar, lex.getStream(), Parse::TRACKMACRO);