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