]> git.lyx.org Git - lyx.git/blobdiff - src/mathed/MathMacro.cpp
Reintroduce broken support for default macro arguments
[lyx.git] / src / mathed / MathMacro.cpp
index e01f7c213440528574c46b25c94977d094ea508a..f6791bbf7638869e66b98ba5bc6e97a96dd684ba 100644 (file)
@@ -30,6 +30,7 @@
 #include "LaTeXFeatures.h"
 #include "LyX.h"
 #include "LyXRC.h"
+#include "MetricsInfo.h"
 
 #include "frontends/Painter.h"
 
@@ -67,7 +68,55 @@ public:
        ///
        InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
        ///
+       bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const
+       {
+               // macro arguments are in macros
+               LATTEST(mi.base.macro_nesting > 0);
+               if (mi.base.macro_nesting == 1)
+                       mi.base.macro_nesting = 0;
+
+               MathRow::Element e_beg(MathRow::BEG_ARG, mi);
+               e_beg.macro = mathMacro_;
+               e_beg.ar = &mathMacro_->cell(idx_);
+               mrow.push_back(e_beg);
+
+               mathMacro_->macro()->unlock();
+               bool has_contents;
+               // handle default macro arguments
+               if (!mathMacro_->editMetrics(mi.base.bv)
+                       && mathMacro_->cell(idx_).empty())
+                       has_contents = def_.addToMathRow(mrow, mi);
+               else
+                       has_contents = mathMacro_->cell(idx_).addToMathRow(mrow, mi);
+               mathMacro_->macro()->lock();
+
+               // if there was no contents, and the contents is editable,
+               // then we insert a box instead.
+               if (!has_contents && mi.base.macro_nesting == 0) {
+                       MathRow::Element e(MathRow::BOX, mi);
+                       e.color = Color_mathline;
+                       mrow.push_back(e);
+                       has_contents = true;
+               }
+
+               if (mi.base.macro_nesting == 0)
+                       mi.base.macro_nesting = 1;
+
+               MathRow::Element e_end(MathRow::END_ARG, mi);
+               e_end.macro = mathMacro_;
+               e_end.ar = &mathMacro_->cell(idx_);
+
+               mrow.push_back(e_end);
+
+               return has_contents;
+       }
+       ///
        void metrics(MetricsInfo & mi, Dimension & dim) const {
+               // macro arguments are in macros
+               LATTEST(mi.base.macro_nesting > 0);
+               if (mi.base.macro_nesting == 1)
+                       mi.base.macro_nesting = 0;
+
                mathMacro_->macro()->unlock();
                mathMacro_->cell(idx_).metrics(mi, dim);
 
@@ -76,6 +125,8 @@ public:
                        def_.metrics(mi, dim);
 
                mathMacro_->macro()->lock();
+               if (mi.base.macro_nesting == 0)
+                       mi.base.macro_nesting = 1;
        }
        // write(), normalize(), infoize() and infoize2() are not needed since
        // MathMacro uses the definition and not the expanded cells.
@@ -93,6 +144,10 @@ public:
        void octave(OctaveStream & os) const { os << mathMacro_->cell(idx_); }
        ///
        void draw(PainterInfo & pi, int x, int y) const {
+               LATTEST(pi.base.macro_nesting > 0);
+               if (pi.base.macro_nesting == 1)
+                       pi.base.macro_nesting = 0;
+
                if (mathMacro_->editMetrics(pi.base.bv)) {
                        // The only way a ArgumentProxy can appear is in a cell of the
                        // MathMacro. Moreover the cells are only drawn in the DISPLAY_FOLDED
@@ -110,6 +165,9 @@ public:
                        def_.draw(pi, x, y);
                } else
                        mathMacro_->cell(idx_).draw(pi, x, y);
+
+               if (pi.base.macro_nesting == 0)
+                       pi.base.macro_nesting = 1;
        }
        ///
        size_t idx() const { return idx_; }
@@ -265,6 +323,45 @@ MathMacro::~MathMacro()
 }
 
 
