]> git.lyx.org Git - lyx.git/blob - src/mathed/MathMacro.cpp
f29a5e4ac79876b92e01463a9de98fdd9164fff7
[lyx.git] / src / mathed / MathMacro.cpp
1 /**
2  * \file MathMacro.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 "MathMacro.h"
16
17 #include "InsetMathChar.h"
18 #include "MathCompletionList.h"
19 #include "MathExtern.h"
20 #include "MathFactory.h"
21 #include "MathStream.h"
22 #include "MathSupport.h"
23
24 #include "Buffer.h"
25 #include "BufferView.h"
26 #include "CoordCache.h"
27 #include "Cursor.h"
28 #include "FuncStatus.h"
29 #include "FuncRequest.h"
30 #include "LaTeXFeatures.h"
31 #include "LyXFunc.h"
32 #include "LyXRC.h"
33 #include "Undo.h"
34
35 #include "frontends/Painter.h"
36
37 #include "support/debug.h"
38 #include "support/lassert.h"
39 #include "support/textutils.h"
40
41 #include <ostream>
42 #include <vector>
43
44 using namespace std;
45
46 namespace lyx {
47
48
49 /// A proxy for the macro values
50 class ArgumentProxy : public InsetMath {
51 public:
52         ///
53         ArgumentProxy(MathMacro & mathMacro, size_t idx) 
54                 : mathMacro_(mathMacro), idx_(idx) {}
55         ///
56         ArgumentProxy(MathMacro & mathMacro, size_t idx, docstring const & def) 
57                 : mathMacro_(mathMacro), idx_(idx) 
58         {
59                         asArray(def, def_);
60         }
61         ///
62         void metrics(MetricsInfo & mi, Dimension & dim) const {
63                 mathMacro_.macro()->unlock();
64                 mathMacro_.cell(idx_).metrics(mi, dim);
65
66                 if (!mathMacro_.editMetrics(mi.base.bv)
67                     && mathMacro_.cell(idx_).empty())
68                         def_.metrics(mi, dim);
69
70                 mathMacro_.macro()->lock();
71         }
72         ///
73         void draw(PainterInfo & pi, int x, int y) const {
74                 if (mathMacro_.editMetrics(pi.base.bv)) {
75                         // The only way a ArgumentProxy can appear is in a cell of the 
76                         // MathMacro. Moreover the cells are only drawn in the DISPLAY_FOLDED 
77                         // mode and then, if the macro is edited the monochrome 
78                         // mode is entered by the MathMacro before calling the cells' draw
79                         // method. Then eventually this code is reached and the proxy leaves
80                         // monochrome mode temporarely. Hence, if it is not in monochrome 
81                         // here (and the assert triggers in pain.leaveMonochromeMode()) 
82                         // it's a bug.
83                         pi.pain.leaveMonochromeMode();
84                         mathMacro_.cell(idx_).draw(pi, x, y);
85                         pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
86                 } else if (mathMacro_.cell(idx_).empty()) {
87                         mathMacro_.cell(idx_).setXY(*pi.base.bv, x, y);
88                         def_.draw(pi, x, y);
89                 } else
90                         mathMacro_.cell(idx_).draw(pi, x, y);
91         }
92         ///
93         size_t idx() const { return idx_; }
94         ///
95         int kerning(BufferView const * bv) const
96         { 
97                 if (mathMacro_.editMetrics(bv)
98                     || !mathMacro_.cell(idx_).empty())
99                         return mathMacro_.cell(idx_).kerning(bv); 
100                 else
101                         return def_.kerning(bv);
102         }
103
104 private:
105         ///
106         Inset * clone() const 
107         {
108                 return new ArgumentProxy(*this);
109         }
110         ///
111         MathMacro & mathMacro_;
112         ///
113         size_t idx_;
114         ///
115         MathData def_;
116 };
117
118
119 MathMacro::MathMacro(docstring const & name)
120         : InsetMathNest(0), name_(name), displayMode_(DISPLAY_INIT),
121                 attachedArgsNum_(0), optionals_(0), nextFoldMode_(true),
122                 macro_(0), needsUpdate_(false), appetite_(9)
123 {}
124
125
126 Inset * MathMacro::clone() const
127 {
128         MathMacro * copy = new MathMacro(*this);
129         copy->needsUpdate_ = true;
130         copy->expanded_.cell(0).clear();
131         return copy;
132 }
133
134
135 docstring MathMacro::name() const
136 {
137         if (displayMode_ == DISPLAY_UNFOLDED)
138                 return asString(cell(0));
139
140         return name_;
141 }
142
143
144 void MathMacro::cursorPos(BufferView const & bv,
145                 CursorSlice const & sl, bool boundary, int & x, int & y) const
146 {
147         // We may have 0 arguments, but InsetMathNest requires at least one.
148         if (nargs() > 0)
149                 InsetMathNest::cursorPos(bv, sl, boundary, x, y);
150 }
151
152
153 bool MathMacro::editMode(BufferView const * bv) const {
154         // find this in cursor trace
155         Cursor const & cur = bv->cursor();
156         for (size_t i = 0; i != cur.depth(); ++i)
157                 if (&cur[i].inset() == this) {
158                         // look if there is no other macro in edit mode above
159                         ++i;
160                         for (; i != cur.depth(); ++i) {
161                                 MathMacro const * macro = dynamic_cast<MathMacro const *>(&cur[i].inset());
162                                 if (macro && macro->displayMode() == DISPLAY_NORMAL)
163                                         return false;
164                         }
165
166                         // ok, none found, I am the highest one
167                         return true;
168                 }
169
170         return false;
171 }
172
173
174 bool MathMacro::editMetrics(BufferView const * bv) const
175 {
176         return editing_[bv];
177 }
178
179
180 void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
181 {
182         // set edit mode for which we will have calculated metrics. But only
183         editing_[mi.base.bv] = editMode(mi.base.bv);
184
185         // calculate new metrics according to display mode
186         if (displayMode_ == DISPLAY_INIT || displayMode_ == DISPLAY_INTERACTIVE_INIT) {
187                 mathed_string_dim(mi.base.font, from_ascii("\\") + name(), dim);
188         } else if (displayMode_ == DISPLAY_UNFOLDED) {
189                 cell(0).metrics(mi, dim);
190                 Dimension bsdim;
191                 mathed_string_dim(mi.base.font, from_ascii("\\"), bsdim);
192                 dim.wid += bsdim.width() + 1;
193                 dim.asc = max(bsdim.ascent(), dim.ascent());
194                 dim.des = max(bsdim.descent(), dim.descent());
195                 metricsMarkers(dim);
196         } else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST 
197                    && editing_[mi.base.bv]) {
198                 // Macro will be edited in a old-style list mode here:
199
200                 LASSERT(macro_ != 0, /**/);
201                 Dimension fontDim;
202                 FontInfo labelFont = sane_font;
203                 math_font_max_dim(labelFont, fontDim.asc, fontDim.des);
204                 
205                 // get dimension of components of list view
206                 Dimension nameDim;
207                 nameDim.wid = mathed_string_width(mi.base.font, from_ascii("Macro \\") + name() + ": ");
208                 nameDim.asc = fontDim.asc;
209                 nameDim.des = fontDim.des;
210
211                 Dimension argDim;
212                 argDim.wid = mathed_string_width(labelFont, from_ascii("#9: "));
213                 argDim.asc = fontDim.asc;
214                 argDim.des = fontDim.des;
215                 
216                 Dimension defDim;
217                 definition_.metrics(mi, defDim);
218                 
219                 // add them up
220                 dim.wid = nameDim.wid + defDim.wid;
221                 dim.asc = max(nameDim.asc, defDim.asc);
222                 dim.des = max(nameDim.des, defDim.des);
223                 
224                 for (idx_type i = 0; i < nargs(); ++i) {
225                         Dimension cdim;
226                         cell(i).metrics(mi, cdim);
227                         dim.des += max(argDim.height(), cdim.height()) + 1;
228                         dim.wid = max(dim.wid, argDim.wid + cdim.wid);
229                 }
230                 
231                 // make space for box and markers, 2 pixels
232                 dim.asc += 1;
233                 dim.des += 1;
234                 dim.wid += 2;
235                 metricsMarkers2(dim);
236         } else {
237                 LASSERT(macro_ != 0, /**/);
238
239                 // calculate metrics, hoping that all cells are seen
240                 macro_->lock();
241                 expanded_.cell(0).metrics(mi, dim);
242
243                 // otherwise do a manual metrics call
244                 CoordCache & coords = mi.base.bv->coordCache();
245                 for (idx_type i = 0; i < nargs(); ++i) {
246                         if (!coords.getArrays().has(&cell(i))) {
247                                 Dimension tdim;
248                                 cell(i).metrics(mi, tdim);
249                         }
250                 }
251                 macro_->unlock();
252
253                 // calculate dimension with label while editing
254                 if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_INLINE_BOX 
255                     && editing_[mi.base.bv]) {
256                         FontInfo font = mi.base.font;
257                         augmentFont(font, from_ascii("lyxtex"));
258                         Dimension namedim;
259                         mathed_string_dim(font, name(), namedim);
260 #if 0
261                         dim.wid += 2 + namedim.wid + 2 + 2;
262                         dim.asc = max(dim.asc, namedim.asc) + 2;
263                         dim.des = max(dim.des, namedim.des) + 2;
264 #endif
265                         dim.wid = max(1 + namedim.wid + 1, 2 + dim.wid + 2);
266                         dim.asc += 1 + namedim.height() + 1;
267                         dim.des += 2;
268                 }
269          
270         }
271 }
272
273
274 int MathMacro::kerning(BufferView const * bv) const {
275         if (displayMode_ == DISPLAY_NORMAL && !editing_[bv])
276                 return expanded_.kerning(bv);
277         else
278                 return 0;
279 }
280
281
282 void MathMacro::updateMacro(MacroContext const & mc) 
283 {
284         if (validName()) {
285                 macro_ = mc.get(name());            
286                 if (macro_ && macroBackup_ != *macro_) {
287                         macroBackup_ = *macro_;
288                         needsUpdate_ = true;
289                 }
290         } else {
291                 macro_ = 0;
292         }
293 }
294
295
296 void MathMacro::updateRepresentation()
297 {
298         // known macro?
299         if (macro_ == 0)
300                 return;
301
302         // update requires
303         requires_ = macro_->requires();
304         
305         // non-normal mode? We are done!
306         if (displayMode_ != DISPLAY_NORMAL)
307                 return;
308
309         // macro changed?
310         if (!needsUpdate_)
311                 return;
312         
313         needsUpdate_ = false;
314         
315         // get default values of macro
316         vector<docstring> const & defaults = macro_->defaults();
317         
318         // create MathMacroArgumentValue objects pointing to the cells of the macro
319         vector<MathData> values(nargs());
320         for (size_t i = 0; i < nargs(); ++i) {
321                 ArgumentProxy * proxy;
322                 if (i < defaults.size()) 
323                         proxy = new ArgumentProxy(*this, i, defaults[i]);
324                 else
325                         proxy = new ArgumentProxy(*this, i);
326                 values[i].insert(0, MathAtom(proxy));
327         }
328         
329         // expanding macro with the values
330         macro_->expand(values, expanded_.cell(0));
331                 // get definition for list edit mode
332         docstring const & display = macro_->display();
333         asArray(display.empty() ? macro_->definition() : display, definition_);
334 }
335
336
337 void MathMacro::draw(PainterInfo & pi, int x, int y) const
338 {
339         Dimension const dim = dimension(*pi.base.bv);
340
341         setPosCache(pi, x, y);
342         int expx = x;
343         int expy = y;
344
345         if (displayMode_ == DISPLAY_INIT || displayMode_ == DISPLAY_INTERACTIVE_INIT) {         
346                 FontSetChanger dummy(pi.base, "lyxtex");
347                 pi.pain.text(x, y, from_ascii("\\") + name(), pi.base.font);
348         } else if (displayMode_ == DISPLAY_UNFOLDED) {
349                 FontSetChanger dummy(pi.base, "lyxtex");
350                 pi.pain.text(x, y, from_ascii("\\"), pi.base.font);
351                 x += mathed_string_width(pi.base.font, from_ascii("\\")) + 1;
352                 cell(0).draw(pi, x, y);
353                 drawMarkers(pi, expx, expy);
354         } else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST
355                    && editing_[pi.base.bv]) {
356                 // Macro will be edited in a old-style list mode here:
357                 
358                 CoordCache const & coords = pi.base.bv->coordCache();
359                 FontInfo const & labelFont = sane_font;
360                 
361                 // markers and box needs two pixels
362                 x += 2;
363                 
364                 // get maximal font height
365                 Dimension fontDim;
366                 math_font_max_dim(pi.base.font, fontDim.asc, fontDim.des);
367                 
368                 // draw label
369                 docstring label = from_ascii("Macro \\") + name() + from_ascii(": ");
370                 pi.pain.text(x, y, label, labelFont);
371                 x += mathed_string_width(labelFont, label);
372
373                 // draw definition
374                 definition_.draw(pi, x, y);
375                 Dimension const & defDim = coords.getArrays().dim(&definition_);
376                 y += max(fontDim.des, defDim.des);
377                                 
378                 // draw parameters
379                 docstring str = from_ascii("#9");
380                 int strw1 = mathed_string_width(labelFont, from_ascii("#9"));
381                 int strw2 = mathed_string_width(labelFont, from_ascii(": "));
382                 
383                 for (idx_type i = 0; i < nargs(); ++i) {
384                         // position of label
385                         Dimension const & cdim = coords.getArrays().dim(&cell(i));
386                         x = expx + 2;
387                         y += max(fontDim.asc, cdim.asc) + 1;
388                         
389                         // draw label
390                         str[1] = '1' + i;
391                         pi.pain.text(x, y, str, labelFont);
392                         x += strw1;
393                         pi.pain.text(x, y, from_ascii(":"), labelFont);
394                         x += strw2;
395                         
396                         // draw paramter
397                         cell(i).draw(pi, x, y);
398                         
399                         // next line
400                         y += max(fontDim.des, cdim.des);
401                 }
402                 
403                 pi.pain.rectangle(expx + 1, expy - dim.asc + 1, dim.wid - 3, 
404                                   dim.height() - 2, Color_mathmacroframe);
405                 drawMarkers2(pi, expx, expy);
406         } else {
407                 bool drawBox = lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_INLINE_BOX;
408                 
409                 // warm up cells
410                 for (size_t i = 0; i < nargs(); ++i)
411                         cell(i).setXY(*pi.base.bv, x, y);
412
413                 if (drawBox && editing_[pi.base.bv]) {
414                         // draw header and rectangle around
415                         FontInfo font = pi.base.font;
416                         augmentFont(font, from_ascii("lyxtex"));
417                         font.setSize(FONT_SIZE_TINY);
418                         font.setColor(Color_mathmacrolabel);
419                         Dimension namedim;
420                         mathed_string_dim(font, name(), namedim);
421
422                         pi.pain.fillRectangle(x, y - dim.asc, dim.wid, 1 + namedim.height() + 1, Color_mathmacrobg);
423                         pi.pain.text(x + 1, y - dim.asc + namedim.asc + 2, name(), font);
424                         expx += (dim.wid - expanded_.cell(0).dimension(*pi.base.bv).width()) / 2;
425                 }
426
427                 if (editing_[pi.base.bv]) {
428                         pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
429                         expanded_.cell(0).draw(pi, expx, expy);
430                         pi.pain.leaveMonochromeMode();
431
432                         if (drawBox)
433                                 pi.pain.rectangle(x, y - dim.asc, dim.wid, 
434                                                   dim.height(), Color_mathmacroframe);
435                 } else
436                         expanded_.cell(0).draw(pi, expx, expy);
437
438                 if (!drawBox)
439                         drawMarkers(pi, x, y);
440         }
441
442         // edit mode changed?
443         if (editing_[pi.base.bv] != editMode(pi.base.bv))
444                 pi.base.bv->cursor().updateFlags(Update::SinglePar);
445 }
446
447
448 void MathMacro::drawSelection(PainterInfo & pi, int x, int y) const
449 {
450         // We may have 0 arguments, but InsetMathNest requires at least one.
451         if (cells_.size() > 0)
452                 InsetMathNest::drawSelection(pi, x, y);
453 }
454
455
456 void MathMacro::setDisplayMode(MathMacro::DisplayMode mode, int appetite)
457 {
458         if (displayMode_ != mode) {             
459                 // transfer name if changing from or to DISPLAY_UNFOLDED
460                 if (mode == DISPLAY_UNFOLDED) {
461                         cells_.resize(1);
462                         asArray(name_, cell(0));
463                 } else if (displayMode_ == DISPLAY_UNFOLDED) {
464                         name_ = asString(cell(0));
465                         cells_.resize(0);
466                 }
467
468                 displayMode_ = mode;
469                 needsUpdate_ = true;
470         }
471         
472         // the interactive init mode is non-greedy by default
473         if (appetite == -1)
474                 appetite_ = (mode == DISPLAY_INTERACTIVE_INIT) ? 0 : 9;
475         else
476                 appetite_ = size_t(appetite);
477 }
478
479
480 MathMacro::DisplayMode MathMacro::computeDisplayMode() const
481 {
482         if (nextFoldMode_ == true && macro_ && !macro_->locked())
483                 return DISPLAY_NORMAL;
484         else
485                 return DISPLAY_UNFOLDED;
486 }
487
488
489 bool MathMacro::validName() const
490 {
491         docstring n = name();
492
493         // empty name?
494         if (n.size() == 0)
495                 return false;
496
497         // converting back and force doesn't swallow anything?
498         /*MathData ma;
499         asArray(n, ma);
500         if (asString(ma) != n)
501                 return false;*/
502
503         // valid characters?
504         for (size_t i = 0; i<n.size(); ++i) {
505                 if (!(n[i] >= 'a' && n[i] <= 'z')
506                     && !(n[i] >= 'A' && n[i] <= 'Z')
507                     && n[i] != '*') 
508                         return false;
509         }
510
511         return true;
512 }
513
514
515 void MathMacro::validate(LaTeXFeatures & features) const
516 {
517         if (!requires_.empty())
518                 features.require(requires_);
519
520         if (name() == "binom")
521                 features.require("binom");
522         
523         // validate the cells and the definition
524         if (displayMode() == DISPLAY_NORMAL) {
525                 definition_.validate(features);
526                 InsetMathNest::validate(features);
527         }
528 }
529
530
531 void MathMacro::edit(Cursor & cur, bool front, EntryDirection entry_from)
532 {
533         cur.updateFlags(Update::SinglePar);
534         InsetMathNest::edit(cur, front, entry_from);
535 }
536
537
538 Inset * MathMacro::editXY(Cursor & cur, int x, int y)
539 {
540         // We may have 0 arguments, but InsetMathNest requires at least one.
541         if (nargs() > 0) {
542                 cur.updateFlags(Update::SinglePar);
543                 return InsetMathNest::editXY(cur, x, y);                
544         } else
545                 return this;
546 }
547
548
549 void MathMacro::removeArgument(Inset::pos_type pos) {
550         if (displayMode_ == DISPLAY_NORMAL) {
551                 LASSERT(size_t(pos) < cells_.size(), /**/);
552                 cells_.erase(cells_.begin() + pos);
553                 if (size_t(pos) < attachedArgsNum_)
554                         --attachedArgsNum_;
555                 if (size_t(pos) < optionals_) {
556                         --optionals_;
557                 }
558
559                 needsUpdate_ = true;
560         }
561 }
562
563
564 void MathMacro::insertArgument(Inset::pos_type pos) {
565         if (displayMode_ == DISPLAY_NORMAL) {
566                 LASSERT(size_t(pos) <= cells_.size(), /**/);
567                 cells_.insert(cells_.begin() + pos, MathData());
568                 if (size_t(pos) < attachedArgsNum_)
569                         ++attachedArgsNum_;
570                 if (size_t(pos) < optionals_)
571                         ++optionals_;
572
573                 needsUpdate_ = true;
574         }
575 }
576
577
578 void MathMacro::detachArguments(vector<MathData> & args, bool strip)
579 {
580         LASSERT(displayMode_ == DISPLAY_NORMAL, /**/);  
581         args = cells_;
582
583         // strip off empty cells, but not more than arity-attachedArgsNum_
584         if (strip) {
585                 size_t i;
586                 for (i = cells_.size(); i > attachedArgsNum_; --i)
587                         if (!cell(i - 1).empty()) break;
588                 args.resize(i);
589         }
590
591         attachedArgsNum_ = 0;
592         expanded_.cell(0) = MathData();
593         cells_.resize(0);
594
595         needsUpdate_ = true;
596 }
597
598
599 void MathMacro::attachArguments(vector<MathData> const & args, size_t arity, int optionals)
600 {
601         LASSERT(displayMode_ == DISPLAY_NORMAL, /**/);
602         cells_ = args;
603         attachedArgsNum_ = args.size();
604         cells_.resize(arity);
605         expanded_.cell(0) = MathData();
606         optionals_ = optionals;
607
608         needsUpdate_ = true;
609 }
610
611
612 bool MathMacro::idxFirst(Cursor & cur) const 
613 {
614         cur.updateFlags(Update::SinglePar);
615         return InsetMathNest::idxFirst(cur);
616 }
617
618
619 bool MathMacro::idxLast(Cursor & cur) const 
620 {
621         cur.updateFlags(Update::SinglePar);
622         return InsetMathNest::idxLast(cur);
623 }
624
625
626 bool MathMacro::notifyCursorLeaves(Cursor const & old, Cursor & cur)
627 {
628         if (displayMode_ == DISPLAY_UNFOLDED) {
629                 docstring const & unfolded_name = name();
630                 if (unfolded_name != name_) {
631                         // The macro name was changed
632                         cur = old;
633                         bool left = cur.pos() == 0;
634                         cur.recordUndoInset();
635                         cur.popForward();
636                         cur.backspace();
637                         cur.insert(createInsetMath(unfolded_name));
638                         if (left)
639                                 cur.backwardPos();
640                         cur.updateFlags(Update::Force);
641                         return true;
642                 }
643         }
644         cur.updateFlags(Update::Force);
645         return InsetMathNest::notifyCursorLeaves(old, cur);
646 }
647
648
649 void MathMacro::fold(Cursor & cur)
650 {
651         if (!nextFoldMode_) {
652                 nextFoldMode_ = true;
653                 cur.updateFlags(Update::SinglePar);
654         }
655 }
656
657
658 void MathMacro::unfold(Cursor & cur)
659 {
660         if (nextFoldMode_) {
661                 nextFoldMode_ = false;
662                 cur.updateFlags(Update::SinglePar);
663         }
664 }
665
666
667 bool MathMacro::folded() const
668 {
669         return nextFoldMode_;
670 }
671
672
673 void MathMacro::write(WriteStream & os) const
674 {
675         MathEnsurer ensurer(os, macro_ != 0, true);
676
677         // non-normal mode
678         if (displayMode_ != DISPLAY_NORMAL) {
679                 os << "\\" << name();
680                 if (name().size() != 1 || isAlphaASCII(name()[0]))
681                         os.pendingSpace(true);
682                 return;
683         }
684
685         // normal mode
686         LASSERT(macro_, /**/);
687
688         // optional arguments make macros fragile
689         if (optionals_ > 0 && os.fragile())
690                 os << "\\protect";
691         
692         os << "\\" << name();
693         bool first = true;
694         
695         // Optional arguments:
696         // First find last non-empty optional argument
697         idx_type emptyOptFrom = 0;
698         idx_type i = 0;
699         for (; i < cells_.size() && i < optionals_; ++i) {
700                 if (!cell(i).empty())
701                         emptyOptFrom = i + 1;
702         }
703         
704         // print out optionals
705         for (i=0; i < cells_.size() && i < emptyOptFrom; ++i) {
706                 first = false;
707                 os << "[" << cell(i) << "]";
708         }
709         
710         // skip the tailing empty optionals
711         i = optionals_;
712         
713         // Print remaining macros 
714         for (; i < cells_.size(); ++i) {
715                 if (cell(i).size() == 1 
716                         && cell(i)[0].nucleus()->asCharInset()
717                         && cell(i)[0].nucleus()->asCharInset()->getChar() < 0x80) {
718                         if (first)
719                                 os << " ";
720                         os << cell(i);
721                 } else
722                         os << "{" << cell(i) << "}";
723                 first = false;
724         }
725
726         // add space if there was no argument
727         if (first)
728                 os.pendingSpace(true);
729 }
730
731
732 void MathMacro::maple(MapleStream & os) const
733 {
734         lyx::maple(expanded_.cell(0), os);
735 }
736
737
738 void MathMacro::mathmlize(MathStream & os) const
739 {
740         lyx::mathmlize(expanded_.cell(0), os);
741 }
742
743
744 void MathMacro::octave(OctaveStream & os) const
745 {
746         lyx::octave(expanded_.cell(0), os);
747 }
748
749
750 void MathMacro::infoize(odocstream & os) const
751 {
752         os << "Macro: " << name();
753 }
754
755
756 void MathMacro::infoize2(odocstream & os) const
757 {
758         os << "Macro: " << name();
759
760 }
761
762
763 bool MathMacro::completionSupported(Cursor const & cur) const
764 {
765         if (displayMode() != DISPLAY_UNFOLDED)
766                 return InsetMathNest::completionSupported(cur);
767
768         return lyxrc.completion_popup_math
769                 && displayMode() == DISPLAY_UNFOLDED
770                 && cur.bv().cursor().pos() == int(name().size());
771 }
772
773
774 bool MathMacro::inlineCompletionSupported(Cursor const & cur) const
775 {
776         if (displayMode() != DISPLAY_UNFOLDED)
777                 return InsetMathNest::inlineCompletionSupported(cur);
778
779         return lyxrc.completion_inline_math
780                 && displayMode() == DISPLAY_UNFOLDED
781                 && cur.bv().cursor().pos() == int(name().size());
782 }
783
784
785 bool MathMacro::automaticInlineCompletion() const
786 {
787         if (displayMode() != DISPLAY_UNFOLDED)
788                 return InsetMathNest::automaticInlineCompletion();
789
790         return lyxrc.completion_inline_math;
791 }
792
793
794 bool MathMacro::automaticPopupCompletion() const
795 {
796         if (displayMode() != DISPLAY_UNFOLDED)
797                 return InsetMathNest::automaticPopupCompletion();
798
799         return lyxrc.completion_popup_math;
800 }
801
802
803 CompletionList const * 
804 MathMacro::createCompletionList(Cursor const & cur) const
805 {
806         if (displayMode() != DISPLAY_UNFOLDED)
807                 return InsetMathNest::createCompletionList(cur);
808
809         return new MathCompletionList(cur.bv().cursor());
810 }
811
812
813 docstring MathMacro::completionPrefix(Cursor const & cur) const
814 {
815         if (displayMode() != DISPLAY_UNFOLDED)
816                 return InsetMathNest::completionPrefix(cur);
817
818         if (!completionSupported(cur))
819                 return docstring();
820         
821         return "\\" + name();
822 }
823
824
825 bool MathMacro::insertCompletion(Cursor & cur, docstring const & s,
826                                         bool finished)
827 {
828         if (displayMode() != DISPLAY_UNFOLDED)
829                 return InsetMathNest::insertCompletion(cur, s, finished);
830
831         if (!completionSupported(cur))
832                 return false;
833
834         // append completion
835         docstring newName = name() + s;
836         asArray(newName, cell(0));
837         cur.bv().cursor().pos() = name().size();
838         cur.updateFlags(Update::SinglePar);
839         
840         // finish macro
841         if (finished) {
842                 cur.bv().cursor().pop();
843                 ++cur.bv().cursor().pos();
844                 cur.updateFlags(Update::SinglePar);
845         }
846         
847         return true;
848 }
849
850
851 void MathMacro::completionPosAndDim(Cursor const & cur, int & x, int & y,
852         Dimension & dim) const
853 {
854         if (displayMode() != DISPLAY_UNFOLDED)
855                 InsetMathNest::completionPosAndDim(cur, x, y, dim);
856         
857         // get inset dimensions
858         dim = cur.bv().coordCache().insets().dim(this);
859         // FIXME: these 3 are no accurate, but should depend on the font.
860         // Now the popup jumps down if you enter a char with descent > 0.
861         dim.des += 3;
862         dim.asc += 3;
863         
864         // and position
865         Point xy
866         = cur.bv().coordCache().insets().xy(this);
867         x = xy.x_;
868         y = xy.y_;
869 }
870
871
872 } // namespace lyx