The branch, betterspacing, has been updated.
  discards  849c811c1e09482ed80db1c1438b3b9eefef66fd (commit)
  discards  65279ea6b7c54bfa74feb944e7b0dbfe56a7c706 (commit)
  discards  d16571d27e9ac432bccd3e141e33bd00658aa927 (commit)
  discards  93098e760bfdc8b21a7df1a7f2a78d1596435a48 (commit)

This update added new revisions after undoing existing revisions.  That is
to say, the old revision is not a strict subset of the new revision.  This
situation occurs when you --force push a change and generate a repository
containing something like this:

 * -- * -- B -- O -- O -- O (849c811c1e09482ed80db1c1438b3b9eefef66fd)
            \
             N -- N -- N (5d74cc342831ba9adea7fc658c18cb9717884f0e)

When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.

- Log -----------------------------------------------------------------

commit 5d74cc342831ba9adea7fc658c18cb9717884f0e
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Thu Oct 13 20:07:45 2016 +0200

    Cleanup of lib/symbols
    
    Try as far as possible to use the same definitions as in the LaTeX files.
    
    An example of that is the definition and subsequent use of \joinrel,
    \relbar and \Relbar.

diff --git a/lib/symbols b/lib/symbols
index 7b969ad..4d2fc16 100644
--- a/lib/symbols
+++ b/lib/symbols
@@ -291,7 +291,7 @@ spadesuit          cmsy        127 170 mathord  &spades;
 lyxnot             cmsy         54  47 mathrel  /
 iffont cmsy
 # 9mu = 0.5em which is the extra space added to relation operators
-\def\not{\lyxnot\kern-9mu}
+\def\not{\lyxnot}
 else
 \def\not{\kern4mu\lyxnot\kern-19mu}
 endif
@@ -659,8 +659,7 @@ hslash             msb         125   0 mathord  &plankv;
 hbar               msb         126   0 mathord  &planck;
 backepsilon        msb         127   0 mathrel  &bepsi;
 
-lyxbar             cmsy        161   0 mathord  &mdash;
-lyxeq              cmr          61   0 mathord  =
+lyxbar             cmsy        161   0 mathrel  &mdash;
 lyxdabar           msa          57   0 mathord  &ndash;
 lyxright           msa          75   0 mathord  &rarr;
 lyxleft            msa          76   0 mathord  &larr;
@@ -962,14 +961,14 @@ bignplus           stmry 112   0 mathop     x  stmaryrd # 
caution: named hugenpl
 #rrbracketex        stmry 127   0 mathclose  x  stmaryrd # only in the font, 
not the .sty caution: named Hugerrbracketex in the font
 
 \def\varcopyright{c\kern-14mu\varbigcirc}                   stmaryrd
-\def\longarrownot{\kern5.5mu\arrownot\kern-5.5mu}           stmaryrd
-\def\Longarrownot{\kern5.5mu\Arrownot\kern-5.5mu}           stmaryrd
-\def\Mapsto{\Mapstochar\kern-9mu\Rightarrow}                stmaryrd
+\def\longarrownot{\mathrel{\kern5.5mu}\arrownot\mathrel{\kern-5.5mu}} stmaryrd
+\def\Longarrownot{\mathrel{\kern5.5mu}\Arrownot\mathrel{\kern-5.5mu}} stmaryrd
+\def\Mapsto{\Mapstochar\Rightarrow}                         stmaryrd
 \def\mapsfrom{\leftarrow\kern-9mu\mapsfromchar}             stmaryrd
 \def\Mapsfrom{\Leftarrow\kern-9mu\Mapsfromchar}             stmaryrd
-\def\Longmapsto{\Mapstochar\kern-7mu\Longrightarrow}        stmaryrd
-\def\longmapsfrom{\longleftarrow\kern-7mu\mapsfromchar}     stmaryrd
-\def\Longmapsfrom{\Longleftarrow\kern-7mu\Mapsfromchar}     stmaryrd
+\def\Longmapsto{\Mapstochar\Longrightarrow}                 stmaryrd
+\def\longmapsfrom{\longleftarrow\mapsfromchar}              stmaryrd
+\def\Longmapsfrom{\Longleftarrow\Mapsfromchar}              stmaryrd
 
 # symbols from the mhchem package, all of them are equivalent to a math symbol
 # mhchem is not loaded because these commands can only be used inside
@@ -1128,29 +1127,32 @@ pod                lyxblacktext  0   0 func     x     
amsmath
 \def\notin{\not\in}                                             mathrel &notin;
 \def\slash{/}
 
-\def\longleftrightarrow{\leftarrow\kern-12.5mu\rightarrow}
-\def\Longleftrightarrow{\Leftarrow\kern-12.5mu\Rightarrow}
-\def\iff{\Leftarrow\kern-12.5mu\Rightarrow}
+\def\joinrel{\mathrel{\kern-3mu}}
+\def\relbar{\lyxbar}
+\def\Relbar{\mathrel{=}}
+\def\longleftrightarrow{\leftarrow\joinrel\rightarrow}
+\def\Longleftrightarrow{\Leftarrow\joinrel\Rightarrow}
+\def\iff{\Leftarrow\joinrel\Rightarrow}
 \def\doteq{\stackrel{\cdot}{=}}
 
 iffont cmsy
-\def\longrightarrow{\lyxbar\kern-11mu\rightarrow}               mathrel &xrarr;
-\def\longleftarrow{\leftarrow\kern-11mu\lyxbar}                 mathrel &xlarr;
-\def\Longrightarrow{\lyxeq\kern-9.5mu\Rightarrow}               mathrel 
&#x27F9;
-\def\Longleftarrow{\Leftarrow\kern-9.5mu\lyxeq}                 mathrel 
&#x27F8;
+\def\longrightarrow{\relbar\joinrel\rightarrow}                 mathrel &xrarr;
+\def\longleftarrow{\leftarrow\joinrel\relbar}                   mathrel &xlarr;
+\def\Longrightarrow{\Relbar\joinrel\Rightarrow}                 mathrel 
&#x27F9;
+\def\Longleftarrow{\Leftarrow\joinrel\Relbar}                   mathrel 
&#x27F8;
 \def\implies{\Longrightarrow}                                   mathrel 
&#x27F9; amsmath
 \def\impliedby{\Longleftarrow}                                  mathrel 
&#x27F8; amsmath
-\def\mapsto{\mapstochar\kern-9mu\rightarrow}                    mathrel 
&#x21A4;
-\def\longmapsto{\mapstochar\kern-6mu\lyxbar\kern-11mu\rightarrow} mathrel 
&#x27FB;
-\def\models{\vert\kern-7mu\lyxeq}                               mathrel &vDash;
+\def\mapsto{\mapstochar\rightarrow}                             mathrel 
&#x21A4;
+\def\longmapsto{\mapstochar\relbar\rightarrow}                  mathrel 
&#x27FB;
+\def\models{\mathrel{\vert}\joinrel\Relbar}                         mathrel 
&vDash;
 else
 \def\implies{=>}                                                mathrel 
&#x27F9; amsmath
 \def\impliedby{<=}                                              mathrel 
&#x27F8; amsmath
 endif
 iffont cmm
-\def\hookrightarrow{\lhook\kern-12mu\rightarrow}                mathrel 
&#x21AA;
-\def\hookleftarrow{\leftarrow\kern-12mu\rhook}                  mathrel 
&#x21A9;
-\def\bowtie{\triangleright\kern-6mu\triangleleft}               mathrel 
&#x22C8;
+\def\hookrightarrow{\lhook\joinrel\rightarrow}                  mathrel 
&#x21AA;
+\def\hookleftarrow{\leftarrow\joinrel\rhook}                    mathrel 
&#x21A9;
+\def\bowtie{\mathrel\triangleright\joinrel\mathrel\triangleleft} mathrel 
&#x22C8;
 endif
 iffont msa
 \def\dashrightarrow{\lyxdabar\lyxdabar\lyxright}                mathrel 
