The branch, betterspacing, has been updated.
  discards  1742d93a2c3b87b01bd05b650314aeb053a32fef (commit)
  discards  6f64cdbb07af94480c44b5f93fc660c502b38d1c (commit)
  discards  b9e63cdd55a5d4638e401195952a0cc713ffe70b (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 (1742d93a2c3b87b01bd05b650314aeb053a32fef)
            \
             N -- N -- N (18bb4ae1dfbdb8a3df60011dacb1b6f7f2b77a35)

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 18bb4ae1dfbdb8a3df60011dacb1b6f7f2b77a35
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Wed Oct 12 19:58:18 2016 +0200

    Hack: fix math class of InsetScript inset when cell 0 has one element
    
    A better fix should be found, but it would mean to dissolve the
    nucleus (cell 0) and then add the scripts, but with proper vertical
    settings.

diff --git a/src/mathed/InsetMathScript.cpp b/src/mathed/InsetMathScript.cpp
index b4cd590..37dcae3 100644
--- a/src/mathed/InsetMathScript.cpp
+++ b/src/mathed/InsetMathScript.cpp
@@ -273,6 +273,18 @@ int InsetMathScript::nker(BufferView const * bv) const
 }
 
 
+MathClass InsetMathScript::mathClass() const
+{
+       // FIXME: this is a hack, since the class will not be correct if
+       // cell 0 has several elements.
+       // The correct implementation would require to dissolve cell 0.
+       if (cell(0).empty())
+               return MC_ORD;
+       else
+               return cell(0)[0]->mathClass();
+}
+
+
 void InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const
 {
        Dimension dim0;
diff --git a/src/mathed/InsetMathScript.h b/src/mathed/InsetMathScript.h
index e84886b..d070743 100644
--- a/src/mathed/InsetMathScript.h
+++ b/src/mathed/InsetMathScript.h
@@ -33,6 +33,8 @@ public:
        ///
        mode_type currentMode() const { return MATH_MODE; }
        ///
+       MathClass mathClass() const;
+       ///
        void metrics(MetricsInfo & mi, Dimension & dim) const;
        ///
        void draw(PainterInfo & pi, int x, int y) const;

commit 341a6b899934002b755c6ce5937ef4900b831f7b
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Wed Oct 12 19:32:56 2016 +0200

    MathRow: Handle monochrome mode when drawing macro argument

diff --git a/src/mathed/MathMacro.h b/src/mathed/MathMacro.h
index 89df7c2..4eb93ba 100644
--- a/src/mathed/MathMacro.h
+++ b/src/mathed/MathMacro.h
@@ -49,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
@@ -157,8 +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);
-       ///
-       bool editMetrics(BufferView const * bv) const;
 
 private:
        ///
diff --git a/src/mathed/MathRow.cpp b/src/mathed/MathRow.cpp
index e3095a9..7024246 100644
--- a/src/mathed/MathRow.cpp
+++ b/src/mathed/MathRow.cpp
@@ -216,6 +216,14 @@ void MathRow::draw(PainterInfo & pi, int x, int const y) 
const
                        break;
                case MathAtomSpacing::BEG_ARG:
                        coords.arrays().add(mas.ar, x, y);
+                       // if the macro is being edited, then the painter is in
+                       // monochrome mode.
+                       if (mas.macro->editMetrics(pi.base.bv))
+                               pi.pain.leaveMonochromeMode();
+                       break;
+               case MathAtomSpacing::END_ARG:
+                       if (mas.macro->editMetrics(pi.base.bv))
+                               pi.pain.enterMonochromeMode(Color_mathbg, 
Color_mathmacroblend);
                        break;
                case MathAtomSpacing::BOX: {
                        d = theFontMetrics(pi.base.font).dimension('I');
@@ -227,7 +235,6 @@ void MathRow::draw(PainterInfo & pi, int x, int const y) 
const
                case MathAtomSpacing::BEGIN:
                case MathAtomSpacing::END:
                case MathAtomSpacing::END_MACRO:
-               case MathAtomSpacing::END_ARG:
                        break;
                }
 

