The branch, betterspacing, has been updated.
  discards  f4ff11f82b7d8dd2162444560b105d90c928f138 (commit)
  discards  cf8283236b7cd8918d486df7dce001cdb56be5f6 (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 (f4ff11f82b7d8dd2162444560b105d90c928f138)
            \
             N -- N -- N (b9e63cdd55a5d4638e401195952a0cc713ffe70b)

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 b9e63cdd55a5d4638e401195952a0cc713ffe70b
Author: Jean-Marc Lasgouttes <[email protected]>
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/MetricsInfo.cpp b/src/MetricsInfo.cpp
index 589e924..38f832c 100644
--- a/src/MetricsInfo.cpp
+++ b/src/MetricsInfo.cpp
@@ -37,8 +37,9 @@ namespace lyx {
 /////////////////////////////////////////////////////////////////////////
 
 MetricsBase::MetricsBase(BufferView * b, FontInfo f, int w)
-       : bv(b), font(move(f)), style(LM_ST_TEXT), fontname("mathnormal"),
-         textwidth(w), solid_line_thickness_(1), solid_line_offset_(1),
+       : bv(b), font(move(f)), style(LM_ST_TEXT), editable(true),
+         fontname("mathnormal"), textwidth(w),
+         solid_line_thickness_(1), solid_line_offset_(1),
          dotted_line_thickness_(1)
 {
        if (lyxrc.zoom >= 200) {
diff --git a/src/MetricsInfo.h b/src/MetricsInfo.h
index d2dd8b7..fc24d2e 100644
--- a/src/MetricsInfo.h
+++ b/src/MetricsInfo.h
@@ -61,6 +61,8 @@ public:
        FontInfo font;
        /// current math style (display/text/script/..)
        Styles style;
+       /// is current MathData editable (that is, not the contents of a macro)
+       bool editable;
        /// name of current font - mathed specific
        std::string fontname;
        /// This is the width available in pixels
diff --git a/src/mathed/InsetMath.cpp b/src/mathed/InsetMath.cpp
index aec53d0..ab03f85 100644
--- a/src/mathed/InsetMath.cpp
+++ b/src/mathed/InsetMath.cpp
@@ -13,6 +13,7 @@
 
 #include "InsetMath.h"
 #include "MathData.h"
+#include "MathRow.cpp"
 #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..ecfbdbc
--- /dev/null
+++ b/src/mathed/MathRow.cpp
@@ -0,0 +1,276 @@
+/**
+ * \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
+{
+       return empty() ? 0 : (*this)[before(size() - 1)].inset->kerning(bv);
+}
+
+
+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:
 src/MetricsInfo.cpp      |    5 +-
 src/MetricsInfo.h        |    2 +
 src/mathed/InsetMath.cpp |   11 ++
 src/mathed/InsetMath.h   |    8 +-
 src/mathed/MathData.cpp  |   28 ++++++
 src/mathed/MathData.h    |    4 +
 src/mathed/MathMacro.cpp |   83 +++++++++++-----
 src/mathed/MathMacro.h   |   15 ++--
 src/mathed/MathRow.cpp   |  239 +---------------------------------------------
 src/mathed/MathRow.h     |    2 +-
 10 files changed, 121 insertions(+), 276 deletions(-)


hooks/post-receive
-- 
Repository for new features

Reply via email to