&#x290F; amssymb
@@ -1161,17 +1163,17 @@ else
 endif
 \def\dasharrow{\dashrightarrow}                                 mathrel 
&#x290F; amssymb
 iffont msb
-\def\Join{\ltimes\kern-18.5mu\rtimes}                           amssymb
+\def\Join{\mathrel{\ltimes\kern-13.5mu\rtimes}}                 amssymb
 else
 \def\Join{|x|}                                                  amssymb
 endif
-# Fixme: latin-1 chars in text file
+# FIXME: UTF-8 chars in text file
 \def\AA{\AA}{Å} textmode &Aring;  amstext,lyxmathsym
 \def\O{\O}{Ø}   textmode &Oslash; amstext,lyxmathsym
 
 iffont cmsy
 # The \sim is placed too high...
-\def\cong{\stackrel{_\sim}{=}}                                  mathrel &cong;
+\def\cong{\stackrel{\sim}{=}}                                   mathrel &cong;
 lyxsurd               cmsy        112 0 mathord  &radic;
 \def\surd{^\lyxsurd}                                            mathord &radic;
 \def\textdegree{\kern-1mu^{\circ}\kern-4mu} textmode &deg; 
textcomp,amstext,lyxmathsym

commit e25cc60f5e8bf0f328f82996bb4c0ad75a7dd0a0
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Wed Oct 5 00:25:38 2016 +0200

    Only display a blue rectangle for editable empty insets
    
    Empty insets should use a minimal amount of space, especially when
    they are part of a built-in macro in lib/symbols.
    
    With this change, blue rectangles signal actually editable places.
    Empty macros in editable data are shown as grey boxes, but they do not
    appear when further nested.
    
    This is done by adding a new type BOX of MathRow::Element object and a
    MetricsInfo::macro_nesting that keeps track of macros (and is reset to
    0 in editable macro arguments).

diff --git a/src/Dimension.h b/src/Dimension.h
index bd8f10d..0607be6 100644
--- a/src/Dimension.h
+++ b/src/Dimension.h
@@ -32,6 +32,8 @@ public:
        void operator+=(Dimension const & dim);
        /// set to empty box
        void clear() { wid = asc = des = 0; }
+       /// check if box is empty
+       bool empty() const { return wid == 0 && asc == 0 && wid == 0; }
        /// get height
        int height() const { return asc + des; }
        /// get ascent
diff --git a/src/MetricsInfo.cpp b/src/MetricsInfo.cpp
index 589e924..82ec7b4 100644
--- a/src/MetricsInfo.cpp
+++ b/src/MetricsInfo.cpp
@@ -87,7 +87,7 @@ Changer MetricsBase::changeFontSet(string const & name, bool 
cond)
 
 MetricsInfo::MetricsInfo(BufferView * bv, FontInfo font, int textwidth,
                          MacroContext const & mc)
-       : base(bv, font, textwidth), macrocontext(mc)
+       : base(bv, font, textwidth), macro_nesting(0), macrocontext(mc)
 {}
 
 
diff --git a/src/MetricsInfo.h b/src/MetricsInfo.h
index d2dd8b7..415fe25 100644
--- a/src/MetricsInfo.h
+++ b/src/MetricsInfo.h
@@ -101,6 +101,8 @@ public:
 
        ///
        MetricsBase base;
+       /// count wether the current mathdata is nested in macro(s)
+       int macro_nesting;
        /// The context to resolve macros
        MacroContext const & macrocontext;
 };
diff --git a/src/mathed/InsetMath.cpp b/src/mathed/InsetMath.cpp
index a8642c1..d2182a5 100644
--- a/src/mathed/InsetMath.cpp
+++ b/src/mathed/InsetMath.cpp
@@ -56,9 +56,9 @@ MathClass InsetMath::mathClass() const
 }
 
 
-bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo const &) const
+bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
 {
-       MathRow::Element e;
+       MathRow::Element e(MathRow::INSET, mi);
        e.inset = this;
        e.mclass = mathClass();
        mrow.push_back(e);
diff --git a/src/mathed/InsetMath.h b/src/mathed/InsetMath.h
index 3d3e1ef..92e4055 100644
--- a/src/mathed/InsetMath.h
+++ b/src/mathed/InsetMath.h
@@ -167,7 +167,7 @@ public:
        /// The class of the math object (used primarily for spacing)
        virtual MathClass mathClass() const;
        /// Add this inset to a math row. Return true if contents got added
-       virtual bool addToMathRow(MathRow &, MetricsInfo const & mi) const;
+       virtual bool addToMathRow(MathRow &, MetricsInfo & mi) const;
 
        /// identifies things that can get scripts
        virtual bool isScriptable() const { return false; }
diff --git a/src/mathed/MathData.cpp b/src/mathed/MathData.cpp
index 8b7a89d..f2100af 100644
--- a/src/mathed/MathData.cpp
+++ b/src/mathed/MathData.cpp
@@ -31,7 +31,6 @@
 #include "mathed/InsetMathUnknown.h"
 
 #include "frontends/FontMetrics.h"
-#include "frontends/Painter.h"
 
 #include "support/debug.h"
 #include "support/docstream.h"
@@ -217,7 +216,7 @@ void MathData::touch() const
 }
 
 
-bool MathData::addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
+bool MathData::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
 {
        bool has_contents = false;
        BufferView * bv = mi.base.bv;
@@ -275,12 +274,6 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) 
const
        slevel_ = (4 * xascent) / 5;
        sshift_ = xascent / 4;
 
-       if (empty()) {
-               // Cache the dimension.
-               mi.base.bv->coordCache().arrays().add(this, dim);
-               return;
-       }
-
        MathRow mrow(mi, this);
        mrow_cache_[mi.base.bv] = mrow;
        mrow.metrics(mi, dim);
@@ -299,11 +292,6 @@ void MathData::draw(PainterInfo & pi, int const x, int 
const y) const
 
        Dimension const & dim = bv.coordCache().getArrays().dim(this);
 
-       if (empty()) {
-               pi.pain.rectangle(x, y - dim.ascent(), dim.width(), 
dim.height(), Color_mathline);
-               return;
-       }
-
        // don't draw outside the workarea
        if (y + dim.descent() <= 0
                || y - dim.ascent() >= bv.workHeight()
diff --git a/src/mathed/MathData.h b/src/mathed/MathData.h
index fa82ee9..9eae466 100644
--- a/src/mathed/MathData.h
+++ b/src/mathed/MathData.h
@@ -122,7 +122,7 @@ public:
        MathAtom const & operator[](pos_type) const;
 
        /// Add this array to a math row. Return true if contents got added
-       bool addToMathRow(MathRow &, MetricsInfo const & mi) const;
+       bool addToMathRow(MathRow &, MetricsInfo & mi) const;
 
        /// rebuild cached metrics information
        void metrics(MetricsInfo & mi, Dimension & dim) const;
diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp
index 7656600..b2dd0f9 100644
--- a/src/mathed/MathMacro.cpp
+++ b/src/mathed/MathMacro.cpp
@@ -68,28 +68,49 @@ public:
        ///
        InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
        ///
-       bool addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
+       bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const
        {
-               MathRow::Element e(MathRow::BEG_ARG);
-               e.macro = mathMacro_;
-               e.ar = &mathMacro_->cell(idx_);
-               mrow.push_back(e);
+               // macro arguments are in macros
+               LATTEST(mi.macro_nesting > 0);
+               if (mi.macro_nesting == 1)
+                       mi.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 const has_contents = 
mathMacro_->cell(idx_).addToMathRow(mrow, mi);
+               bool has_contents = mathMacro_->cell(idx_).addToMathRow(mrow, 
mi);
                mathMacro_->macro()->lock();
 
-               e.type = MathRow::END_ARG;
-               mrow.push_back(e);
+               if (mi.macro_nesting == 0)
+                       mi.macro_nesting = 1;
 
-               if (has_contents)
-                       return true;
-               // if there was no contents, then we insert the empty macro 
inset
-               // instead.
-               return InsetMath::addToMathRow(mrow, mi);
+               // if there was no contents, and the contents is editable,
+               // then we insert a box instead.
+               if (!has_contents && mi.macro_nesting == 1) {
+                       MathRow::Element e(MathRow::BOX, mi);
+                       e.color = Color_mathline;
+                       mrow.push_back(e);
+                       has_contents = true;
+               }
+
+               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.macro_nesting > 0);
+               if (mi.macro_nesting == 1)
+                       mi.macro_nesting = 0;
+
                mathMacro_->macro()->unlock();
                mathMacro_->cell(idx_).metrics(mi, dim);
 
@@ -98,6 +119,8 @@ public:
                        def_.metrics(mi, dim);
 
                mathMacro_->macro()->lock();
+               if (mi.macro_nesting == 0)
+                       mi.macro_nesting = 1;
        }
        // write(), normalize(), infoize() and infoize2() are not needed since
        // MathMacro uses the definition and not the expanded cells.
