The branch, betterspacing, has been updated.
discards 9b04e46b777681faf1a71b4e660e82e8847e49a9 (commit)
discards e8983e3ba3fa4221194cc67281ceadc656cc3a4b (commit)
discards ddb7beab2d54cb053714b1c933c9b07f548dc299 (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 (9b04e46b777681faf1a71b4e660e82e8847e49a9)
\
N -- N -- N (f4ff11f82b7d8dd2162444560b105d90c928f138)
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 f4ff11f82b7d8dd2162444560b105d90c928f138
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 linearized (replaced with their contents) so that
all math insets are typeset as a string together.
To this end, we introduce a special iterator class which describes the
Mathdata 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 linearized. 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/MathData.cpp b/src/mathed/MathData.cpp
index bd4d53e..1e61570 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"
@@ -247,7 +246,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 +253,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;
-
- docstring const & completion = mi.base.bv->inlineCompletion();
- if (completion.length() == 0)
- continue;
+ MathRow mrow(mi, this);
+ mrow_cache_[mi.base.bv] = mrow;
+ mrow.metrics(mi, dim);
+ kerning_ = mrow.kerning(mi.base.bv);
- 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 +283,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..3c2493d 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 {
@@ -177,6 +180,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/MathRow.cpp b/src/mathed/MathRow.cpp
new file mode 100644
index 0000000..fe714f6
--- /dev/null
+++ b/src/mathed/MathRow.cpp
@@ -0,0 +1,511 @@
+/**
+ * \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 {
+
+namespace {
+
+/*
+ * The two classes below implement a trivial iterator that allows to
+ * handle macros in MathData. It may seem a lot of code for a single
+ * use, but actually it allows to simplify the MathRow stuff a lot.
+ * The important code is the ++ operator.
+ */
+
+struct MathDataSlice
+{
+ ///
+ MathDataSlice(MathData const * ar1)
+ : ar(ar1), pos(0), macro(0), has_contents(false),
in_argument(false) {}
+
+ /// The current math array
+ MathData const * ar;
+ /// The position in the array
+ size_t pos;
+ /// The macro in which this slice lives
+ MathMacro const * macro;
+ /// Whether some actual contents (inset) was found in this macro
+ bool has_contents;
+ /// Whether this slice is in a macro argument
+ bool in_argument;
+};
+
+
+class MathDataIterator : public vector<MathDataSlice>
+{
+public:
+ //
+ MathDataIterator(MetricsInfo const & mi, MathData const * ar)
+ : kind(MathAtomSpacing::BEGIN), mi_(mi), init_ar_(ar) {}
+
+ // the inset to which the iterator points
+ InsetMath const * inset() const {
+ return (*back().ar)[back().pos].nucleus();
+ }
+
+ bool at_slice_end() const {
+ return empty() || back().pos >= back().ar->size();
+ }
+
+ // Go to next position, stepping inside macros and their arguments
+ // if necessary. On return, the iterator kind describes the element
+ // it points to, and mas() will return a directly usable
+ // MathAtomSpacing object.
+ MathDataIterator & operator++();
+
+ // The spacing object to insert into the math row
+ MathAtomSpacing mas() const;
+
+ ///
+ MathAtomSpacing::Kind kind;
+
+private:
+ /// Return macro if im is a macro that can be unwound, 0 otherwise
+ MathMacro const * inlineMacro(InsetMath const * im) const
+ {
+ MathMacro const * mm = im->asMacro();
+ // The test corresponds to the case where we can do something.
+ bool const inlm = mm && !mm->editMetrics(mi_.base.bv)
+ && mm->displayMode() == MathMacro::DISPLAY_NORMAL;
+ return inlm ? mm : 0;
+ }
+ ///
+ bool isMacroArgument(InsetMath const * im)
+ {
+ return im->lyxCode() == ARGUMENT_PROXY_CODE;
+ }
+
+ ///
+ MetricsInfo const & mi_;
+ ///
+ MathData const * init_ar_;
+};
+
+
+MathDataIterator & MathDataIterator::operator++()
+{
+ switch (kind) {
+ case MathAtomSpacing::BEGIN: {
+ MathData * ar = const_cast<MathData*>(init_ar_);
+ ar->updateMacros(&mi_.base.bv->cursor(), mi_.macrocontext,
+ InternalUpdate);
+ push_back(MathDataSlice(ar));
+ break;
+ }
+ case MathAtomSpacing::END:
+ // already at end, nothing to do
+ return *this;
+ case MathAtomSpacing::INSET:
+ ++back().pos;
+ break;
+ case MathAtomSpacing::BEG_MACRO: {
+ MathMacro const * m = inlineMacro(inset());
+ MathData * ar = const_cast<MathData*>(&m->expanded());
+ ar->updateMacros(&mi_.base.bv->cursor(), mi_.macrocontext,
+ InternalUpdate);
+ push_back(MathDataSlice(ar));
+ m->macro()->lock();
+ back().macro = m;
+ break;
+ }
+ case MathAtomSpacing::END_MACRO:
+ back().macro->macro()->unlock();
+ // If macro is empty and it is in an editable string (either
+ // top-level or argument), then insert the macro inset in the
+ // string.
+ if (!back().has_contents
+ && (size() <= 2 || (*this)[size() - 2].in_argument)) {
+ // return the empty macro as an inset. It will be
+ // displayed with a blue box.
+ kind = MathAtomSpacing::INSET;
+ pop_back();
+ // early return (otherwise we would reopen the argument)
+ return *this;
+ } else {
+ pop_back();
+ if (!empty())
+ ++back().pos;
+ }
+ break;
+ case MathAtomSpacing::BEG_ARG: {
+ MathMacro const * m = back().macro;
+ push_back(MathDataSlice(&inset()->cell(0)));
+ back().macro = m;
+ back().in_argument = true;
+ break;
+ }
+ case MathAtomSpacing::END_ARG:
+ if (back().has_contents) {
+ pop_back();
+ ++back().pos;
+ } else {
+ // return the empty macro as an inset. It will be
+ // displayed with a blue box.
+ kind = MathAtomSpacing::INSET;
+ pop_back();
+ // early return (otherwise we would reopen the argument)
+ return *this;
+ }
+ break;
+ }
+
+ // Now set the current kind
+ if (empty())
+ kind = MathAtomSpacing::END;
+ else if (at_slice_end()) {
+ if (back().in_argument)
+ kind = MathAtomSpacing::END_ARG;
+ else if (back().macro)
+ kind = MathAtomSpacing::END_MACRO;
+ else {
+ // End of the data
+ kind = MathAtomSpacing::END;
+ pop_back();
+ }
+ } else {
+ // There is an inset at cursor. If it is a macro, one must do
+ // this before calling inlineMacro (see start of
+ // MathMacro::metrics).
+ if (inset()->asMacro())
+ inset()->asMacro()->setEditMetrics(mi_.base.bv);
+
+ if (inlineMacro(inset()))
+ kind = MathAtomSpacing::BEG_MACRO;
+ else if (isMacroArgument(inset()))
+ kind = MathAtomSpacing::BEG_ARG;
+ else {
+ kind = MathAtomSpacing::INSET;
+ back().has_contents = true;
+ }
+ }
+
+ return *this;
+}
+
+
+MathAtomSpacing MathDataIterator::mas() const
+{
+ MathAtomSpacing mas(kind);
+ switch (kind) {
+ case MathAtomSpacing::BEGIN:
+ case MathAtomSpacing::END:
+ // this is not supposed to happen
+ LATTEST(false);
+ break;
+ case MathAtomSpacing::INSET:
+ mas.inset = inset();
+ mas.mclass = mas.inset ? mas.inset->mathClass() : MC_UNKNOWN;
+ break;
+ case MathAtomSpacing::BEG_MACRO:
+ mas.macro = inlineMacro(inset());
+ break;
+ case MathAtomSpacing::END_MACRO:
+ mas.macro = back().macro;
+ break;
+ case MathAtomSpacing::BEG_ARG:
+ mas.macro = back().macro;
+ mas.ar = &inset()->cell(0);
+ break;
+ case MathAtomSpacing::END_ARG:
+ mas.macro = back().macro;
+ mas.ar = back().ar;
+ break;
+ }
+ return mas;
+}
+
+
+} // anonymous namespace
+
+
+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;
+
+ // 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 =
mi.base.bv->inlineCompletionPos();
+ bool const has_completion = inlineCompletionPos.inMathed()
+ && &inlineCompletionPos.cell() == ar;
+ size_t const compl_pos = has_completion ? inlineCompletionPos.pos() : 0;
+
+ // append to the MathRow vector all the math insets, alongside
+ // with indication of opening/closing of macros and macro
+ // arguments.
+ MathDataIterator it(mi, ar);
+ ++it;
+
+ // First there is a dummy element of type "open"
+ push_back(MathAtomSpacing(MathAtomSpacing::BEGIN, MC_OPEN));
+
+ while (it.kind != MathAtomSpacing::END) {
+ push_back(it.mas());
+ if (it.size() == 1 && compl_pos == it.back().pos + 1) {
+ back().compl_text = mi.base.bv->inlineCompletion();
+ back().compl_unique_to =
mi.base.bv->inlineCompletionUniqueChars();
+ }
+ ++it;
+ }
+
+ // 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..1c5dd78
--- /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 linearise 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
commit cf8283236b7cd8918d486df7dce001cdb56be5f6
Author: Jean-Marc Lasgouttes <[email protected]>
Date: Wed Sep 21 00:32:34 2016 +0200
Make some macro and macro argument data accessible
Some methods are made public, and ArgumentProxy has a cell method.
diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp
index 06a55bf..c70dc36 100644
--- a/src/mathed/MathMacro.cpp
+++ b/src/mathed/MathMacro.cpp
@@ -68,12 +68,18 @@ public:
///
InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
///
+ MathData const & cell(idx_type idx) const
+ {
+ LATTEST(idx == 0);
+ return mathMacro_->cell(idx_);
+ }
+ ///
void metrics(MetricsInfo & mi, Dimension & dim) const {
mathMacro_->macro()->unlock();
- mathMacro_->cell(idx_).metrics(mi, dim);
+ cell(0).metrics(mi, dim);
if (!mathMacro_->editMetrics(mi.base.bv)
- && mathMacro_->cell(idx_).empty())
+ && cell(0).empty())
def_.metrics(mi, dim);
mathMacro_->macro()->lock();
@@ -81,17 +87,17 @@ public:
// write(), normalize(), infoize() and infoize2() are not needed since
// MathMacro uses the definition and not the expanded cells.
///
- void maple(MapleStream & ms) const { ms << mathMacro_->cell(idx_); }
+ void maple(MapleStream & ms) const { ms << cell(0); }
///
- void maxima(MaximaStream & ms) const { ms << mathMacro_->cell(idx_); }
+ void maxima(MaximaStream & ms) const { ms << cell(0); }
///
- void mathematica(MathematicaStream & ms) const { ms <<
mathMacro_->cell(idx_); }
+ void mathematica(MathematicaStream & ms) const { ms << cell(0); }
///
- void mathmlize(MathStream & ms) const { ms << mathMacro_->cell(idx_); }
+ void mathmlize(MathStream & ms) const { ms << cell(0); }
///
- void htmlize(HtmlStream & ms) const { ms << mathMacro_->cell(idx_); }
+ void htmlize(HtmlStream & ms) const { ms << cell(0); }
///
- void octave(OctaveStream & os) const { os << mathMacro_->cell(idx_); }
+ void octave(OctaveStream & os) const { os << cell(0); }
///
void draw(PainterInfo & pi, int x, int y) const {
if (mathMacro_->editMetrics(pi.base.bv)) {
@@ -104,13 +110,13 @@ public:
// here (and the assert triggers in
pain.leaveMonochromeMode())
// it's a bug.
pi.pain.leaveMonochromeMode();
- mathMacro_->cell(idx_).draw(pi, x, y);
+ cell(0).draw(pi, x, y);
pi.pain.enterMonochromeMode(Color_mathbg,
Color_mathmacroblend);
- } else if (mathMacro_->cell(idx_).empty()) {
- mathMacro_->cell(idx_).setXY(*pi.base.bv, x, y);
+ } else if (cell(0).empty()) {
+ cell(0).setXY(*pi.base.bv, x, y);
def_.draw(pi, x, y);
} else
- mathMacro_->cell(idx_).draw(pi, x, y);
+ cell(0).draw(pi, x, y);
}
///
size_t idx() const { return idx_; }
@@ -118,8 +124,8 @@ public:
int kerning(BufferView const * bv) const
{
if (mathMacro_->editMetrics(bv)
- || !mathMacro_->cell(idx_).empty())
- return mathMacro_->cell(idx_).kerning(bv);
+ || !cell(0).empty())
+ return cell(0).kerning(bv);
else
return def_.kerning(bv);
}
@@ -266,6 +272,12 @@ MathMacro::~MathMacro()
}
+MathData const & MathMacro::expanded() const
+{
+ return d->expanded_;
+}
+
+
Inset * MathMacro::clone() const
{
MathMacro * copy = new MathMacro(*this);
@@ -344,7 +356,7 @@ bool MathMacro::editMode(BufferView const * bv) const {
}
-MacroData const * MathMacro::macro()
+MacroData const * MathMacro::macro() const
{
return d->macro_;
}
@@ -356,10 +368,16 @@ bool MathMacro::editMetrics(BufferView const * bv) const
}
+void MathMacro::setEditMetrics(BufferView const * bv) const
+{
+ d->editing_[bv] = editMode(bv);
+}
+
+
void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
{
// set edit mode for which we will have calculated metrics. But only
- d->editing_[mi.base.bv] = editMode(mi.base.bv);
+ setEditMetrics(mi.base.bv);
// calculate new metrics according to display mode
if (d->displayMode_ == DISPLAY_INIT || d->displayMode_ ==
DISPLAY_INTERACTIVE_INIT) {
diff --git a/src/mathed/MathMacro.h b/src/mathed/MathMacro.h
index 1a8654a..f08b254 100644
--- a/src/mathed/MathMacro.h
+++ b/src/mathed/MathMacro.h
@@ -37,6 +37,10 @@ public:
///
virtual MathMacro const * asMacro() const { return this; }
///
+ MacroData const * macro() const;
+ ///
+ MathData const & expanded() const;
+ ///
void draw(PainterInfo & pi, int x, int y) const;
/// draw selection background
void drawSelection(PainterInfo & pi, int x, int y) const;
@@ -45,8 +49,13 @@ public:
{ drawMarkers2(pi, x, y); }
///
void metrics(MetricsInfo & mi, Dimension & dim) const;
+ /// Was the macro in edit mode when metrics got calculated?
+ bool editMetrics(BufferView const * bv) const;
+ ///
+ void setEditMetrics(BufferView const * bv) const;
///
int kerning(BufferView const * bv) const;
+
/// get cursor position
void cursorPos(BufferView const & bv, CursorSlice const & sl,
bool boundary, int & x, int & y) const;
@@ -151,10 +160,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:
///
-----------------------------------------------------------------------
Summary of changes:
src/frontends/qt4/GuiPainter.cpp | 2 -
src/mathed/MathData.cpp | 2 -
src/mathed/MathMacro.cpp | 8 ++++++-
src/mathed/MathMacro.h | 8 ++++--
src/mathed/MathRow.cpp | 39 +++++++++++++++++++++++++------------
5 files changed, 38 insertions(+), 21 deletions(-)
hooks/post-receive
--
Repository for new features