+bool MathMacro::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
+{
+       // set edit mode for which we will have calculated row.
+       // This is the same as what is done in metrics().
+       d->editing_[mi.base.bv] = editMode(mi.base.bv);
+
+       if (displayMode() != MathMacro::DISPLAY_NORMAL
+           || d->editing_[mi.base.bv])
+               return InsetMath::addToMathRow(mrow, mi);
+
+       MathRow::Element e_beg(MathRow::BEG_MACRO, mi);
+       e_beg.macro = this;
+       mrow.push_back(e_beg);
+
+       ++mi.base.macro_nesting;
+
+       d->macro_->lock();
+       bool has_contents = d->expanded_.addToMathRow(mrow, mi);
+       d->macro_->unlock();
+
+       // if there was no contents and the array is editable, then we
+       // insert a grey box instead.
+       if (!has_contents && mi.base.macro_nesting == 1) {
+               MathRow::Element e(MathRow::BOX, mi);
+               e.color = Color_mathmacroblend;
+               mrow.push_back(e);
+               has_contents = true;
+       }
+
+       --mi.base.macro_nesting;
+
+       MathRow::Element e_end(MathRow::END_MACRO, mi);
+       e_end.macro = this;
+       mrow.push_back(e_end);
+
+       return has_contents;
+}
+
+
 Inset * MathMacro::clone() const
 {
        MathMacro * copy = new MathMacro(*this);
@@ -343,7 +440,7 @@ bool MathMacro::editMode(BufferView const * bv) const {
 }
 
 
-MacroData const * MathMacro::macro()
+MacroData const * MathMacro::macro() const
 {
        return d->macro_;
 }
@@ -357,20 +454,25 @@ bool MathMacro::editMetrics(BufferView const * bv) const
 
 void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
 {
+       // the macro contents is not editable (except the arguments)
+       ++mi.base.macro_nesting;
+
        // set edit mode for which we will have calculated metrics. But only
        d->editing_[mi.base.bv] = editMode(mi.base.bv);
 
        // calculate new metrics according to display mode
        if (d->displayMode_ == DISPLAY_INIT || d->displayMode_ == DISPLAY_INTERACTIVE_INIT) {
+               Changer dummy = mi.base.changeFontSet("lyxtex");
                mathed_string_dim(mi.base.font, from_ascii("\\") + name(), dim);
        } else if (d->displayMode_ == DISPLAY_UNFOLDED) {
+               Changer dummy = mi.base.changeFontSet("lyxtex");
                cell(0).metrics(mi, dim);
                Dimension bsdim;
                mathed_string_dim(mi.base.font, from_ascii("\\"), bsdim);
                dim.wid += bsdim.width() + 1;
                dim.asc = max(bsdim.ascent(), dim.ascent());
                dim.des = max(bsdim.descent(), dim.descent());
-               metricsMarkers(dim);
+               metricsMarkers(mi, dim);
        } else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST
                   && d->editing_[mi.base.bv]) {
                // Macro will be edited in a old-style list mode here:
@@ -410,10 +512,14 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
                dim.asc += 1;
                dim.des += 1;
                dim.wid += 2;
-               metricsMarkers2(dim);
+               metricsMarkers2(mi, dim);
        } else {
                LBUFERR(d->macro_);
 
+               Changer dummy = (currentMode() == TEXT_MODE)
+                       ? mi.base.font.changeShape(UP_SHAPE)
+                       : Changer();
+
                // calculate metrics, hoping that all cells are seen
                d->macro_->lock();
                d->expanded_.metrics(mi, dim);
@@ -432,7 +538,7 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
                if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_INLINE_BOX
                    && d->editing_[mi.base.bv]) {
                        FontInfo font = mi.base.font;
-                       augmentFont(font, from_ascii("lyxtex"));
+                       augmentFont(font, "lyxtex");
                        Dimension namedim;
                        mathed_string_dim(font, name(), namedim);
 #if 0
@@ -445,6 +551,9 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
                        dim.des += 2;
                }
        }
+
+       // restore macro nesting
+       --mi.base.macro_nesting;
 }
 
 
@@ -538,7 +647,8 @@ void MathMacro::updateRepresentation(Cursor * cur, MacroContext const & mc,
        }
        // get definition for list edit mode
        docstring const & display = d->macro_->display();
-       asArray(display.empty() ? d->macro_->definition() : display, d->definition_);
+       asArray(display.empty() ? d->macro_->definition() : display,
+               d->definition_, Parse::QUIET);
 }
 
 