@@ -287,31 +310,42 @@ MathMacro::~MathMacro()
 }
 
 
-bool MathMacro::addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
+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]) {
-               MathRow::Element e(MathRow::BEG_MACRO);
-               e.macro = this;
-               mrow.push_back(e);
+       if (displayMode() != MathMacro::DISPLAY_NORMAL
+           || d->editing_[mi.base.bv])
+               return InsetMath::addToMathRow(mrow, mi);
 
-               d->macro_->lock();
-               bool const has_contents = d->expanded_.addToMathRow(mrow, mi);
-               d->macro_->unlock();
+       MathRow::Element e_beg(MathRow::BEG_MACRO, mi);
+       e_beg.macro = this;
+       mrow.push_back(e_beg);
 
-               e.type = MathRow::END_MACRO;
-               mrow.push_back(e);
+       ++mi.macro_nesting;
 
-               if (has_contents)
-                       return true;
-               // if there was no contents, then we insert the empty macro 
inset
-               // instead.
+       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.macro_nesting == 1) {
+               MathRow::Element e(MathRow::BOX, mi);
+               e.color = Color_mathmacroblend;
+               mrow.push_back(e);
+               has_contents = true;
        }
-       return InsetMath::addToMathRow(mrow, mi);
+
+       --mi.macro_nesting;
+
+       MathRow::Element e_end(MathRow::END_MACRO, mi);
+       e_end.macro = this;
+       mrow.push_back(e_end);
+
+       return has_contents;
 }
 
 
@@ -407,6 +441,9 @@ 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.macro_nesting;
+
        // set edit mode for which we will have calculated metrics. But only
        d->editing_[mi.base.bv] = editMode(mi.base.bv);
 
@@ -495,6 +532,9 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) 
const
                        dim.des += 2;
                }
        }
+
+       // restore macro nesting
+       --mi.macro_nesting;
 }
 
 
diff --git a/src/mathed/MathMacro.h b/src/mathed/MathMacro.h
index 9c6fdb1..4eb93ba 100644
--- a/src/mathed/MathMacro.h
+++ b/src/mathed/MathMacro.h
@@ -39,7 +39,7 @@ public:
        ///
        /// If the macro is in normal edit mode, dissolve its contents in
        /// the row. Otherwise, just insert the inset.
-       bool addToMathRow(MathRow &, MetricsInfo const & mi) const;
+       bool addToMathRow(MathRow &, MetricsInfo & mi) const;
        ///
        void draw(PainterInfo & pi, int x, int y) const;
        /// draw selection background
diff --git a/src/mathed/MathRow.cpp b/src/mathed/MathRow.cpp
index 656b4e5..6a573dc 100644
--- a/src/mathed/MathRow.cpp
+++ b/src/mathed/MathRow.cpp
@@ -22,6 +22,7 @@
 #include "CoordCache.h"
 #include "MetricsInfo.h"
 
+#include "frontends/FontMetrics.h"
 #include "frontends/Painter.h"
 
 #include "support/debug.h"