commit e722866b05c8f3c8beb58d408b7101cff0a57b4f
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Wed Oct 5 22:14:48 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 MathAtomSpacing 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 23c9bd0..d2f73ad 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
 {
-       MathAtomSpacing mas;
+       MathAtomSpacing mas(MathAtomSpacing::INSET, mi);
        mas.inset = this;
        mas.mclass = mathClass();
        mrow.push_back(mas);
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 9cc6c79..ffe9506 100644
--- a/src/mathed/MathMacro.cpp
+++ b/src/mathed/MathMacro.cpp
@@ -68,28 +68,48 @@ public:
        ///
        InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
        ///
-       bool addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
-       {
-               MathAtomSpacing mas(MathAtomSpacing::BEG_ARG);
-               mas.macro = mathMacro_;
-               mas.ar = &mathMacro_->cell(idx_);
-               mrow.push_back(mas);
+       bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const {
+               // macro arguments are in macros
+               LATTEST(mi.macro_nesting > 0);
+               if (mi.macro_nesting == 1)
+                       mi.macro_nesting = 0;
+
+               MathAtomSpacing mas_beg(MathAtomSpacing::BEG_ARG, mi);
+               mas_beg.macro = mathMacro_;
+               mas_beg.ar = &mathMacro_->cell(idx_);
+               mrow.push_back(mas_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();
 
-               mas.kind = MathAtomSpacing::END_ARG;
-               mrow.push_back(mas);
+               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) {
+                       MathAtomSpacing mas(MathAtomSpacing::BOX, mi);
+                       mas.color = Color_mathline;
+                       mrow.push_back(mas);
+                       has_contents = true;
+               }
+
+               MathAtomSpacing mas_end(MathAtomSpacing::END_ARG, mi);
+               mas_end.macro = mathMacro_;
+               mas_end.ar = &mathMacro_->cell(idx_);
+
+               mrow.push_back(mas_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 +118,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 +309,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]) {
-               MathAtomSpacing mas(MathAtomSpacing::BEG_MACRO);
-               mas.macro = this;
-               mrow.push_back(mas);
+       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();
+       ++mi.macro_nesting;
 
-               mas.kind = MathAtomSpacing::END_MACRO;
-               mrow.push_back(mas);
+       MathAtomSpacing mas_beg(MathAtomSpacing::BEG_MACRO, mi);
+       mas_beg.macro = this;
+       mrow.push_back(mas_beg);
 
-               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) {
+               MathAtomSpacing mas(MathAtomSpacing::BOX, mi);
+               mas.color = Color_mathmacroblend;
+               mrow.push_back(mas);
+               has_contents = true;
        }
-       return InsetMath::addToMathRow(mrow, mi);
+
+       --mi.macro_nesting;
+
+       MathAtomSpacing mas_end(MathAtomSpacing::END_MACRO, mi);
+       mas_end.macro = this;
+       mrow.push_back(mas_end);
+
+       return has_contents;
 }
 
 
@@ -407,6 +440,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 +531,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 22268b3..89df7c2 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 d15eeca..e3095a9 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"
@@ -36,22 +37,33 @@ namespace lyx {
 
 
 MathAtomSpacing::MathAtomSpacing(Kind k, MathClass const mc)
-       : kind(k),
+       : kind(k), macro_nesting(0),
          inset(0), mclass(mc), before(0), after(0), compl_unique_to(0),
-         macro(0)
+         macro(0), color(Color_red)
 {}
 
 
-MathRow::MathRow(MetricsInfo const & mi, MathData const * ar)
-{
-       if (ar->empty())
-               return;
+MathAtomSpacing::MathAtomSpacing(Kind k, MetricsInfo &mi)
+       : kind(k), 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 & mi, MathData const * ar)
+{
        // First there is a dummy element of type "open"
        push_back(MathAtomSpacing(MathAtomSpacing::BEGIN, 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
+       if (!has_contents && mi.macro_nesting == 0) {
+               MathAtomSpacing mas(MathAtomSpacing::BOX, mi);
+               mas.color = Color_mathline;
+               push_back(mas);
+       }
 
        // Finally there is a dummy element of type "close"
        push_back(MathAtomSpacing(MathAtomSpacing::END, MC_CLOSE));
@@ -117,6 +129,7 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) 
const
 
        for (MathAtomSpacing const & mas : *this) {
                Dimension d;
+               mi.macro_nesting = mas.macro_nesting;
                switch (mas.kind) {
                case MathAtomSpacing::BEGIN:
                case MathAtomSpacing::END:
@@ -125,12 +138,6 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) 
const
                        mas.inset->metrics(mi, d);
                        d.wid += mas.before + mas.after;
                        coords.insets().add(mas.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 MathAtomSpacing::BEG_MACRO:
                        mas.macro->macro()->lock();
@@ -158,6 +165,19 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) 
const
                        coords.arrays().add(mas.ar, dim_arrays[mas.ar]);
                        dim_arrays.erase(mas.ar);
                        break;
+               case MathAtomSpacing::BOX:
+                       d = theFontMetrics(mi.base.font).dimension('I');
+                       d.wid += mas.before + mas.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 (mas.compl_text.empty())
@@ -197,6 +217,13 @@ void MathRow::draw(PainterInfo & pi, int x, int const y) 
const
                case MathAtomSpacing::BEG_ARG:
                        coords.arrays().add(mas.ar, x, y);
                        break;
+               case MathAtomSpacing::BOX: {
+                       d = theFontMetrics(pi.base.font).dimension('I');
+                       pi.pain.rectangle(x + mas.before, y - d.ascent(),
+                                         d.width(), d.height(), mas.color);
+                       x += d.wid;
+                       break;
+               }
                case MathAtomSpacing::BEGIN:
                case MathAtomSpacing::END:
                case MathAtomSpacing::END_MACRO:
@@ -264,6 +291,9 @@ ostream & operator<<(ostream & os, MathAtomSpacing const & 
mas)
        case MathAtomSpacing::END_ARG:
                os << ")";
                break;
+       case MathAtomSpacing::BOX:
+               os << "@";
+               break;
        }
        return os;
 }
diff --git a/src/mathed/MathRow.h b/src/mathed/MathRow.h
index 4b26d91..e191290 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>
@@ -40,13 +42,18 @@ struct MathAtomSpacing
                END_ARG, // a macro argument ends here
                BEGIN, // dummy element before row
                END, // dummy element after row
+               BOX // an empty box
        };
 
        ///
        MathAtomSpacing(Kind k = INSET, MathClass const mc = MC_ORD);
+       ///
+       MathAtomSpacing(Kind k, MetricsInfo & mi);
 
        /// Classifies the contents of the object
        Kind kind;
+       /// count wether the current mathdata is nested in macro(s)
+       int macro_nesting;
 
        /// When kind is INSET
        /// the math inset
@@ -66,6 +73,9 @@ struct MathAtomSpacing
 
        // kind is BEG_ARG, END_ARG
        MathData const * ar;
+
+       // kind is BOX
+       ColorCode color;
 };
 
 /*
@@ -85,7 +95,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 168ba261275c7b3ea99d413cc981a86334376b86
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 3e0bb2f442917db8de563506306e8dc796da14d4
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..23c9bd0 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
+{
+       MathAtomSpacing mas;
+       mas.inset = this;
+       mas.mclass = mathClass();
+       mrow.push_back(mas);
+       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..9cc6c79 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
+       {
+               MathAtomSpacing mas(MathAtomSpacing::BEG_ARG);
+               mas.macro = mathMacro_;
+               mas.ar = &mathMacro_->cell(idx_);
+               mrow.push_back(mas);
+
+               mathMacro_->macro()->unlock();
+               bool const has_contents = 
mathMacro_->cell(idx_).addToMathRow(mrow, mi);
+               mathMacro_->macro()->lock();
+
+               mas.kind = MathAtomSpacing::END_ARG;
+               mrow.push_back(mas);
+
+               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]) {
+               MathAtomSpacing mas(MathAtomSpacing::BEG_MACRO);
+               mas.macro = this;
+               mrow.push_back(mas);
+
+               d->macro_->lock();
+               bool const has_contents = d->expanded_.addToMathRow(mrow, mi);
+               d->macro_->unlock();
+
+               mas.kind = MathAtomSpacing::END_MACRO;
+               mrow.push_back(mas);
+
+               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..22268b3 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;
@@ -117,6 +121,8 @@ public:
        ///
        docstring name() const;
        ///
+       MacroData const * macro() const;
+       ///
        docstring macroName() const;
        ///
        bool validName() const;
@@ -152,8 +158,6 @@ protected:
        /// 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..d15eeca
--- /dev/null
+++ b/src/mathed/MathRow.cpp
@@ -0,0 +1,279 @@
+/**
+ * \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 {
+
+
+MathAtomSpacing::MathAtomSpacing(Kind k, MathClass const mc)
+       : kind(k),
+         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(MathAtomSpacing(MathAtomSpacing::BEGIN, MC_OPEN));
+
+       // Then insert the MathData argument
+       ar->addToMathRow(*this, mi);
+
+       // Finally there is a dummy element of type "close"
+       push_back(MathAtomSpacing(MathAtomSpacing::END, MC_CLOSE));
+
+       // update classes
+       for (int i = 1 ; i != static_cast<int>(size()) - 1 ; ++i) {
+               if ((*this)[i].kind != MathAtomSpacing::INSET)
+                       continue;
+               update_class((*this)[i].mclass, (*this)[before(i)].mclass,
+                            (*this)[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>(size()) ; ++i) {
+               if ((*this)[i].kind != MathAtomSpacing::INSET)
+                       continue;
+               MathAtomSpacing & bef = (*this)[before(i)];
+               int spc = class_spacing(bef.mclass, (*this)[i].mclass, mi.base);
+               bef.after = spc / 2;
+               // this is better than spc / 2 to avoid rounding problems
+               (*this)[i].before = spc - spc / 2;
+       }
+       // Do not lose spacing allocated to extremities
+       if (!empty()) {
+               (*this)[after(0)].before += front().after;
+               (*this)[before(size() - 1)].after += back().before;
+       }
+}
+
+
+int MathRow::before(int i) const
+{
+       do
+               --i;
+       while ((*this)[i].kind != MathAtomSpacing::BEGIN
+                  && (*this)[i].kind != MathAtomSpacing::INSET);
+
+       return i;
+}
+
+
+int MathRow::after(int i) const
+{
+       do
+               ++i;
+       while ((*this)[i].kind != MathAtomSpacing::END
+                  && (*this)[i].kind != MathAtomSpacing::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 (MathAtomSpacing const & mas : *this) {
+               Dimension d;
+               switch (mas.kind) {
+               case MathAtomSpacing::BEGIN:
+               case MathAtomSpacing::END:
+                       break;
+               case MathAtomSpacing::INSET:
+                       mas.inset->metrics(mi, d);
+                       d.wid += mas.before + mas.after;
+                       coords.insets().add(mas.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 MathAtomSpacing::BEG_MACRO:
+                       mas.macro->macro()->lock();
+                       // Add a macro to current list
+                       dim_macros[mas.macro] = Dimension();
+                       break;
+               case MathAtomSpacing::END_MACRO:
+                       LATTEST(dim_macros.find(mas.macro) != dim_macros.end());
+                       mas.macro->macro()->unlock();
+                       // Cache the dimension of the macro and remove it from
+                       // tracking map.
+                       coords.insets().add(mas.macro, dim_macros[mas.macro]);
+                       dim_macros.erase(mas.macro);
+                       break;
+                       // This is basically like macros
+               case MathAtomSpacing::BEG_ARG:
+                       if (mas.macro)
+                               mas.macro->macro()->unlock();
+                       dim_arrays[mas.ar] = Dimension();
+                       break;
+               case MathAtomSpacing::END_ARG:
+                       LATTEST(dim_arrays.find(mas.ar) != dim_arrays.end());
+                       if (mas.macro)
+                               mas.macro->macro()->lock();
+                       coords.arrays().add(mas.ar, dim_arrays[mas.ar]);
+                       dim_arrays.erase(mas.ar);
+                       break;
+               }
+
+               if (mas.compl_text.empty())
+                       continue;
+               FontInfo font = mi.base.font;
+               augmentFont(font, "mathnormal");
+               dim.wid += mathed_string_width(font, mas.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 (MathAtomSpacing const & mas : *this) {
+               Dimension d;
+               switch (mas.kind) {
+               case MathAtomSpacing::INSET: {
+                       coords.insets().add(mas.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(mas.inset);
+                       Dimension d2 = d;
+                       d2.wid -= mas.before + mas.after;
+                       coords.insets().add(mas.inset, d2);
+                       mas.inset->drawSelection(pi, x + mas.before, y);
+                       mas.inset->draw(pi, x + mas.before, y);
+                       coords.insets().add(mas.inset, d);
+                       x += d.wid;
+                       break;
+               }
+               case MathAtomSpacing::BEG_MACRO:
+                       coords.insets().add(mas.macro, x, y);
+                       break;
+               case MathAtomSpacing::BEG_ARG:
+                       coords.arrays().add(mas.ar, x, y);
+                       break;
+               case MathAtomSpacing::BEGIN:
+               case MathAtomSpacing::END:
+               case MathAtomSpacing::END_MACRO:
+               case MathAtomSpacing::END_ARG:
+                       break;
+               }
+
+               if (mas.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 = mas.compl_text.substr(0, 
mas.compl_unique_to);
+               docstring const s2 = mas.compl_text.substr(mas.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 (empty())
+               return 0;
+       InsetMath const * inset = (*this)[before(size() - 1)].inset;
+       return inset ? inset->kerning(bv) : 0;
+}
+
+
+ostream & operator<<(ostream & os, MathAtomSpacing const & mas)
+{
+       switch (mas.kind) {
+       case MathAtomSpacing::BEGIN:
+               os << "{";
+               break;
+       case MathAtomSpacing::END:
+               os << "}";
+               break;
+       case MathAtomSpacing::INSET:
+               os << "<" << mas.before << "-"
+                  << to_utf8(class_to_string(mas.mclass))
+                  << "-" << mas.after << ">";
+               break;
+       case MathAtomSpacing::BEG_MACRO:
+               os << "\\" << to_utf8(mas.macro->name()) << "[";
+               break;
+       case MathAtomSpacing::END_MACRO:
+               os << "]";
+               break;
+       case MathAtomSpacing::BEG_ARG:
+               os << "#(";
+               break;
+       case MathAtomSpacing::END_ARG:
+               os << ")";
+               break;
+       }
+       return os;
+}
+
+
+ostream & operator<<(ostream & os, MathRow const & mrow)
+{
+       for (MathAtomSpacing const & mas : mrow)
+               os << mas << "  ";
+       return os;
+}
+
+} // namespace lyx
diff --git a/src/mathed/MathRow.h b/src/mathed/MathRow.h
new file mode 100644
index 0000000..4b26d91
--- /dev/null
+++ b/src/mathed/MathRow.h
@@ -0,0 +1,114 @@
+// -*- 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;
+
+// A sequence of elements, together with their spacing
+struct MathAtomSpacing
+{
+       enum Kind {
+               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
+       };
+
+       ///
+       MathAtomSpacing(Kind k = INSET, MathClass const mc = MC_ORD);
+
+       /// Classifies the contents of the object
+       Kind kind;
+
+       /// When kind 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 kind is BEG_MACRO, END_MACRO, BEG_ARG, END_ARG
+       /// the math macro
+       MathMacro const * macro;
+
+       // kind is BEG_ARG, END_ARG
+       MathData const * ar;
+};
+
+/*
+ * 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 std::vector<MathAtomSpacing>
+{
+public:
+       MathRow() {};
+
+       // 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;
+};
+
+///
+std::ostream & operator<<(std::ostream & os, MathAtomSpacing const & mas);
+
+///
+std::ostream & operator<<(std::ostream & os, MathRow const & mrow);
+
+
+} // namespace lyx
+
+#endif

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

Summary of changes:
 lib/symbols                    |   28 ++++++------
 src/Dimension.h                |    2 +
 src/MetricsInfo.cpp            |    7 +--
 src/MetricsInfo.h              |    4 +-
 src/mathed/InsetMath.cpp       |    4 +-
 src/mathed/InsetMath.h         |    2 +-
 src/mathed/InsetMathScript.cpp |   12 +++++
 src/mathed/InsetMathScript.h   |    2 +
 src/mathed/MathData.cpp        |   14 +-----
 src/mathed/MathData.h          |    2 +-
 src/mathed/MathMacro.cpp       |   99 ++++++++++++++++++++++++++++------------
 src/mathed/MathMacro.h         |    6 +-
 src/mathed/MathRow.cpp         |   70 ++++++++++++++++++++++------
 src/mathed/MathRow.h           |   12 ++++-
 14 files changed, 178 insertions(+), 86 deletions(-)


hooks/post-receive
-- 
Repository for new features

Reply via email to