@@ -551,10 +661,10 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const
        int expy = y;
 
        if (d->displayMode_ == DISPLAY_INIT || d->displayMode_ == DISPLAY_INTERACTIVE_INIT) {
-               FontSetChanger dummy(pi.base, "lyxtex");
+               Changer dummy = pi.base.changeFontSet("lyxtex");
                pi.pain.text(x, y, from_ascii("\\") + name(), pi.base.font);
        } else if (d->displayMode_ == DISPLAY_UNFOLDED) {
-               FontSetChanger dummy(pi.base, "lyxtex");
+               Changer dummy = pi.base.changeFontSet("lyxtex");
                pi.pain.text(x, y, from_ascii("\\"), pi.base.font);
                x += mathed_string_width(pi.base.font, from_ascii("\\")) + 1;
                cell(0).draw(pi, x, y);
@@ -612,7 +722,13 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const
                                  dim.height() - 2, Color_mathmacroframe);
                drawMarkers2(pi, expx, expy);
        } else {
+               // the macro contents is not editable (except the arguments)
+               ++pi.base.macro_nesting;
+
                bool drawBox = lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_INLINE_BOX;
+               Changer dummy = (currentMode() == TEXT_MODE)
+                       ? pi.base.font.changeShape(UP_SHAPE)
+                       : Changer();
 
                // warm up cells
                for (size_t i = 0; i < nargs(); ++i)
@@ -621,7 +737,7 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const
                if (drawBox && d->editing_[pi.base.bv]) {
                        // draw header and rectangle around
                        FontInfo font = pi.base.font;
-                       augmentFont(font, from_ascii("lyxtex"));
+                       augmentFont(font, "lyxtex");
                        font.setSize(FONT_SIZE_TINY);
                        font.setColor(Color_mathmacrolabel);
                        Dimension namedim;
@@ -643,8 +759,11 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const
                } else
                        d->expanded_.draw(pi, expx, expy);
 
+               --pi.base.macro_nesting;
+
                if (!drawBox)
                        drawMarkers(pi, x, y);
+
        }
 
        // edit mode changed?
@@ -747,6 +866,17 @@ size_t MathMacro::appetite() const
 }
 
 
+InsetMath::mode_type MathMacro::currentMode() const
+{
+       // User defined macros are always assumed to be mathmode macros.
+       // Only the global macros defined in lib/symbols may be textmode.
+
+       MacroData const * data = MacroTable::globalMacros().get(name());
+       bool textmode = data && data->symbol() && data->symbol()->extra == "textmode";
+       return textmode ? TEXT_MODE : MATH_MODE;
+}
+
+
 void MathMacro::validate(LaTeXFeatures & features) const
 {
        // Immediately after a document is loaded, in some cases the MacroData
@@ -924,7 +1054,13 @@ bool MathMacro::folded() const
 
 void MathMacro::write(WriteStream & os) const
 {
-       MathEnsurer ensurer(os, d->macro_ != 0, true);
+       MacroData const * data = MacroTable::globalMacros().get(name());
+       bool textmode_macro = data && data->symbol()
+                                  && data->symbol()->extra == "textmode";
+       bool needs_mathmode = data && (!data->symbol()
+                                      || data->symbol()->extra != "textmode");
+
+       MathEnsurer ensurer(os, needs_mathmode, true, textmode_macro);
 
        // non-normal mode
        if (d->displayMode_ != DISPLAY_NORMAL) {
@@ -938,6 +1074,15 @@ void MathMacro::write(WriteStream & os) const
        // we should be ok to continue even if this fails.
        LATTEST(d->macro_);
 
+       // We may already be in the argument of a macro
+       bool const inside_macro = os.insideMacro();
+       os.insideMacro(true);
+
+       // Enclose in braces to avoid latex errors with xargs if we have
+       // optional arguments and are in the optional argument of a macro
+       if (d->optionals_ && inside_macro)
+               os << '{';
+
        // Always protect macros in a fragile environment
        if (os.fragile())
                os << "\\protect";
@@ -976,9 +1121,13 @@ void MathMacro::write(WriteStream & os) const
                first = false;
        }
 
-       // add space if there was no argument
-       if (first)
+       // Close the opened brace or add space if there was no argument
+       if (d->optionals_ && inside_macro)
+               os << '}';
+       else if (first)
                os.pendingSpace(true);
+
+       os.insideMacro(inside_macro);
 }
 
 
@@ -1008,7 +1157,7 @@ void MathMacro::mathmlize(MathStream & os) const
                docstring const xmlname = d->macro_->xmlname();
                if (!xmlname.empty()) {
                        char const * type = d->macro_->MathMLtype();
-                       os << '<' << type << "> " << xmlname << " /<"
+                       os << '<' << type << "> " << xmlname << " </"
                           << type << '>';
                        return;
                }