@@ -35,26 +36,33 @@ using namespace std;
 namespace lyx {
 
 
-MathRow::Element::Element(Type t, MathClass const mc)
-       : type(t),
-         inset(0), mclass(mc), before(0), after(0), compl_unique_to(0),
-         macro(0)
+MathRow::Element::Element(Type t, MetricsInfo &mi)
+       : type(t), macro_nesting(mi.macro_nesting),
+         inset(0), mclass(MC_ORD), before(0), after(0), compl_unique_to(0),
+         macro(0), color(Color_red)
 {}
 
 
-MathRow::MathRow(MetricsInfo const & mi, MathData const * ar)
+MathRow::MathRow(MetricsInfo & mi, MathData const * ar)
 {
-       if (ar->empty())
-               return;
-
        // First there is a dummy element of type "open"
-       push_back(Element(BEGIN, MC_OPEN));
+       push_back(Element(BEGIN, mi));
+       back().mclass = MC_OPEN;
 
        // Then insert the MathData argument
-       ar->addToMathRow(*this, mi);
+       bool const has_contents = ar->addToMathRow(*this, mi);
+
+       // empty arrays are visible when they are editable
+       // we reserve the necessary space anyway (even if nothing gets drawn)
+       if (!has_contents) {
+               Element e(BOX, mi);
+               e.color = Color_mathline;
+               push_back(e);
+       }
 
        // Finally there is a dummy element of type "close"
-       push_back(Element(END, MC_CLOSE));
+       push_back(Element(END, mi));
+       back().mclass = MC_CLOSE;
 
        // update classes
        for (int i = 1 ; i != static_cast<int>(elements_.size()) - 1 ; ++i) {
@@ -114,9 +122,9 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) 
const
        map<MathMacro const *, Dimension> dim_macros;
        map<MathData const *, Dimension> dim_arrays;
        CoordCache & coords = mi.base.bv->coordCache();
-
        for (Element const & e : elements_) {
                Dimension d;
+               mi.macro_nesting = e.macro_nesting;
                switch (e.type) {
                case BEGIN:
                case END:
@@ -125,12 +133,6 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) 
const
                        e.inset->metrics(mi, d);
                        d.wid += e.before + e.after;
                        coords.insets().add(e.inset, d);
-                       dim += d;
-                       // Now add the dimension to current macros and 
arguments.
-                       for (auto & dim_macro : dim_macros)
-                               dim_macro.second += d;
-                       for (auto & dim_array : dim_arrays)
-                               dim_array.second += d;
                        break;
                case BEG_MACRO:
                        e.macro->macro()->lock();
@@ -158,6 +160,19 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) 
const
                        coords.arrays().add(e.ar, dim_arrays[e.ar]);
                        dim_arrays.erase(e.ar);
                        break;
+               case BOX:
+                       d = theFontMetrics(mi.base.font).dimension('I');
+                       d.wid += e.before + e.after;
+                       break;
+               }
+
+               if (!d.empty()) {
+                       dim += d;
+                       // Now add the dimension to current macros and 
arguments.
+                       for (auto & dim_macro : dim_macros)
+                               dim_macro.second += d;
+                       for (auto & dim_array : dim_arrays)
+                               dim_array.second += d;
                }
 
                if (e.compl_text.empty())
@@ -174,7 +189,6 @@ void MathRow::draw(PainterInfo & pi, int x, int const y) 
const
 {
        CoordCache & coords = pi.base.bv->coordCache();
        for (Element const & e : elements_) {
-               Dimension d;
                switch (e.type) {
                case INSET: {
                        coords.insets().add(e.inset, x, y);
@@ -205,6 +219,15 @@ void MathRow::draw(PainterInfo & pi, int x, int const y) 
const
                        if (e.macro->editMetrics(pi.base.bv))
                                pi.pain.enterMonochromeMode(Color_mathbg, 
Color_mathmacroblend);
                        break;
+               case BOX: {
+                       Dimension const d = 
theFontMetrics(pi.base.font).dimension('I');
+                       // the box is not visible in non-editable context 
(except for grey macro boxes).
+                       if (e.macro_nesting == 0 || e.color == 
Color_mathmacroblend)
+                               pi.pain.rectangle(x + e.before, y - d.ascent(),
+                                                                 d.width(), 
d.height(), e.color);
+                       x += d.wid;
+                       break;
+               }
                case BEGIN:
                case END:
                case END_MACRO:
@@ -271,6 +294,9 @@ ostream & operator<<(ostream & os, MathRow::Element const & 
e)
        case MathRow::END_ARG:
                os << ")";
                break;
+       case MathRow::BOX:
+               os << "@";
+               break;
        }
        return os;
 }
diff --git a/src/mathed/MathRow.h b/src/mathed/MathRow.h
index d0260e2..3d31e1c 100644
--- a/src/mathed/MathRow.h
+++ b/src/mathed/MathRow.h
@@ -14,6 +14,8 @@
 
 #include "MathClass.h"
 
+#include "ColorCode.h"
+
 #include "support/docstring.h"
 
 #include <vector>
@@ -51,16 +53,19 @@ public:
                END_ARG, // a macro argument ends here
                BEGIN, // dummy element before row
                END, // dummy element after row
+               BOX // an empty box
        };
 
        // An elements, together with its spacing
        struct Element
        {
                ///
-               Element(Type t = INSET, MathClass const mc = MC_ORD);
+               Element(Type t, MetricsInfo & mi);
 
                /// Classifies the contents of the object
                Type type;
+               /// count wether the current mathdata is nested in macro(s)
+               int macro_nesting;
 
                /// When type is INSET
                /// the math inset
@@ -80,6 +85,9 @@ public:
 
                // type is BEG_ARG, END_ARG
                MathData const * ar;
+
+               // type is BOX
+               ColorCode color;
        };
 
        ///
@@ -105,7 +113,7 @@ public:
 
        // create the math row by unwinding all macros in the MathData and
        // compute the spacings.
-       MathRow(MetricsInfo const & mi, MathData const * ar);
+       MathRow(MetricsInfo & mi, MathData const * ar);
 
        //
        void metrics(MetricsInfo & mi, Dimension & dim) const;

commit d0a095e7e68808f1d76e9b52761692d9034b15b0
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Wed Oct 5 00:25:38 2016 +0200

    Add support for \mathbin and friends
    
    All they do is change the class of the elements that they contain.
    
    As an application, use \mathrel in lib/symbols. This allows to mostly
    use the mathtools.sty definitions as is.

diff --git a/lib/symbols b/lib/symbols
index ba2b6d3..7b969ad 100644
--- a/lib/symbols
+++ b/lib/symbols
@@ -1100,20 +1100,20 @@ pod                lyxblacktext  0   0 func     x     
amsmath
 
 
 # mathtools.sty
-\def\vcentcolon{:}                                              mathrel :      
  mathtools
-\def\dblcolon{\vcentcolon\kern-8mu\vcentcolon}                  mathrel ::     
  mathtools
-\def\coloneqq{\vcentcolon\kern-7mu=}                            mathrel 
&#x2254; mathtools
-\def\Coloneqq{\dblcolon\kern-7mu=}                              mathrel ::=    
  mathtools
-\def\coloneq{\vcentcolon\kern-7mu-}                             mathrel :-     
  mathtools
-\def\Coloneq{\dblcolon\kern-7mu-}                               mathrel ::-    
  mathtools
-\def\eqqcolon{=\kern-8mu\vcentcolon}                            mathrel 
&#x2255; mathtools
-\def\Eqqcolon{=\kern-8mu\dblcolon}                              mathrel =::    
  mathtools
-\def\eqcolon{-\kern-8mu\vcentcolon}                             mathrel -:     
  mathtools
-\def\Eqcolon{-\kern-8mu\dblcolon}                               mathrel -::    
  mathtools
-\def\colonapprox{\vcentcolon\kern-7mu\approx}                   mathrel :&ap;  
  mathtools
-\def\Colonapprox{\dblcolon\kern-7mu\approx}                     mathrel ::&ap; 
  mathtools
-\def\colonsim{\vcentcolon\kern-7mu\sim}                         mathrel :&sim; 
  mathtools
-\def\Colonsim{\dblcolon\kern-7mu\sim}                           mathrel 
::&sim;  mathtools
+\def\vcentcolon{\mathrel{:}}                                              
mathrel :        mathtools
+\def\dblcolon{\vcentcolon\mathrel{\kern-1.2mu}\vcentcolon}                  
mathrel ::       mathtools
+\def\coloneqq{\vcentcolon\mathrel{\kern-1.2mu}=}                            
mathrel &#x2254; mathtools
+\def\Coloneqq{\dblcolon\mathrel{\kern-1.2mu}=}                              
mathrel ::=      mathtools
+\def\coloneq{\vcentcolon\mathrel{\kern-1.2mu}\mathrel{-}}                      
       mathrel :-       mathtools
+\def\Coloneq{\dblcolon\mathrel{\kern-1.2mu}\mathrel{-}}                        
       mathrel ::-      mathtools
+\def\eqqcolon{=\mathrel{\kern-1.2mu}\vcentcolon}                            
mathrel &#x2255; mathtools
+\def\Eqqcolon{=\mathrel{\kern-1.2mu}\dblcolon}                              
mathrel =::      mathtools
+\def\eqcolon{-\mathrel{\kern-1.2mu}\vcentcolon}                             
mathrel -:       mathtools
+\def\Eqcolon{-\mathrel{\kern-1.2mu}\dblcolon}                               
mathrel -::      mathtools
+\def\colonapprox{\vcentcolon\mathrel{\kern-1.2mu}\approx}                   
mathrel :&ap;    mathtools
+\def\Colonapprox{\dblcolon\mathrel{\kern-1.2mu}\approx}                     
mathrel ::&ap;   mathtools
+\def\colonsim{\vcentcolon\mathrel{\kern-1.2mu}\sim}                         
mathrel :&sim;   mathtools
+\def\Colonsim{\dblcolon\mathrel{\kern-1.2mu}\sim}                           
mathrel ::&sim;  mathtools
 
 
 #
diff --git a/src/Makefile.am b/src/Makefile.am
index 44f92db..43037e2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -401,6 +401,7 @@ SOURCEFILESMATHED = \
        mathed/InsetMath.cpp \
        mathed/InsetMathCases.cpp \
        mathed/InsetMathChar.cpp \
+       mathed/InsetMathClass.cpp \
        mathed/InsetMathColor.cpp \
        mathed/InsetMathComment.cpp \
        mathed/InsetMathDecoration.cpp \
@@ -475,6 +476,7 @@ HEADERFILESMATHED = \
        mathed/InsetMathCancelto.h \
        mathed/InsetMathCases.h \
        mathed/InsetMathChar.h \
+       mathed/InsetMathClass.h \
        mathed/InsetMathColor.h \
        mathed/InsetMathComment.h \
        mathed/InsetMathDelim.h \
diff --git a/src/insets/InsetCode.h b/src/insets/InsetCode.h
index 1df6800..7d4632c 100644
--- a/src/insets/InsetCode.h
+++ b/src/insets/InsetCode.h
@@ -235,6 +235,8 @@ enum InsetCode {
        ///
        IPADECO_CODE,
        ///
+       MATH_CLASS_CODE,
+       ///
        INSET_CODE_SIZE
 };
 
diff --git a/src/mathed/InsetMathClass.cpp b/src/mathed/InsetMathClass.cpp
new file mode 100644
index 0000000..10a9600
--- /dev/null
+++ b/src/mathed/InsetMathClass.cpp
@@ -0,0 +1,57 @@
+/**
+ * \file InsetMathClass.cpp
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author André Pönitz
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#include <config.h>
+
+#include "InsetMathClass.h"
+
+#include "support/docstream.h"
+
+
+namespace lyx {
+
+InsetMathClass::InsetMathClass(Buffer * buf, MathClass mc)
+       : InsetMathNest(buf, 1), math_class_(mc)
+{}
+
+
+Inset * InsetMathClass::clone() const
+{
+       return new InsetMathClass(*this);
+}
+
+
+void InsetMathClass::metrics(MetricsInfo & mi, Dimension & dim) const
+{
+       cell(0).metrics(mi, dim);
+       metricsMarkers(dim);
+}
+
+
+void InsetMathClass::draw(PainterInfo & pi, int x, int y) const
+{
+       cell(0).draw(pi, x + 2, y);
+       drawMarkers(pi, x, y);
+}
+
+
+docstring InsetMathClass::name() const
+{
+       return class_to_string(math_class_);
+}
+
+
+void InsetMathClass::infoize(odocstream & os) const
+{
+       os << name() << " ";
+}
+
+
+} // namespace lyx
diff --git a/src/mathed/InsetMathClass.h b/src/mathed/InsetMathClass.h
new file mode 100644
index 0000000..f50040d
--- /dev/null
+++ b/src/mathed/InsetMathClass.h
@@ -0,0 +1,50 @@
+// -*- C++ -*-
+/**
+ * \file InsetMathClass.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author André Pönitz
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#ifndef MATH_CLASSINSET_H
+#define MATH_CLASSINSET_H
+
+#include "MathClass.h"
+
+#include "InsetMathNest.h"
+
+
+namespace lyx {
+
+
+/// Support for LaTeX's \\mathxxx class-changing commands
+
+class InsetMathClass : public InsetMathNest {
+public:
+       ///
+       InsetMathClass(Buffer * buf, MathClass);
+       ///
+       docstring name() const;
+       ///
+       MathClass mathClass() const { return math_class_; }
+       ///
+       void metrics(MetricsInfo & mi, Dimension & dim) const;
+       ///
+       void draw(PainterInfo & pi, int x, int y) const;
+       ///
+       void infoize(odocstream & os) const;
+       ///
+       InsetCode lyxCode() const { return MATH_CLASS_CODE; }
+
+private:
+       virtual Inset * clone() const;
+       ///
+       MathClass math_class_;
+};
+
+
+} // namespace lyx
+#endif
diff --git a/src/mathed/MathFactory.cpp b/src/mathed/MathFactory.cpp
index bb7be8b..f000937 100644
--- a/src/mathed/MathFactory.cpp
+++ b/src/mathed/MathFactory.cpp
@@ -19,6 +19,7 @@
 #include "InsetMathCancel.h"
 #include "InsetMathCancelto.h"
 #include "InsetMathCases.h"
+#include "InsetMathClass.h"
 #include "InsetMathColor.h"
 #include "InsetMathDecoration.h"
 #include "InsetMathDots.h"
@@ -662,10 +663,13 @@ MathAtom createInsetMath(docstring const & s, Buffer * 
buf)
                return MathAtom(new InsetMathSpecialChar(s));
        if (s == " ")
                return MathAtom(new InsetMathSpace(" ", ""));
-
        if (s == "regexp")
                return MathAtom(new InsetMathHull(buf, hullRegexp));
 
+       MathClass const mc = string_to_class(s);
+       if (mc != MC_UNKNOWN)
+               return MathAtom(new InsetMathClass(buf, mc));
+
        return MathAtom(new MathMacro(buf, s));
 }
 

commit 5f04c9ffa4db4b8e5de916033cb4545f2cb93bb0
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Mon Aug 22 14:17:01 2016 +0200

    Set correctly the spacing between atoms in MathData
    
    New MathRow class which contains the description of a MathData object
    in terms of math class insets. Macros and their arguments used in the
    MathData object are dissolved (replaced with their contents) so that
    all math insets are typeset as a string together.
    
    To this end, we introduce methods addToMathRow() that allows each
    inset/mathdata to insert itself in terms of insets, begin/end macro
    and begin/end macro argument.
    
      + the class class and spacing are computed using the MathClass helpers.
    
      + metrics/draw of insets is delegated to the relevant methods. The
      case of draw is trickier, since many draw() methods rely on their
      metrics without any spacing added.
    
      + only the macros that not currently edited and are in mode
      DISPLAY_NORMAL are dissolved. The others are considered as normal
      math insets. The goal of handling spacing of macros is to be able to
      use proper macro definitions in lib/symbols.
    
      + The dimension and position of the macros and arguments are set 
separately.
    
    The MathRow data is cached in the MathData object in a
    bufferview-dependent way (possibly different dpi for different
    screens). Most of the work of MathData::metrics/draw is delegated to
    MathRow metrics/draw.

diff --git a/src/Makefile.am b/src/Makefile.am
index f37092e..44f92db 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -458,6 +458,7 @@ SOURCEFILESMATHED = \
        mathed/MathMacroArgument.cpp \
        mathed/MathMacroTemplate.cpp \
        mathed/MathParser.cpp \
+       mathed/MathRow.cpp \
        mathed/MathStream.cpp \
        mathed/MathSupport.cpp \
        mathed/TextPainter.cpp
@@ -530,6 +531,7 @@ HEADERFILESMATHED = \
        mathed/MathMacroTemplate.h \
        mathed/MathParser.h \
        mathed/MathParser_flags.h \
+       mathed/MathRow.h \
        mathed/ReplaceData.h \
        mathed/MathStream.h \
        mathed/MathSupport.h \
diff --git a/src/mathed/InsetMath.cpp b/src/mathed/InsetMath.cpp
index aec53d0..a8642c1 100644
--- a/src/mathed/InsetMath.cpp
+++ b/src/mathed/InsetMath.cpp
@@ -13,6 +13,7 @@
 
 #include "InsetMath.h"
 #include "MathData.h"
+#include "MathRow.h"
 #include "MathStream.h"
 
 #include "support/debug.h"
@@ -55,6 +56,16 @@ MathClass InsetMath::mathClass() const
 }
 
 
+bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo const &) const
+{
+       MathRow::Element e;
+       e.inset = this;
+       e.mclass = mathClass();
+       mrow.push_back(e);
+       return true;
+}
+
+
 void InsetMath::dump() const
 {
        lyxerr << "---------------------------------------------" << endl;
diff --git a/src/mathed/InsetMath.h b/src/mathed/InsetMath.h
index 0046218..3d3e1ef 100644
--- a/src/mathed/InsetMath.h
+++ b/src/mathed/InsetMath.h
@@ -54,7 +54,10 @@ inclusion in the "real LyX insets" FormulaInset and 
FormulaMacroInset.
 
 */
 
+class Cursor;
 class OutputParams;
+class MetricsInfo;
+
 class InsetMathArray;
 class InsetMathAMSArray;
 class InsetMathBrace;
@@ -73,7 +76,6 @@ class InsetMathSpace;
 class InsetMathSpecialChar;
 class InsetMathSymbol;
 class InsetMathUnknown;
-
 class InsetMathRef;
 
 class HtmlStream;
@@ -87,7 +89,7 @@ class WriteStream;
 
 class MathMacroTemplate;
 class MathMacro;
-class Cursor;
+class MathRow;
 class TextPainter;
 class TextMetricsInfo;
 class ReplaceData;
@@ -164,6 +166,8 @@ public:
 
        /// The class of the math object (used primarily for spacing)
        virtual MathClass mathClass() const;
+       /// Add this inset to a math row. Return true if contents got added
+       virtual bool addToMathRow(MathRow &, MetricsInfo const & mi) const;
 
        /// identifies things that can get scripts
        virtual bool isScriptable() const { return false; }
diff --git a/src/mathed/MathData.cpp b/src/mathed/MathData.cpp
index bd4d53e..8b7a89d 100644
--- a/src/mathed/MathData.cpp
+++ b/src/mathed/MathData.cpp
@@ -30,12 +30,11 @@
 
 #include "mathed/InsetMathUnknown.h"
 
-#include "support/debug.h"
-#include "support/docstream.h"
-
 #include "frontends/FontMetrics.h"
 #include "frontends/Painter.h"
 
+#include "support/debug.h"
+#include "support/docstream.h"
 #include "support/gettext.h"
 #include "support/lassert.h"
 #include "support/lyxalgo.h"
@@ -218,6 +217,34 @@ void MathData::touch() const
 }
 
 
+bool MathData::addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
+{
+       bool has_contents = false;
+       BufferView * bv = mi.base.bv;
+       MathData * ar = const_cast<MathData*>(this);
+       ar->updateMacros(&bv->cursor(), mi.macrocontext,
+                        InternalUpdate);
+
+       // FIXME: for completion, try to insert the relevant data in the
+       // mathrow (like is done for text rows). We could add a pair of
+       // InsetMathColor inset, but these come with extra spacing of
+       // their own.
+       DocIterator const & inlineCompletionPos = bv->inlineCompletionPos();
+       bool const has_completion = inlineCompletionPos.inMathed()
+               && &inlineCompletionPos.cell() == this;
+       size_t const compl_pos = has_completion ? inlineCompletionPos.pos() : 0;
+
+       for (size_t i = 0 ; i < size() ; ++i) {
+               has_contents |= (*this)[i]->addToMathRow(mrow, mi);
+               if (i + 1 == compl_pos) {
+                       mrow.back().compl_text = bv->inlineCompletion();
+                       mrow.back().compl_unique_to = 
bv->inlineCompletionUniqueChars();
+               }
+       }
+       return has_contents;
+}
+
+
 #if 0
 namespace {
 
@@ -247,7 +274,6 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) 
const
        mindes_ = (3 * xascent) / 4;
        slevel_ = (4 * xascent) / 5;
        sshift_ = xascent / 4;
-       kerning_ = 0;
 
        if (empty()) {
                // Cache the dimension.
@@ -255,45 +281,17 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) 
const
                return;
        }
 
