]> git.lyx.org Git - features.git/blob - src/mathed/InsetMathMacro.cpp
Fix broken Apple speller interface
[features.git] / src / mathed / InsetMathMacro.cpp
1 /**
2  * \file InsetMathMacro.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author André Pönitz
8  * \author Stefan Schimanski
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "InsetMathMacro.h"
16
17 #include "InsetMathChar.h"
18 #include "InsetMathScript.h"
19 #include "MathCompletionList.h"
20 #include "MathExtern.h"
21 #include "MathFactory.h"
22 #include "MathRow.h"
23 #include "MathStream.h"
24 #include "MathSupport.h"
25
26 #include "Buffer.h"
27 #include "BufferView.h"
28 #include "CoordCache.h"
29 #include "Cursor.h"
30 #include "FuncStatus.h"
31 #include "FuncRequest.h"
32 #include "LaTeXFeatures.h"
33 #include "LyX.h"
34 #include "LyXRC.h"
35 #include "MetricsInfo.h"
36
37 #include "frontends/Painter.h"
38
39 #include "support/debug.h"
40 #include "support/gettext.h"
41 #include "support/lassert.h"
42 #include "support/lstrings.h"
43 #include "support/Changer.h"
44 #include "support/textutils.h"
45
46 #include <ostream>
47 #include <vector>
48
49 using namespace lyx::support;
50 using namespace std;
51
52 namespace lyx {
53
54
55 /// A proxy for the macro values
56 class InsetArgumentProxy : public InsetMath {
57 public:
58         ///
59         InsetArgumentProxy(InsetMathMacro * mathMacro, size_t idx)
60                 : mathMacro_(mathMacro), idx_(idx) {}
61         ///
62         InsetArgumentProxy(InsetMathMacro * mathMacro, size_t idx, docstring const & def)
63                 : mathMacro_(mathMacro), idx_(idx)
64         {
65                         asArray(def, def_);
66         }
67         ///
68         void setBuffer(Buffer & buffer) override
69         {
70                 Inset::setBuffer(buffer);
71                 def_.setBuffer(buffer);
72         }
73         ///
74         void setOwner(InsetMathMacro * mathMacro) { mathMacro_ = mathMacro; }
75         ///
76         InsetMathMacro const * owner() { return mathMacro_; }
77         ///
78         marker_type marker(BufferView const *) const override { return marker_type::NO_MARKER; }
79         ///
80         InsetCode lyxCode() const override { return ARGUMENT_PROXY_CODE; }
81         /// The math data to use for display
82         MathData const & displayCell(BufferView const * bv) const
83         {
84                 // handle default macro arguments
85                 bool use_def_arg = !mathMacro_->editMetrics(bv)
86                         && mathMacro_->cell(idx_).empty();
87                 return use_def_arg ? def_ : mathMacro_->cell(idx_);
88         }
89         ///
90         bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const override
91         {
92                 // macro arguments are in macros
93                 LATTEST(mathMacro_->nesting() > 0);
94                 /// The macro nesting can change display of insets. Change it locally.
95                 Changer chg = changeVar(mi.base.macro_nesting,
96                                           mathMacro_->nesting() == 1 ? 0 : mathMacro_->nesting());
97
98                 MathRow::Element e_beg(mi, MathRow::BEGIN);
99                 e_beg.inset = this;
100                 e_beg.ar = &mathMacro_->cell(idx_);
101                 mrow.push_back(e_beg);
102
103                 mathMacro_->macro()->unlock();
104                 bool has_contents = displayCell(mi.base.bv).addToMathRow(mrow, mi);
105                 mathMacro_->macro()->lock();
106
107                 // if there was no contents, and the contents is editable,
108                 // then we insert a box instead.
109                 if (!has_contents && mathMacro_->nesting() == 1) {
110                         // mathclass is ord because it should be spaced as a normal atom
111                         MathRow::Element e(mi, MathRow::BOX, MC_ORD);
112                         e.color = Color_mathline;
113                         mrow.push_back(e);
114                         has_contents = true;
115                 }
116
117                 MathRow::Element e_end(mi, MathRow::END);
118                 e_end.inset = this;
119                 e_end.ar = &mathMacro_->cell(idx_);
120                 mrow.push_back(e_end);
121
122                 return has_contents;
123         }
124         ///
125         void beforeMetrics() const override
126         {
127                 mathMacro_->macro()->unlock();
128         }
129         ///
130         void afterMetrics() const override
131         {
132                 mathMacro_->macro()->lock();
133         }
134         ///
135         void beforeDraw(PainterInfo const & pi) const override
136         {
137                 // if the macro is being edited, then the painter is in
138                 // monochrome mode.
139                 if (mathMacro_->editMetrics(pi.base.bv))
140                         pi.pain.leaveMonochromeMode();
141         }
142         ///
143         void afterDraw(PainterInfo const & pi) const override
144         {
145                 if (mathMacro_->editMetrics(pi.base.bv))
146                         pi.pain.enterMonochromeMode(Color_mathmacroblend);
147         }
148         ///
149         void metrics(MetricsInfo &, Dimension &) const override {
150                 // This should never be invoked, since InsetArgumentProxy insets are linearized
151                 LATTEST(false);
152         }
153         ///
154         void draw(PainterInfo &, int, int) const override {
155                 // This should never be invoked, since InsetArgumentProxy insets are linearized
156                 LATTEST(false);
157         }
158         ///
159         int kerning(BufferView const * bv) const override
160         {
161                 return displayCell(bv).kerning(bv);
162         }
163         // write(), normalize(), infoize() and infoize2() are not needed since
164         // InsetMathMacro uses the definition and not the expanded cells.
165         ///
166         void maple(MapleStream & ms) const override { ms << mathMacro_->cell(idx_); }
167         ///
168         void maxima(MaximaStream & ms) const override { ms << mathMacro_->cell(idx_); }
169         ///
170         void mathematica(MathematicaStream & ms) const override { ms << mathMacro_->cell(idx_); }
171         ///
172         void mathmlize(MathMLStream & ms) const override { ms << mathMacro_->cell(idx_); }
173         ///
174         void htmlize(HtmlStream & ms) const override { ms << mathMacro_->cell(idx_); }
175         ///
176         void octave(OctaveStream & os) const override { os << mathMacro_->cell(idx_); }
177         ///
178         MathClass mathClass() const override
179         {
180                 return MC_UNKNOWN;
181                 // This can be refined once the pointer issues are fixed. I did not
182                 // notice any immediate crash with the following code, but it is risky
183                 // nevertheless:
184                 //return mathMacro_->cell(idx_).mathClass();
185         }
186
187 private:
188         ///
189         Inset * clone() const override
190         {
191                 return new InsetArgumentProxy(*this);
192         }
193         ///
194         InsetMathMacro * mathMacro_;
195         ///
196         size_t idx_;
197         ///
198         MathData def_;
199 };
200
201
202 /// Private implementation of InsetMathMacro
203 class InsetMathMacro::Private {
204 public:
205         Private(Buffer * buf, docstring const & name)
206                 : name_(name), displayMode_(DISPLAY_INIT),
207                   expanded_(buf), definition_(buf), attachedArgsNum_(0),
208                   optionals_(0), nextFoldMode_(true), macroBackup_(buf),
209                   macro_(nullptr), needsUpdate_(false), isUpdating_(false),
210                   appetite_(9), nesting_(0), limits_(AUTO_LIMITS)
211         {
212         }
213         /// Update the pointers to our owner of all expanded macros.
214         /// This needs to be called every time a copy of the owner is created
215         /// (bug 9418).
216         void updateChildren(InsetMathMacro * owner);
217         /// Recursively update the pointers of all expanded macros
218         /// appearing in the arguments of the current macro
219         void updateNestedChildren(InsetMathMacro * owner, InsetMathNest * ni);
220         /// name of macro
221         docstring name_;
222         /// current display mode
223         DisplayMode displayMode_;
224         /// expanded macro with ArgumentProxies
225         MathData expanded_;
226         /// macro definition with #1,#2,.. insets
227         MathData definition_;
228         /// number of arguments that were really attached
229         size_t attachedArgsNum_;
230         /// optional argument attached? (only in DISPLAY_NORMAL mode)
231         size_t optionals_;
232         /// fold mode to be set in next metrics call?
233         bool nextFoldMode_;
234         /// if macro_ == true, then here is a copy of the macro
235         /// don't use it for locking
236         MacroData macroBackup_;
237         /// if macroNotFound_ == false, then here is a reference to the macro
238         /// this might invalidate after metrics was called
239         MacroData const * macro_;
240         ///
241         mutable std::map<BufferView const *, bool> editing_;
242         ///
243         std::string required_;
244         /// update macro representation
245         bool needsUpdate_;
246         ///
247         bool isUpdating_;
248         /// maximal number of arguments the macro is greedy for
249         size_t appetite_;
250         /// Level of nesting in macros (including this one)
251         int nesting_;
252         ///
253         Limits limits_;
254 };
255
256
257 void InsetMathMacro::Private::updateChildren(InsetMathMacro * owner)
258 {
259         for (size_t i = 0; i < expanded_.size(); ++i) {
260                 InsetArgumentProxy * p = dynamic_cast<InsetArgumentProxy *>(expanded_[i].nucleus());
261                 if (p)
262                         p->setOwner(owner);
263
264                 InsetMathNest * ni = expanded_[i].nucleus()->asNestInset();
265                 if (ni)
266                         updateNestedChildren(owner, ni);
267         }
268
269         if (macro_) {
270                 // The macro_ pointer is updated when MathData::metrics() is
271                 // called. However, when instant preview is on or the macro is
272                 // not on screen, MathData::metrics() is not called and we may
273                 // have a dangling pointer. As a safety measure, when a macro
274                 // is copied, always let macro_ point to the backup copy of the
275                 // MacroData structure. This backup is updated every time the
276                 // macro is changed, so it will not become stale.
277                 macro_ = &macroBackup_;
278         }
279 }
280
281
282 void InsetMathMacro::Private::updateNestedChildren(InsetMathMacro * owner, InsetMathNest * ni)
283 {
284         for (size_t i = 0; i < ni->nargs(); ++i) {
285                 MathData & ar = ni->cell(i);
286                 for (size_t j = 0; j < ar.size(); ++j) {
287                         InsetArgumentProxy * ap = dynamic_cast
288                                 <InsetArgumentProxy *>(ar[j].nucleus());
289                         if (ap) {
290                                 InsetMathMacro::Private * md = ap->owner()->d;
291                                 if (md->macro_)
292                                         md->macro_ = &md->macroBackup_;
293                                 ap->setOwner(owner);
294                         }
295                         InsetMathNest * imn = ar[j].nucleus()->asNestInset();
296                         if (imn)
297                                 updateNestedChildren(owner, imn);
298                 }
299         }
300 }
301
302
303 InsetMathMacro::InsetMathMacro(Buffer * buf, docstring const & name)
304         : InsetMathNest(buf, 0), d(new Private(buf, name))
305 {}
306
307
308 InsetMathMacro::InsetMathMacro(InsetMathMacro const & that)
309         : InsetMathNest(that), d(new Private(*that.d))
310 {
311         // FIXME This should not really be necessary, but when we are
312         // initializing the table of global macros, we create macros
313         // with no associated Buffer.
314         if (that.buffer_)
315                 setBuffer(*that.buffer_);
316         d->updateChildren(this);
317 }
318
319
320 InsetMathMacro & InsetMathMacro::operator=(InsetMathMacro const & that)
321 {
322         if (&that == this)
323                 return *this;
324         InsetMathNest::operator=(that);
325         *d = *that.d;
326         d->updateChildren(this);
327         return *this;
328 }
329
330
331 InsetMathMacro::~InsetMathMacro()
332 {
333         delete d;
334 }
335
336
337 bool InsetMathMacro::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
338 {
339         // set edit mode for which we will have calculated row.
340         // This is the same as what is done in metrics().
341         d->editing_[mi.base.bv] = editMode(mi.base.bv);
342
343         // For now we do not linearize in the following cases (can be improved)
344         // - display mode different from normal
345         // - editing with parameter list
346         // - editing with box around macro
347         if (displayMode() != InsetMathMacro::DISPLAY_NORMAL
348                 || (d->editing_[mi.base.bv] && lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST))
349                 return InsetMath::addToMathRow(mrow, mi);
350
351         /// The macro nesting can change display of insets. Change it locally.
352         Changer chg = changeVar(mi.base.macro_nesting, d->nesting_);
353
354         MathRow::Element e_beg(mi, MathRow::BEGIN);
355         e_beg.inset = this;
356         e_beg.marker = (d->nesting_ == 1) ? marker(mi.base.bv) : marker_type::NO_MARKER;
357         mrow.push_back(e_beg);
358
359         d->macro_->lock();
360         bool has_contents = d->expanded_.addToMathRow(mrow, mi);
361         d->macro_->unlock();
362
363         // if there was no contents and the array is editable, then we
364         // insert a grey box instead.
365         if (!has_contents && mi.base.macro_nesting == 1) {
366                 // mathclass is unknown because it is irrelevant for spacing
367                 MathRow::Element e(mi, MathRow::BOX);
368                 e.color = Color_mathmacroblend;
369                 mrow.push_back(e);
370                 has_contents = true;
371         }
372
373         MathRow::Element e_end(mi, MathRow::END);
374         e_end.inset = this;
375         e_end.marker = (d->nesting_ == 1) ? marker(mi.base.bv) : marker_type::NO_MARKER;
376         mrow.push_back(e_end);
377
378         return has_contents;
379 }
380
381
382 /// Whether the inset allows \(no)limits
383 bool InsetMathMacro::allowsLimitsChange() const
384 {
385         // similar to the code in mathClass(), except that we search for
386         // the right-side class.
387         MathClass mc = MC_UNKNOWN;
388         if (MacroData const * m = macroBackup()) {
389                 // If it is a global macro and is defined explicitly
390                 if (m->symbol())
391                         mc = string_to_class(m->symbol()->extra);
392         }
393         // Otherwise guess from the expanded macro
394         if (mc == MC_UNKNOWN)
395                 mc = d->expanded_.lastMathClass();
396
397         return mc == MC_OP;
398 }
399
400
401 Limits InsetMathMacro::defaultLimits(bool display) const
402 {
403         if (d->expanded_.empty())
404                 return NO_LIMITS;
405         // Guess from the expanded macro
406         InsetMath const * in = d->expanded_.back().nucleus();
407         Limits const lim = in->limits() == AUTO_LIMITS
408                 ? in->defaultLimits(display) : in->limits();
409         LATTEST(lim != AUTO_LIMITS);
410         return lim;
411 }
412
413
414 Limits InsetMathMacro::limits() const
415 {
416         return d->limits_;
417 }
418
419
420 void InsetMathMacro::limits(Limits lim)
421 {
422         d->limits_ = lim;
423 }
424
425
426 void InsetMathMacro::beforeMetrics() const
427 {
428         d->macro_->lock();
429 }
430
431
432 void InsetMathMacro::afterMetrics() const
433 {
434         d->macro_->unlock();
435 }
436
437
438 void InsetMathMacro::beforeDraw(PainterInfo const & pi) const
439 {
440         if (d->editing_[pi.base.bv])
441                 pi.pain.enterMonochromeMode(Color_mathmacroblend);
442 }
443
444
445 void InsetMathMacro::afterDraw(PainterInfo const & pi) const
446 {
447         if (d->editing_[pi.base.bv])
448                 pi.pain.leaveMonochromeMode();
449 }
450
451
452 Inset * InsetMathMacro::clone() const
453 {
454         InsetMathMacro * copy = new InsetMathMacro(*this);
455         copy->d->needsUpdate_ = true;
456         //copy->d->expanded_.clear();
457         return copy;
458 }
459
460
461 void InsetMathMacro::normalize(NormalStream & os) const
462 {
463         os << "[macro " << name();
464         for (size_t i = 0; i < nargs(); ++i)
465                 os << ' ' << cell(i);
466         os << ']';
467 }
468
469
470 InsetMathMacro::DisplayMode InsetMathMacro::displayMode() const
471 {
472         return d->displayMode_;
473 }
474
475
476 bool InsetMathMacro::extraBraces() const
477 {
478         return d->displayMode_ == DISPLAY_NORMAL && arity() > 0;
479 }
480
481
482 docstring InsetMathMacro::name() const
483 {
484         if (d->displayMode_ == DISPLAY_UNFOLDED)
485                 return asString(cell(0));
486
487         return d->name_;
488 }
489
490
491 docstring InsetMathMacro::macroName() const
492 {
493         return d->name_;
494 }
495
496
497 int InsetMathMacro::nesting() const
498 {
499         return d->nesting_;
500 }
501
502
503 void InsetMathMacro::cursorPos(BufferView const & bv,
504                 CursorSlice const & sl, bool boundary, int & x, int & y) const
505 {
506         // We may have 0 arguments, but InsetMathNest requires at least one.
507         if (nargs() > 0)
508                 InsetMathNest::cursorPos(bv, sl, boundary, x, y);
509 }
510
511
512 bool InsetMathMacro::editMode(BufferView const * bv) const {
513         // find this in cursor trace
514         DocIterator const & cur =
515                 // Do not move the reference while selecting with the mouse to avoid
516                 // flicker due to changing metrics
517                 bv->mouseSelecting() ? bv->cursor().realAnchor() : bv->cursor();
518         for (size_t i = 0; i != cur.depth(); ++i)
519                 if (&cur[i].inset() == this) {
520                         // look if there is no other macro in edit mode above
521                         ++i;
522                         for (; i != cur.depth(); ++i) {
523                                 InsetMath * im = cur[i].asInsetMath();
524                                 if (im) {
525                                         InsetMathMacro const * macro = im->asMacro();
526                                         if (macro && macro->displayMode() == DISPLAY_NORMAL)
527                                                 return false;
528                                 }
529                         }
530
531                         // ok, none found, I am the highest one
532                         return true;
533                 }
534
535         return false;
536 }
537
538
539 MacroData const * InsetMathMacro::macro() const
540 {
541         return d->macro_;
542 }
543
544
545 bool InsetMathMacro::editMetrics(BufferView const * bv) const
546 {
547         return d->editing_[bv];
548 }
549
550
551 marker_type InsetMathMacro::marker(BufferView const * bv) const
552 {
553         if (nargs() == 0)
554                 return marker_type::NO_MARKER;
555
556         switch (d->displayMode_) {
557         case DISPLAY_INIT:
558         case DISPLAY_INTERACTIVE_INIT:
559                 return marker_type::NO_MARKER;
560         case DISPLAY_UNFOLDED:
561                 return marker_type::MARKER;
562         case DISPLAY_NORMAL:
563                 switch (lyxrc.macro_edit_style) {
564                 case LyXRC::MACRO_EDIT_INLINE:
565                         return marker_type::MARKER2;
566                 case LyXRC::MACRO_EDIT_INLINE_BOX:
567                         return d->editing_[bv] ? marker_type::BOX_MARKER : marker_type::MARKER2;
568                 case LyXRC::MACRO_EDIT_LIST:
569                         return marker_type::MARKER2;
570                 }
571         }
572         // please gcc 4.6
573         return marker_type::NO_MARKER;
574 }
575
576
577 void InsetMathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
578 {
579         /// The macro nesting can change display of insets. Change it locally.
580         Changer chg = changeVar(mi.base.macro_nesting, d->nesting_);
581
582         // set edit mode for which we will have calculated metrics. But only
583         d->editing_[mi.base.bv] = editMode(mi.base.bv);
584
585         // calculate new metrics according to display mode
586         if (d->displayMode_ == DISPLAY_INIT || d->displayMode_ == DISPLAY_INTERACTIVE_INIT) {
587                 Changer dummy = mi.base.changeFontSet("lyxtex");
588                 mathed_string_dim(mi.base.font, from_ascii("\\") + name(), dim);
589         } else if (d->displayMode_ == DISPLAY_UNFOLDED) {
590                 Changer dummy = mi.base.changeFontSet("lyxtex");
591                 cell(0).metrics(mi, dim);
592                 Dimension bsdim;
593                 mathed_string_dim(mi.base.font, from_ascii("\\"), bsdim);
594                 dim.wid += bsdim.width() + 1;
595                 dim.asc = max(bsdim.ascent(), dim.ascent());
596                 dim.des = max(bsdim.descent(), dim.descent());
597         } else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST
598                    && d->editing_[mi.base.bv]) {
599                 // Macro will be edited in a old-style list mode here:
600
601                 LBUFERR(d->macro_);
602                 Dimension fontDim;
603                 FontInfo labelFont = sane_font;
604                 math_font_max_dim(labelFont, fontDim.asc, fontDim.des);
605
606                 // get dimension of components of list view
607                 Dimension nameDim;
608                 nameDim.wid = mathed_string_width(mi.base.font, from_ascii("Macro \\") + name() + ": ");
609                 nameDim.asc = fontDim.asc;
610                 nameDim.des = fontDim.des;
611
612                 Dimension argDim;
613                 argDim.wid = mathed_string_width(labelFont, from_ascii("#9: "));
614                 argDim.asc = fontDim.asc;
615                 argDim.des = fontDim.des;
616
617                 Dimension defDim;
618                 d->definition_.metrics(mi, defDim);
619
620                 // add them up
621                 dim.wid = nameDim.wid + defDim.wid;
622                 dim.asc = max(nameDim.asc, defDim.asc);
623                 dim.des = max(nameDim.des, defDim.des);
624
625                 for (idx_type i = 0; i < nargs(); ++i) {
626                         Dimension cdim;
627                         cell(i).metrics(mi, cdim);
628                         dim.des += max(argDim.height(), cdim.height()) + 1;
629                         dim.wid = max(dim.wid, argDim.wid + cdim.wid);
630                 }
631
632                 // make space for box and markers, 2 pixels
633                 dim.asc += 1;
634                 dim.des += 1;
635                 dim.wid += 2;
636         } else {
637                 // We should not be here, since the macro is linearized in this case.
638                 LBUFERR(false);
639         }
640 }
641
642
643 int InsetMathMacro::kerning(BufferView const * bv) const {
644         if (d->displayMode_ == DISPLAY_NORMAL && !d->editing_[bv])
645                 return d->expanded_.kerning(bv);
646         else
647                 return 0;
648 }
649
650
651 void InsetMathMacro::updateMacro(MacroContext const & mc)
652 {
653         if (validName()) {
654                 d->macro_ = mc.get(name());
655                 if (d->macro_ && d->macroBackup_ != *d->macro_) {
656                         d->macroBackup_ = *d->macro_;
657                         d->needsUpdate_ = true;
658                 }
659         } else {
660                 d->macro_ = nullptr;
661         }
662 }
663
664
665 class InsetMathMacro::UpdateLocker
666 {
667 public:
668         explicit UpdateLocker(InsetMathMacro & mm) : mac(mm)
669         {
670                 mac.d->isUpdating_ = true;
671         }
672         ~UpdateLocker() { mac.d->isUpdating_ = false; }
673 private:
674         InsetMathMacro & mac;
675 };
676 /** Avoid wrong usage of UpdateLocker.
677     To avoid wrong usage:
678     UpdateLocker(...); // wrong
679     UpdateLocker locker(...); // right
680 */
681 #define UpdateLocker(x) unnamed_UpdateLocker;
682 // Tip gotten from Bobby Schmidt's column in C/C++ Users Journal
683
684
685 void InsetMathMacro::updateRepresentation(Cursor * cur, MacroContext const & mc,
686         UpdateType utype, int nesting)
687 {
688         // block recursive calls (bug 8999)
689         if (d->isUpdating_)
690                 return;
691
692         UpdateLocker locker(*this);
693
694         // known macro?
695         if (d->macro_ == nullptr)
696                 return;
697
698         // remember nesting level of this macro
699         d->nesting_ = nesting;
700
701         // update requires
702         d->required_ = d->macro_->required();
703
704         if (!d->needsUpdate_
705                 // non-normal mode? We are done!
706                 || (d->displayMode_ != DISPLAY_NORMAL))
707                 return;
708
709         d->needsUpdate_ = false;
710
711         // get default values of macro
712         vector<docstring> const & defaults = d->macro_->defaults();
713
714         // create MathMacroArgumentValue objects pointing to the cells of the macro
715         vector<MathData> values(nargs());
716         for (size_t i = 0; i < nargs(); ++i) {
717                 InsetArgumentProxy * proxy;
718                 if (i < defaults.size())
719                         proxy = new InsetArgumentProxy(this, i, defaults[i]);
720                 else
721                         proxy = new InsetArgumentProxy(this, i);
722                 values[i].insert(0, MathAtom(proxy));
723         }
724         // expanding macro with the values
725         // Only update the argument macros if anything was expanded or the LyX
726         // representation part does not contain the macro itself, otherwise we
727         // would get an endless loop (bugs 9140 and 11595). UpdateLocker does
728         // not work in this case, since MacroData::expand() creates new
729         // InsetMathMacro objects, so this would be a different recursion path
730         // than the one protected by UpdateLocker.
731         docstring const & display = d->macro_->display();
732         docstring const latexname = from_ascii("\\") + macroName();
733         bool const ret = d->macro_->expand(values, d->expanded_);
734         d->expanded_.setBuffer(buffer());
735         if (ret && !support::contains(display, latexname)) {
736                 if (utype == OutputUpdate && !d->expanded_.empty())
737                         d->expanded_.updateMacros(cur, mc, utype, nesting);
738         }
739         // get definition for list edit mode
740         asArray(display.empty() ? d->macro_->definition() : display,
741                 d->definition_, Parse::QUIET | Parse::MACRODEF);
742 }
743
744
745 void InsetMathMacro::draw(PainterInfo & pi, int x, int y) const
746 {
747         Dimension const dim = dimension(*pi.base.bv);
748
749         int expx = x;
750         int expy = y;
751
752         if (d->displayMode_ == DISPLAY_INIT || d->displayMode_ == DISPLAY_INTERACTIVE_INIT) {
753                 Changer dummy = pi.base.changeFontSet("lyxtex");
754                 pi.pain.text(x, y, from_ascii("\\") + name(), pi.base.font);
755         } else if (d->displayMode_ == DISPLAY_UNFOLDED) {
756                 Changer dummy = pi.base.changeFontSet("lyxtex");
757                 pi.pain.text(x, y, from_ascii("\\"), pi.base.font);
758                 x += mathed_string_width(pi.base.font, from_ascii("\\")) + 1;
759                 cell(0).draw(pi, x, y);
760         } else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST
761                    && d->editing_[pi.base.bv]) {
762                 // Macro will be edited in a old-style list mode here:
763
764                 CoordCache const & coords = pi.base.bv->coordCache();
765                 FontInfo const & labelFont = sane_font;
766
767                 // box needs one pixel
768                 x += 1;
769
770                 // get maximal font height
771                 Dimension fontDim;
772                 math_font_max_dim(pi.base.font, fontDim.asc, fontDim.des);
773
774                 // draw label
775                 docstring label = from_ascii("Macro \\") + name() + from_ascii(": ");
776                 pi.pain.text(x, y, label, labelFont);
777                 x += mathed_string_width(labelFont, label);
778
779                 // draw definition
780                 d->definition_.draw(pi, x, y);
781                 Dimension const & defDim = coords.getArrays().dim(&d->definition_);
782                 y += max(fontDim.des, defDim.des);
783
784                 // draw parameters
785                 docstring str = from_ascii("#9");
786                 int strw1 = mathed_string_width(labelFont, from_ascii("#9"));
787                 int strw2 = mathed_string_width(labelFont, from_ascii(": "));
788
789                 for (idx_type i = 0; i < nargs(); ++i) {
790                         // position of label
791                         Dimension const & cdim = coords.getArrays().dim(&cell(i));
792                         x = expx + 1;
793                         y += max(fontDim.asc, cdim.asc) + 1;
794
795                         // draw label
796                         str[1] = '1' + i;
797                         pi.pain.text(x, y, str, labelFont);
798                         x += strw1;
799                         pi.pain.text(x, y, from_ascii(":"), labelFont);
800                         x += strw2;
801
802                         // draw parameter
803                         cell(i).draw(pi, x, y);
804
805                         // next line
806                         y += max(fontDim.des, cdim.des);
807                 }
808
809                 pi.pain.rectangle(expx, expy - dim.asc + 1, dim.wid - 1,
810                                   dim.height() - 2, Color_mathmacroframe);
811         } else {
812                 // We should not be here, since the macro is linearized in this case.
813                 LBUFERR(false);
814         }
815
816         // edit mode changed?
817         if (d->editing_[pi.base.bv] != editMode(pi.base.bv))
818                 pi.base.bv->cursor().screenUpdateFlags(Update::SinglePar);
819 }
820
821
822 void InsetMathMacro::setDisplayMode(InsetMathMacro::DisplayMode mode, int appetite)
823 {
824         if (d->displayMode_ != mode) {
825                 // transfer name if changing from or to DISPLAY_UNFOLDED
826                 if (mode == DISPLAY_UNFOLDED) {
827                         cells_.resize(1);
828                         asArray(d->name_, cell(0));
829                 } else if (d->displayMode_ == DISPLAY_UNFOLDED) {
830                         d->name_ = asString(cell(0));
831                         cells_.resize(0);
832                 }
833
834                 d->displayMode_ = mode;
835                 d->needsUpdate_ = true;
836         }
837
838         // the interactive init mode is non-greedy by default
839         if (appetite == -1)
840                 d->appetite_ = (mode == DISPLAY_INTERACTIVE_INIT) ? 0 : 9;
841         else
842                 d->appetite_ = size_t(appetite);
843 }
844
845
846 InsetMathMacro::DisplayMode InsetMathMacro::computeDisplayMode() const
847 {
848         if (d->nextFoldMode_ && d->macro_ && !d->macro_->locked())
849                 return DISPLAY_NORMAL;
850         else
851                 return DISPLAY_UNFOLDED;
852 }
853
854
855 bool InsetMathMacro::validName() const
856 {
857         docstring n = name();
858
859         if (n.empty())
860                 return false;
861
862         // converting back and force doesn't swallow anything?
863         /*MathData ma;
864         asArray(n, ma);
865         if (asString(ma) != n)
866                 return false;*/
867
868         // valid characters?
869         if (n.size() > 1) {
870                 for (char_type c : n) {
871                         if (!(c >= 'a' && c <= 'z')
872                             && !(c >= 'A' && c <= 'Z')
873                             && c != '*')
874                                 return false;
875                 }
876         }
877
878         return true;
879 }
880
881
882 size_t InsetMathMacro::arity() const
883 {
884         if (d->displayMode_ == DISPLAY_NORMAL )
885                 return cells_.size();
886         else
887                 return 0;
888 }
889
890
891 size_t InsetMathMacro::optionals() const
892 {
893         return d->optionals_;
894 }
895
896
897 void InsetMathMacro::setOptionals(int n)
898 {
899         if (n <= int(nargs()))
900                 d->optionals_ = n;
901 }
902
903
904 size_t InsetMathMacro::appetite() const
905 {
906         return d->appetite_;
907 }
908
909
910 MathClass InsetMathMacro::mathClass() const
911 {
912         // This can be just a heuristic, since it is only considered for display
913         // when the macro is not linearised. Therefore it affects:
914         // * The spacing of the inset while being edited,
915         // * Intelligent splitting
916         // * Cursor word movement (Ctrl-Arrow).
917         if (MacroData const * m = macroBackup()) {
918                 // If it is a global macro and is defined explicitly
919                 if (m->symbol()) {
920                         MathClass mc = string_to_class(m->symbol()->extra);
921                         if (mc != MC_UNKNOWN)
922                                 return mc;
923                 }
924         }
925         // Otherwise guess from the expanded macro
926         return d->expanded_.mathClass();
927 }
928
929
930 InsetMath::mode_type InsetMathMacro::currentMode() const
931 {
932         // User defined macros are always assumed to be mathmode macros.
933         // Only the global macros defined in lib/symbols may be textmode.
934         if (MacroData const * m = macroBackup()) {
935                 if (m->symbol() && m->symbol()->extra == "textmode")
936                         return TEXT_MODE;
937                 else
938                         return MATH_MODE;
939         }
940         // Unknown macros are undecided.
941         return UNDECIDED_MODE;
942 }
943
944
945 MacroData const * InsetMathMacro::macroBackup() const
946 {
947         if (macro())
948                 return &d->macroBackup_;
949         if (MacroData const * data = MacroTable::globalMacros().get(name()))
950                 return data;
951         return nullptr;
952 }
953
954
955 void InsetMathMacro::validate(LaTeXFeatures & features) const
956 {
957         // Immediately after a document is loaded, in some cases the MacroData
958         // of the global macros defined in the lib/symbols file may still not
959         // be known to the macro machinery because it will be set only after
960         // the first call to updateMacros(). This is not a problem unless
961         // instant preview is on for math, in which case we will be missing
962         // the corresponding requirements.
963         // In this case, we get the required info from the global macro table.
964         if (!d->required_.empty())
965                 features.require(d->required_);
966         else if (!d->macro_) {
967                 // Update requires for known global macros.
968                 MacroData const * data = MacroTable::globalMacros().get(name());
969                 if (data && !data->required().empty())
970                         features.require(data->required());
971         }
972
973         // Validate the cells and the definition.
974         // However, don't validate the definition if the macro is
975         // from the symbols file and has not been redefined, because
976         // in this case the definition is only used for screen display.
977         MathWordList const & words = mathedWordList();
978         MathWordList::const_iterator it = words.find(name());
979         MacroNameSet macros;
980         buffer().listMacroNames(macros);
981         if (it == words.end() || it->second.inset != "macro"
982             || macros.find(name()) != macros.end()) {
983                 if (displayMode() == DISPLAY_NORMAL) {
984                                 d->definition_.validate(features);
985                 } else if (displayMode() == DISPLAY_INIT) {
986                         MathData ar(const_cast<Buffer *>(&buffer()));
987                         MacroData const * data = buffer().getMacro(name());
988                         if (data) {
989                                 asArray(data->definition(), ar);
990                                 ar.validate(features);
991                         }
992                 }
993         }
994         InsetMathNest::validate(features);
995 }
996
997
998 void InsetMathMacro::edit(Cursor & cur, bool front, EntryDirection entry_from)
999 {
1000         cur.screenUpdateFlags(Update::SinglePar);
1001         InsetMathNest::edit(cur, front, entry_from);
1002 }
1003
1004
1005 Inset * InsetMathMacro::editXY(Cursor & cur, int x, int y)
1006 {
1007         // We may have 0 arguments, but InsetMathNest requires at least one.
1008         if (nargs() > 0) {
1009                 cur.screenUpdateFlags(Update::SinglePar);
1010                 return InsetMathNest::editXY(cur, x, y);
1011         } else
1012                 return this;
1013 }
1014
1015
1016 void InsetMathMacro::removeArgument(pos_type pos) {
1017         if (d->displayMode_ == DISPLAY_NORMAL) {
1018                 LASSERT(size_t(pos) < cells_.size(), return);
1019                 cells_.erase(cells_.begin() + pos);
1020                 if (size_t(pos) < d->attachedArgsNum_)
1021                         --d->attachedArgsNum_;
1022                 if (size_t(pos) < d->optionals_) {
1023                         --d->optionals_;
1024                 }
1025
1026                 d->needsUpdate_ = true;
1027         }
1028 }
1029
1030
1031 void InsetMathMacro::insertArgument(pos_type pos) {
1032         if (d->displayMode_ == DISPLAY_NORMAL) {
1033                 LASSERT(size_t(pos) <= cells_.size(), return);
1034                 cells_.insert(cells_.begin() + pos, MathData());
1035                 if (size_t(pos) < d->attachedArgsNum_)
1036                         ++d->attachedArgsNum_;
1037                 if (size_t(pos) < d->optionals_)
1038                         ++d->optionals_;
1039
1040                 d->needsUpdate_ = true;
1041         }
1042 }
1043
1044
1045 void InsetMathMacro::detachArguments(vector<MathData> & args, bool strip)
1046 {
1047         LASSERT(d->displayMode_ == DISPLAY_NORMAL, return);
1048         args = cells_;
1049
1050         // strip off empty cells, but not more than arity-attachedArgsNum_
1051         if (strip) {
1052                 size_t i;
1053                 for (i = cells_.size(); i > d->attachedArgsNum_; --i)
1054                         if (!cell(i - 1).empty()) break;
1055                 args.resize(i);
1056         }
1057
1058         d->attachedArgsNum_ = 0;
1059         d->expanded_ = MathData();
1060         cells_.resize(0);
1061
1062         d->needsUpdate_ = true;
1063 }
1064
1065
1066 void InsetMathMacro::attachArguments(vector<MathData> const & args, size_t arity, int optionals)
1067 {
1068         LASSERT(d->displayMode_ == DISPLAY_NORMAL, return);
1069         cells_ = args;
1070         d->attachedArgsNum_ = args.size();
1071         cells_.resize(arity);
1072         d->expanded_ = MathData();
1073         d->optionals_ = optionals;
1074
1075         d->needsUpdate_ = true;
1076 }
1077
1078
1079 bool InsetMathMacro::idxFirst(Cursor & cur) const
1080 {
1081         cur.screenUpdateFlags(Update::SinglePar);
1082         return InsetMathNest::idxFirst(cur);
1083 }
1084
1085
1086 bool InsetMathMacro::idxLast(Cursor & cur) const
1087 {
1088         cur.screenUpdateFlags(Update::SinglePar);
1089         return InsetMathNest::idxLast(cur);
1090 }
1091
1092
1093 bool InsetMathMacro::notifyCursorLeaves(Cursor const & old, Cursor & cur)
1094 {
1095         if (d->displayMode_ == DISPLAY_UNFOLDED) {
1096                 docstring const & unfolded_name = name();
1097                 if (unfolded_name != d->name_) {
1098                         // The macro name was changed
1099                         Cursor inset_cursor = old;
1100                         int macroSlice = inset_cursor.find(this);
1101                         // returning true means the cursor is "now" invalid,
1102                         // which it was.
1103                         LASSERT(macroSlice != -1, return true);
1104                         inset_cursor.cutOff(macroSlice);
1105                         inset_cursor.recordUndoInset();
1106                         inset_cursor.pop();
1107                         inset_cursor.cell().erase(inset_cursor.pos());
1108                         inset_cursor.cell().insert(inset_cursor.pos(),
1109                                 createInsetMath(unfolded_name, cur.buffer()));
1110                         cur.resetAnchor();
1111                         cur.screenUpdateFlags(cur.result().screenUpdate() | Update::SinglePar);
1112                         return true;
1113                 }
1114         }
1115         cur.screenUpdateFlags(Update::Force);
1116         return InsetMathNest::notifyCursorLeaves(old, cur);
1117 }
1118
1119
1120 void InsetMathMacro::fold(Cursor & cur)
1121 {
1122         if (!d->nextFoldMode_) {
1123                 d->nextFoldMode_ = true;
1124                 cur.screenUpdateFlags(Update::SinglePar);
1125         }
1126 }
1127
1128
1129 void InsetMathMacro::unfold(Cursor & cur)
1130 {
1131         if (d->nextFoldMode_) {
1132                 d->nextFoldMode_ = false;
1133                 cur.screenUpdateFlags(Update::SinglePar);
1134         }
1135 }
1136
1137
1138 bool InsetMathMacro::folded() const
1139 {
1140         return d->nextFoldMode_;
1141 }
1142
1143
1144 void InsetMathMacro::write(TeXMathStream & os) const
1145 {
1146         mode_type mode = currentMode();
1147         MathEnsurer ensurer(os, mode == MATH_MODE, true, mode == TEXT_MODE);
1148
1149         // non-normal mode
1150         if (d->displayMode_ != DISPLAY_NORMAL) {
1151                 os << "\\" << name();
1152                 if (name().size() != 1 || isAlphaASCII(name()[0]))
1153                         os.pendingSpace(true);
1154                 return;
1155         }
1156
1157         // normal mode
1158         // we should be ok to continue even if this fails.
1159         LATTEST(d->macro_);
1160
1161         // Always protect macros in a fragile environment
1162         if (os.fragile())
1163                 os << "\\protect";
1164
1165         os << "\\" << name();
1166         bool first = true;
1167
1168         // Optional arguments:
1169         // First find last non-empty optional argument
1170         idx_type emptyOptFrom = 0;
1171         idx_type i = 0;
1172         for (; i < cells_.size() && i < d->optionals_; ++i) {
1173                 if (!cell(i).empty())
1174                         emptyOptFrom = i + 1;
1175         }
1176
1177         // print out optionals
1178         for (i=0; i < cells_.size() && i < emptyOptFrom; ++i) {
1179                 first = false;
1180                 // For correctly parsing it when a document is reloaded, we
1181                 // need to enclose an optional argument in braces if it starts
1182                 // with a script inset with empty nucleus or ends with a
1183                 // delimiter-size-modifier macro (see #10497 and #11346).
1184                 // We also need to do that when the optional argument
1185                 // contains macros with optionals.
1186                 bool braced = false;
1187                 size_type last = cell(i).size() - 1;
1188                 if (!cell(i).empty() && cell(i)[last]->asUnknownInset()) {
1189                         latexkeys const * l = in_word_set(cell(i)[last]->name());
1190                         braced = (l && l->inset == "big");
1191                 } else if (!cell(i).empty() && cell(i)[0]->asScriptInset()) {
1192                         braced = cell(i)[0]->asScriptInset()->nuc().empty();
1193                 } else {
1194                         for (size_type j = 0; j < cell(i).size(); ++j) {
1195                                 InsetMathMacro const * ma = cell(i)[j]->asMacro();
1196                                 if (ma && ma->optionals()) {
1197                                         braced = true;
1198                                         break;
1199                                 }
1200                         }
1201                 }
1202                 if (braced)
1203                         os << "[{" << cell(i) << "}]";
1204                 else
1205                         os << "[" << cell(i) << "]";
1206         }
1207
1208         // skip the tailing empty optionals
1209         i = d->optionals_;
1210
1211         // Print remaining arguments
1212         for (; i < cells_.size(); ++i) {
1213                 if (cell(i).size() == 1
1214                         && cell(i)[0].nucleus()->asCharInset()
1215                         && isASCII(cell(i)[0].nucleus()->asCharInset()->getChar())) {
1216                         if (first)
1217                                 os << " ";
1218                         os << cell(i);
1219                 } else
1220                         os << "{" << cell(i) << "}";
1221                 first = false;
1222         }
1223
1224         // add space if there was no argument
1225         // or add braces if we have optionals but none are present and [ follows
1226         if (first) {
1227                 os.pendingSpace(true);
1228                 os.useBraces(d->optionals_ > 0);
1229         }
1230
1231         // write \(no)limits modifiers if relevant
1232         writeLimits(os);
1233 }
1234
1235
1236 void InsetMathMacro::maple(MapleStream & os) const
1237 {
1238         lyx::maple(d->expanded_, os);
1239 }
1240
1241
1242 void InsetMathMacro::maxima(MaximaStream & os) const
1243 {
1244         lyx::maxima(d->expanded_, os);
1245 }
1246
1247
1248 void InsetMathMacro::mathematica(MathematicaStream & os) const
1249 {
1250         lyx::mathematica(d->expanded_, os);
1251 }
1252
1253
1254 void InsetMathMacro::mathmlize(MathMLStream & ms) const
1255 {
1256         // macro_ is 0 if this is an unknown macro
1257         LATTEST(d->macro_ || d->displayMode_ != DISPLAY_NORMAL);
1258         if (d->macro_) {
1259                 docstring const xmlname = (ms.xmlMode()) ? d->macro_->xmlname() : d->macro_->htmlname();
1260                 if (!xmlname.empty()) {
1261                         char const * type = d->macro_->MathMLtype();
1262                         ms << "<" << from_ascii(ms.namespacedTag(type)) << ">"
1263                            << xmlname
1264                            << "</" << from_ascii(ms.namespacedTag(type)) << ">";
1265                         return;
1266                 }
1267         }
1268         if (d->expanded_.empty()) {
1269                 // this means that we do not recognize the macro
1270                 throw MathExportException();
1271         }
1272         ms << d->expanded_;
1273 }
1274
1275
1276 void InsetMathMacro::htmlize(HtmlStream & os) const
1277 {
1278         // macro_ is 0 if this is an unknown macro
1279         LATTEST(d->macro_ || d->displayMode_ != DISPLAY_NORMAL);
1280         if (d->macro_) {
1281                 docstring const xmlname = d->macro_->htmlname();
1282                 if (!xmlname.empty()) {
1283                         os << ' ' << xmlname << ' ';
1284                         return;
1285                 }
1286         }
1287         if (d->expanded_.empty()) {
1288                 // this means that we do not recognize the macro
1289                 throw MathExportException();
1290         }
1291         os << d->expanded_;
1292 }
1293
1294
1295 void InsetMathMacro::octave(OctaveStream & os) const
1296 {
1297         lyx::octave(d->expanded_, os);
1298 }
1299
1300
1301 void InsetMathMacro::infoize(odocstream & os) const
1302 {
1303         os << bformat(_("Macro: %1$s"), name());
1304 }
1305
1306
1307 void InsetMathMacro::infoize2(odocstream & os) const
1308 {
1309         os << bformat(_("Macro: %1$s"), name());
1310 }
1311
1312
1313 bool InsetMathMacro::completionSupported(Cursor const & cur) const
1314 {
1315         if (cur.buffer()->isReadonly())
1316                 return false;
1317
1318         if (displayMode() != DISPLAY_UNFOLDED)
1319                 return InsetMathNest::completionSupported(cur);
1320
1321         return lyxrc.completion_popup_math
1322                 && cur.bv().cursor().pos() == int(name().size());
1323 }
1324
1325
1326 bool InsetMathMacro::inlineCompletionSupported(Cursor const & cur) const
1327 {
1328         if (cur.buffer()->isReadonly())
1329                 return false;
1330
1331         if (displayMode() != DISPLAY_UNFOLDED)
1332                 return InsetMathNest::inlineCompletionSupported(cur);
1333
1334         return lyxrc.completion_inline_math
1335                 && cur.bv().cursor().pos() == int(name().size());
1336 }
1337
1338
1339 bool InsetMathMacro::automaticInlineCompletion() const
1340 {
1341         if (displayMode() != DISPLAY_UNFOLDED)
1342                 return InsetMathNest::automaticInlineCompletion();
1343
1344         return lyxrc.completion_inline_math;
1345 }
1346
1347
1348 bool InsetMathMacro::automaticPopupCompletion() const
1349 {
1350         if (displayMode() != DISPLAY_UNFOLDED)
1351                 return InsetMathNest::automaticPopupCompletion();
1352
1353         return lyxrc.completion_popup_math;
1354 }
1355
1356
1357 CompletionList const *
1358 InsetMathMacro::createCompletionList(Cursor const & cur) const
1359 {
1360         if (displayMode() != DISPLAY_UNFOLDED)
1361                 return InsetMathNest::createCompletionList(cur);
1362
1363         return new MathCompletionList(cur.bv().cursor());
1364 }
1365
1366
1367 docstring InsetMathMacro::completionPrefix(Cursor const & cur) const
1368 {
1369         if (displayMode() != DISPLAY_UNFOLDED)
1370                 return InsetMathNest::completionPrefix(cur);
1371
1372         return "\\" + name();
1373 }
1374
1375
1376 bool InsetMathMacro::insertCompletion(Cursor & cur, docstring const & s, bool finished)
1377 {
1378         if (cur.buffer()->isReadonly())
1379                 return false;
1380
1381         if (displayMode() != DISPLAY_UNFOLDED)
1382                 return InsetMathNest::insertCompletion(cur, s, finished);
1383
1384         if (!completionSupported(cur))
1385                 return false;
1386
1387         // Contrary to Text, the whole inset should be recorded (#12581).
1388         cur.recordUndoInset();
1389
1390         // append completion
1391         docstring newName = name() + s;
1392         asArray(newName, cell(0));
1393         cur.bv().cursor().pos() = name().size();
1394         cur.screenUpdateFlags(Update::SinglePar);
1395
1396         // finish macro
1397         if (finished) {
1398                 cur.bv().cursor().pop();
1399                 ++cur.bv().cursor().pos();
1400                 cur.screenUpdateFlags(Update::SinglePar);
1401         }
1402
1403         return true;
1404 }
1405
1406
1407 void InsetMathMacro::completionPosAndDim(Cursor const & cur, int & x, int & y,
1408         Dimension & dim) const
1409 {
1410         if (displayMode() != DISPLAY_UNFOLDED)
1411                 InsetMathNest::completionPosAndDim(cur, x, y, dim);
1412
1413         // get inset dimensions
1414         dim = cur.bv().coordCache().insets().dim(this);
1415         // FIXME: these 3 are no accurate, but should depend on the font.
1416         // Now the popup jumps down if you enter a char with descent > 0.
1417         dim.des += 3;
1418         dim.asc += 3;
1419
1420         // and position
1421         Point xy
1422         = cur.bv().coordCache().insets().xy(this);
1423         x = xy.x_;
1424         y = xy.y_;
1425 }
1426
1427
1428 void InsetMathMacro::setBuffer(Buffer & buffer)
1429 {
1430         d->definition_.setBuffer(buffer);
1431         InsetMathNest::setBuffer(buffer);
1432 }
1433
1434 } // namespace lyx