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