-       Cursor & cur = mi.base.bv->cursor();
-       const_cast<MathData*>(this)->updateMacros(&cur, mi.macrocontext, 
InternalUpdate);
-
-       DocIterator const & inlineCompletionPos = 
mi.base.bv->inlineCompletionPos();
-       MathData const * inlineCompletionData = 0;
-       if (inlineCompletionPos.inMathed())
-               inlineCompletionData = &inlineCompletionPos.cell();
-
-       dim.asc = 0;
-       dim.wid = 0;
-       Dimension d;
-       CoordCache::Insets & coords = mi.base.bv->coordCache().insets();
-       for (pos_type i = 0, n = size(); i != n; ++i) {
-               MathAtom const & at = operator[](i);
-               at->metrics(mi, d);
-               coords.add(at.nucleus(), d);
-               dim += d;
-               if (i == n - 1)
-                       kerning_ = at->kerning(mi.base.bv);
-
-               // HACK to draw completion suggestion inline
-               if (inlineCompletionData != this
-                   || size_t(inlineCompletionPos.pos()) != i + 1)
-                       continue;
+       MathRow mrow(mi, this);
+       mrow_cache_[mi.base.bv] = mrow;
+       mrow.metrics(mi, dim);
+       kerning_ = mrow.kerning(mi.base.bv);
 
-               docstring const & completion = mi.base.bv->inlineCompletion();
-               if (completion.length() == 0)
-                       continue;
-
-               FontInfo font = mi.base.font;
-               augmentFont(font, "mathnormal");
-               dim.wid += mathed_string_width(font, completion);
-       }
        // Cache the dimension.
        mi.base.bv->coordCache().arrays().add(this, dim);
 }
 
 
-void MathData::draw(PainterInfo & pi, int x, int y) const
+void MathData::draw(PainterInfo & pi, int const x, int const y) const
 {
        //lyxerr << "MathData::draw: x: " << x << " y: " << y << endl;
        BufferView & bv  = *pi.base.bv;
@@ -313,48 +311,8 @@ void MathData::draw(PainterInfo & pi, int x, int y) const
                || x >= bv. workWidth())
                return;
 
-       DocIterator const & inlineCompletionPos = bv.inlineCompletionPos();
-       MathData const * inlineCompletionData = 0;
-       if (inlineCompletionPos.inMathed())
-               inlineCompletionData = &inlineCompletionPos.cell();
-
-       CoordCache::Insets & coords = pi.base.bv->coordCache().insets();
-       for (size_t i = 0, n = size(); i != n; ++i) {
-               MathAtom const & at = operator[](i);
-               coords.add(at.nucleus(), x, y);
-               at->drawSelection(pi, x, y);
-               at->draw(pi, x, y);
-               x += coords.dim(at.nucleus()).wid;
-
-               // Is the inline completion here?
-               if (inlineCompletionData != this
-                   || size_t(inlineCompletionPos.pos()) != i + 1)
-                       continue;
-               docstring const & completion = bv.inlineCompletion();
-               if (completion.length() == 0)
-                       continue;
-               FontInfo f = pi.base.font;
-               augmentFont(f, "mathnormal");
-
-               // draw the unique and the non-unique completion part
-               // Note: this is not time-critical as it is
-               // only done once per screen.
-               size_t uniqueTo = bv.inlineCompletionUniqueChars();
-               docstring s1 = completion.substr(0, uniqueTo);
-               docstring s2 = completion.substr(uniqueTo);
-
-               if (!s1.empty()) {
-                       f.setColor(Color_inlinecompletion);
-                       pi.pain.text(x, y, s1, f);
-                       x += mathed_string_width(f, s1);
-               }
-
-               if (!s2.empty()) {
-                       f.setColor(Color_nonunique_inlinecompletion);
-                       pi.pain.text(x, y, s2, f);
-                       x += mathed_string_width(f, s2);
-               }
-       }
+       MathRow const & mrow = mrow_cache_[pi.base.bv];
+       mrow.draw(pi, x, y);
 }
 
 
diff --git a/src/mathed/MathData.h b/src/mathed/MathData.h
index 4b79f80..fa82ee9 100644
--- a/src/mathed/MathData.h
+++ b/src/mathed/MathData.h
@@ -16,7 +16,9 @@
 #define MATH_DATA_H
 
 #include "Dimension.h"
+
 #include "MathAtom.h"
+#include "MathRow.h"
 
 #include "OutputEnums.h"
 
@@ -24,6 +26,7 @@
 
 #include <cstddef>
 #include <vector>
+#include <map>
 
 
 namespace lyx {
@@ -117,6 +120,10 @@ public:
        MathAtom & operator[](pos_type);
        /// checked read access
        MathAtom const & operator[](pos_type) const;
+
+       /// Add this array to a math row. Return true if contents got added
+       bool addToMathRow(MathRow &, MetricsInfo const & mi) const;
+
        /// rebuild cached metrics information
        void metrics(MetricsInfo & mi, Dimension & dim) const;
        ///
@@ -177,6 +184,9 @@ protected:
        mutable int kerning_;
        Buffer * buffer_;
 
+       /// cached object that describes typeset data
+       mutable std::map<BufferView*, MathRow> mrow_cache_;
+
 private:
        /// is this an exact match at this position?
        bool find1(MathData const & ar, size_type pos) const;
diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp
index 06a55bf..7656600 100644
--- a/src/mathed/MathMacro.cpp
+++ b/src/mathed/MathMacro.cpp
@@ -68,6 +68,27 @@ public:
        ///
        InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
        ///
+       bool addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
+       {
+               MathRow::Element e(MathRow::BEG_ARG);
+               e.macro = mathMacro_;
+               e.ar = &mathMacro_->cell(idx_);
+               mrow.push_back(e);
+
+               mathMacro_->macro()->unlock();
+               bool const has_contents = 
mathMacro_->cell(idx_).addToMathRow(mrow, mi);
+               mathMacro_->macro()->lock();
+
+               e.type = MathRow::END_ARG;
+               mrow.push_back(e);
+
+               if (has_contents)
+                       return true;
+               // if there was no contents, then we insert the empty macro 
inset
+               // instead.
+               return InsetMath::addToMathRow(mrow, mi);
+       }
+       ///
        void metrics(MetricsInfo & mi, Dimension & dim) const {
                mathMacro_->macro()->unlock();
                mathMacro_->cell(idx_).metrics(mi, dim);
@@ -266,6 +287,34 @@ MathMacro::~MathMacro()
 }
 
 
+bool MathMacro::addToMathRow(MathRow & mrow, MetricsInfo const & 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]) {
+               MathRow::Element e(MathRow::BEG_MACRO);
+               e.macro = this;
+               mrow.push_back(e);
+
+               d->macro_->lock();
+               bool const has_contents = d->expanded_.addToMathRow(mrow, mi);
+               d->macro_->unlock();
+
+               e.type = MathRow::END_MACRO;
+               mrow.push_back(e);
+
+               if (has_contents)
+                       return true;
+               // if there was no contents, then we insert the empty macro 
inset
+               // instead.
+       }
+       return InsetMath::addToMathRow(mrow, mi);
+}
+
+
 Inset * MathMacro::clone() const
 {
        MathMacro * copy = new MathMacro(*this);
@@ -344,7 +393,7 @@ bool MathMacro::editMode(BufferView const * bv) const {
 }
 
 
-MacroData const * MathMacro::macro()
+MacroData const * MathMacro::macro() const
 {
        return d->macro_;
 }
diff --git a/src/mathed/MathMacro.h b/src/mathed/MathMacro.h
index 1a8654a..9c6fdb1 100644
--- a/src/mathed/MathMacro.h
+++ b/src/mathed/MathMacro.h
@@ -37,6 +37,10 @@ public:
        ///
        virtual MathMacro const * asMacro() const { return this; }
        ///
+       /// If the macro is in normal edit mode, dissolve its contents in
+       /// the row. Otherwise, just insert the inset.
+       bool addToMathRow(MathRow &, MetricsInfo const & mi) const;
+       ///
        void draw(PainterInfo & pi, int x, int y) const;
        /// draw selection background
        void drawSelection(PainterInfo & pi, int x, int y) const;
@@ -45,6 +49,8 @@ public:
        { drawMarkers2(pi, x, y); }
        ///
        void metrics(MetricsInfo & mi, Dimension & dim) const;
+       /// was the macro in edit mode when computing metrics?
+       bool editMetrics(BufferView const * bv) const;
        ///
        int kerning(BufferView const * bv) const;
        /// get cursor position
@@ -117,6 +123,8 @@ public:
        ///
        docstring name() const;
        ///
+       MacroData const * macro() const;
+       ///
        docstring macroName() const;
        ///
        bool validName() const;
@@ -151,10 +159,6 @@ protected:
        /// attach arguments (maybe less than arity at the end of an MathData),
        /// including the optional ones (even if it can be empty here)
        void attachArguments(std::vector<MathData> const & args, size_t arity, 
int optionals);
-       ///
-       MacroData const * macro();
-       ///
-       bool editMetrics(BufferView const * bv) const;
 
 private:
        ///
diff --git a/src/mathed/MathRow.cpp b/src/mathed/MathRow.cpp
new file mode 100644
index 0000000..656b4e5
--- /dev/null
+++ b/src/mathed/MathRow.cpp
@@ -0,0 +1,286 @@
+/**
+ * \file MathRow.cpp
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Jean-Marc Lasgouttes
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#include <config.h>
+
+#include "MathRow.h"
+
+#include "InsetMath.h"
+#include "MathClass.h"
+#include "MathData.h"
+#include "MathMacro.h"
+#include "MathSupport.h"
+
+#include "BufferView.h"
+#include "CoordCache.h"
+#include "MetricsInfo.h"
+
+#include "frontends/Painter.h"
+
+#include "support/debug.h"
+#include "support/docstring.h"
+#include "support/lassert.h"
+
+#include <ostream>
+
+using namespace std;
+
+namespace lyx {
+
+
+MathRow::Element::Element(Type t, MathClass const mc)
+       : type(t),
+         inset(0), mclass(mc), before(0), after(0), compl_unique_to(0),
+         macro(0)
+{}
+
+
+MathRow::MathRow(MetricsInfo const & mi, MathData const * ar)
+{
+       if (ar->empty())
+               return;
+
+       // First there is a dummy element of type "open"
+       push_back(Element(BEGIN, MC_OPEN));
+
+       // Then insert the MathData argument
+       ar->addToMathRow(*this, mi);
+
+       // Finally there is a dummy element of type "close"
+       push_back(Element(END, MC_CLOSE));
+
+       // update classes
+       for (int i = 1 ; i != static_cast<int>(elements_.size()) - 1 ; ++i) {
+               if (elements_[i].type != INSET)
+                       continue;
+               update_class(elements_[i].mclass, elements_[before(i)].mclass,
+                            elements_[after(i)].mclass);
+       }
+
+       // set spacing
+       // We go to the end to handle spacing at the end of equation
+       for (int i = 1 ; i != static_cast<int>(elements_.size()) ; ++i) {
+               if (elements_[i].type != INSET)
+                       continue;
+               Element & bef = elements_[before(i)];
+               int spc = class_spacing(bef.mclass, elements_[i].mclass, 
mi.base);
+               bef.after = spc / 2;
+               // this is better than spc / 2 to avoid rounding problems
+               elements_[i].before = spc - spc / 2;
+       }
+       // Do not lose spacing allocated to extremities
+       if (!elements_.empty()) {
+               elements_[after(0)].before += elements_.front().after;
+               elements_[before(elements_.size() - 1)].after += 
elements_.back().before;
+       }
+}
+
+
+int MathRow::before(int i) const
+{
+       do
+               --i;
+       while (elements_[i].type != BEGIN
+                  && elements_[i].type != INSET);
+
+       return i;
+}
+
+
+int MathRow::after(int i) const
+{
+       do
+               ++i;
+       while (elements_[i].type != END
+                  && elements_[i].type != INSET);
+
+       return i;
+}
+
+
+void MathRow::metrics(MetricsInfo & mi, Dimension & dim) const
+{
+       dim.asc = 0;
+       dim.wid = 0;
+       // In order to compute the dimension of macros and their
+       // arguments, it is necessary to keep track of them.
+       map<MathMacro const *, Dimension> dim_macros;
+       map<MathData const *, Dimension> dim_arrays;
+       CoordCache & coords = mi.base.bv->coordCache();
+
+       for (Element const & e : elements_) {
+               Dimension d;
+               switch (e.type) {
+               case BEGIN:
+               case END:
+                       break;
+               case INSET:
+                       e.inset->metrics(mi, d);
+                       d.wid += e.before + e.after;
+                       coords.insets().add(e.inset, d);
+                       dim += d;
+                       // Now add the dimension to current macros and 
arguments.
+                       for (auto & dim_macro : dim_macros)
+                               dim_macro.second += d;
+                       for (auto & dim_array : dim_arrays)
+                               dim_array.second += d;
+                       break;
+               case BEG_MACRO:
+                       e.macro->macro()->lock();
+                       // Add a macro to current list
+                       dim_macros[e.macro] = Dimension();
+                       break;
+               case END_MACRO:
+                       LATTEST(dim_macros.find(e.macro) != dim_macros.end());
+                       e.macro->macro()->unlock();
+                       // Cache the dimension of the macro and remove it from
+                       // tracking map.
+                       coords.insets().add(e.macro, dim_macros[e.macro]);
+                       dim_macros.erase(e.macro);
+                       break;
+                       // This is basically like macros
+               case BEG_ARG:
+                       if (e.macro)
+                               e.macro->macro()->unlock();
+                       dim_arrays[e.ar] = Dimension();
+                       break;
+               case END_ARG:
+                       LATTEST(dim_arrays.find(e.ar) != dim_arrays.end());
+                       if (e.macro)
+                               e.macro->macro()->lock();
+                       coords.arrays().add(e.ar, dim_arrays[e.ar]);
+                       dim_arrays.erase(e.ar);
+                       break;
+               }
+
+               if (e.compl_text.empty())
+                       continue;
+               FontInfo font = mi.base.font;
+               augmentFont(font, "mathnormal");
+               dim.wid += mathed_string_width(font, e.compl_text);
+       }
+       LATTEST(dim_macros.empty() && dim_arrays.empty());
+}
+
+
+void MathRow::draw(PainterInfo & pi, int x, int const y) const
+{
+       CoordCache & coords = pi.base.bv->coordCache();
+       for (Element const & e : elements_) {
+               Dimension d;
+               switch (e.type) {
+               case INSET: {
+                       coords.insets().add(e.inset, x, y);
+                       // This is hackish: the math inset does not know that 
space
+                       // has been added before and after it; we alter its 
dimension
+                       // while it is drawing, because it relies on this value.
+                       Dimension const d = coords.insets().dim(e.inset);
+                       Dimension d2 = d;
+                       d2.wid -= e.before + e.after;
+                       coords.insets().add(e.inset, d2);
+                       e.inset->drawSelection(pi, x + e.before, y);
+                       e.inset->draw(pi, x + e.before, y);
+                       coords.insets().add(e.inset, d);
+                       x += d.wid;
+                       break;
+               }
+               case BEG_MACRO:
+                       coords.insets().add(e.macro, x, y);
+                       break;
+               case BEG_ARG:
+                       coords.arrays().add(e.ar, x, y);
+                       // if the macro is being edited, then the painter is in
+                       // monochrome mode.
+                       if (e.macro->editMetrics(pi.base.bv))
+                               pi.pain.leaveMonochromeMode();
+                       break;
+               case END_ARG:
+                       if (e.macro->editMetrics(pi.base.bv))
+                               pi.pain.enterMonochromeMode(Color_mathbg, 
Color_mathmacroblend);
+                       break;
+               case BEGIN:
+               case END:
+               case END_MACRO:
+                       break;
+               }
+
+               if (e.compl_text.empty())
+                       continue;
+               FontInfo f = pi.base.font;
+               augmentFont(f, "mathnormal");
+
+               // draw the unique and the non-unique completion part
+               // Note: this is not time-critical as it is
+               // only done once per screen.
+               docstring const s1 = e.compl_text.substr(0, e.compl_unique_to);
+               docstring const s2 = e.compl_text.substr(e.compl_unique_to);
+
+               if (!s1.empty()) {
+                       f.setColor(Color_inlinecompletion);
+                       pi.pain.text(x, y, s1, f);
+                       x += mathed_string_width(f, s1);
+               }
+               if (!s2.empty()) {
+                       f.setColor(Color_nonunique_inlinecompletion);
+                       pi.pain.text(x, y, s2, f);
+                       x += mathed_string_width(f, s2);
+               }
+       }
+}
+
+
+int MathRow::kerning(BufferView const * bv) const
+{
+       if (elements_.empty())
+               return 0;
+       InsetMath const * inset = elements_[before(elements_.size() - 1)].inset;
+       return inset ? inset->kerning(bv) : 0;
+}
+
+
+ostream & operator<<(ostream & os, MathRow::Element const & e)
+{
+       switch (e.type) {
+       case MathRow::BEGIN:
+               os << "{";
+               break;
+       case MathRow::END:
+               os << "}";
+               break;
+       case MathRow::INSET:
+               os << "<" << e.before << "-"
+                  << to_utf8(class_to_string(e.mclass))
+                  << "-" << e.after << ">";
+               break;
+       case MathRow::BEG_MACRO:
+               os << "\\" << to_utf8(e.macro->name()) << "[";
+               break;
+       case MathRow::END_MACRO:
+               os << "]";
+               break;
+       case MathRow::BEG_ARG:
+               os << "#(";
+               break;
+       case MathRow::END_ARG:
+               os << ")";
+               break;
+       }
+       return os;
+}
+
+
+ostream & operator<<(ostream & os, MathRow const & mrow)
+{
+       for (MathRow::Element const & e : mrow)
+               os << e << "  ";
+       return os;
+}
+
+} // namespace lyx
diff --git a/src/mathed/MathRow.h b/src/mathed/MathRow.h
new file mode 100644
index 0000000..d0260e2
--- /dev/null
+++ b/src/mathed/MathRow.h
@@ -0,0 +1,137 @@
+// -*- C++ -*-
+/**
+ * \file MathRow.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Jean-Marc Lasgouttes
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#ifndef MATH_ROW_H
+#define MATH_ROW_H
+
+#include "MathClass.h"
+
+#include "support/docstring.h"
+
+#include <vector>
+
+namespace lyx {
+
+class BufferView;
+class Dimension;
+class MetricsInfo;
+class PainterInfo;
+
+class InsetMath;
+class MathData;
+class MathMacro;
+
+/*
+ * While for editing purpose it is important that macros are counted
+ * as a single element, this is not the case for display. To get the
+ * spacing correct, it is necessary to dissolve all the macros that
+ * can be, along with their arguments. Then one obtains a
+ * representation of the MathData contents as a string of insets and
+ * then spacing can be done properly.
+ *
+ * This is the purpose of the MathRow class.
+ */
+class MathRow
+{
+public:
+       // What row elements can be
+       enum Type {
+               INSET, // this element is a plain inset
+               BEG_MACRO, // a macro begins here
+               END_MACRO, // a macro ends here
+               BEG_ARG, // a macro argument begins here
+               END_ARG, // a macro argument ends here
+               BEGIN, // dummy element before row
+               END, // dummy element after row
+       };
+
+       // An elements, together with its spacing
+       struct Element
+       {
+               ///
+               Element(Type t = INSET, MathClass const mc = MC_ORD);
+
+               /// Classifies the contents of the object
+               Type type;
+
+               /// When type is INSET
+               /// the math inset
+               InsetMath const * inset;
+               /// the class of the inset
+               MathClass mclass;
+               /// the spacing around the inset
+               int before, after;
+               // Non empty when there is a completion to draw
+               docstring compl_text;
+               // the number of characters forming the unique part.
+               size_t compl_unique_to;
+
+               /// When type is BEG_MACRO, END_MACRO, BEG_ARG, END_ARG
+               /// the math macro
+               MathMacro const * macro;
+
+               // type is BEG_ARG, END_ARG
+               MathData const * ar;
+       };
+
+       ///
+       MathRow() {};
+       ///
+       typedef std::vector<Element> Elements;
+       ///
+       typedef Elements::iterator iterator;
+       ///
+       typedef Elements::const_iterator const_iterator;
+       ///
+       iterator begin() { return elements_.begin(); }
+       ///
+       iterator end() { return elements_.end(); }
+       ///
+       const_iterator begin() const { return elements_.begin(); }
+       ///
+       const_iterator end() const { return elements_.end(); }
+       //
+       void push_back(Element const & e) { elements_.push_back(e); }
+       //
+       Element & back() { return elements_.back(); }
+
+       // create the math row by unwinding all macros in the MathData and
+       // compute the spacings.
+       MathRow(MetricsInfo const & mi, MathData const * ar);
+
+       //
+       void metrics(MetricsInfo & mi, Dimension & dim) const;
+       //
+       void draw(PainterInfo & pi, int const x, int const y) const;
+
+       /// superscript kerning
+       int kerning(BufferView const *) const;
+
+private:
+       // Index of the first inset element before position i
+       int before(int i) const;
+       // Index of the first inset element after position i
+       int after(int i) const;
+
+       ///
+       Elements elements_;
+};
+
+///
+std::ostream & operator<<(std::ostream & os, MathRow::Element const & elt);
+
+///
+std::ostream & operator<<(std::ostream & os, MathRow const & mrow);
+
+
+} // namespace lyx
+
+#endif

-----------------------------------------------------------------------

Summary of changes:
 src/mathed/InsetMath.cpp |    8 +-
 src/mathed/MathMacro.cpp |   45 +++++-----
 src/mathed/MathRow.cpp   |  211 +++++++++++++++++++++++-----------------------
 src/mathed/MathRow.h     |  119 +++++++++++++++-----------
 4 files changed, 201 insertions(+), 182 deletions(-)


hooks/post-receive
-- 
Repository for new features

Reply via email to