#include "MathRow.h"
-#include "InsetMath.h"
-#include "MathClass.h"
#include "MathData.h"
#include "MathSupport.h"
#include "BufferView.h"
+#include "ColorSet.h"
#include "CoordCache.h"
#include "MetricsInfo.h"
+#include "mathed/InsetMath.h"
+
#include "frontends/FontMetrics.h"
#include "frontends/Painter.h"
#include "support/docstring.h"
#include "support/lassert.h"
+#include <algorithm>
#include <ostream>
using namespace std;
MathRow::Element::Element(MetricsInfo const & mi, Type t, MathClass mc)
: type(t), mclass(mc), before(0), after(0), macro_nesting(mi.base.macro_nesting),
- marker(InsetMath::NO_MARKER), inset(0), compl_unique_to(0),
+ marker(marker_type::NO_MARKER), inset(nullptr), compl_unique_to(0), ar(nullptr),
color(Color_red)
{}
+namespace {
+
+// Helper functions for markers
+
+int markerMargin(MathRow::Element const & e)
+{
+ switch(e.marker) {
+ case marker_type::MARKER:
+ case marker_type::MARKER2:
+ case marker_type::BOX_MARKER:
+ return 2;
+ case marker_type::NO_MARKER:
+ return 0;
+ }
+ // should not happen
+ return 0;
+}
+
+
+void afterMetricsMarkers(MetricsInfo const & , MathRow::Element & e,
+ Dimension & dim)
+{
+ // handle vertical space for markers
+ switch(e.marker) {
+ case marker_type::NO_MARKER:
+ break;
+ case marker_type::MARKER:
+ ++dim.des;
+ break;
+ case marker_type::MARKER2:
+ ++dim.asc;
+ ++dim.des;
+ break;
+ case marker_type::BOX_MARKER:
+ FontInfo font;
+ font.setSize(TINY_SIZE);
+ Dimension namedim;
+ mathed_string_dim(font, e.inset->name(), namedim);
+ int const namewid = 1 + namedim.wid + 1;
+
+ if (namewid > dim.wid)
+ e.after += namewid - dim.wid;
+ ++dim.asc;
+ dim.des += 3 + namedim.height();
+ }
+}
+
+
+void drawMarkers(PainterInfo const & pi, MathRow::Element const & e,
+ int const x, int const y)
+{
+ if (e.marker == marker_type::NO_MARKER)
+ return;
+
+ CoordCache const & coords = pi.base.bv->coordCache();
+ Dimension const dim = coords.getInsets().dim(e.inset);
+
+ // the marker is before/after the inset. Necessary space has been reserved already.
+ int const l = x + e.before - (markerMargin(e) > 0 ? 1 : 0);
+ int const r = x + dim.width() - e.after;
+
+ // Grey lower box
+ if (e.marker == marker_type::BOX_MARKER) {
+ // draw header and rectangle around
+ FontInfo font;
+ font.setSize(TINY_SIZE);
+ font.setColor(Color_mathmacrolabel);
+ Dimension namedim;
+ mathed_string_dim(font, e.inset->name(), namedim);
+ pi.pain.fillRectangle(l, y + dim.des - namedim.height() - 2,
+ dim.wid, namedim.height() + 2, Color_mathmacrobg);
+ pi.pain.text(l, y + dim.des - namedim.des - 1, e.inset->name(), font);
+ }
+
+ // Color for corners
+ bool const highlight = e.inset->mouseHovered(pi.base.bv)
+ || e.inset->editing(pi.base.bv);
+ ColorCode const pen_color = highlight ? Color_mathframe : Color_mathcorners;
+ // If the corners have the same color as the background, do not paint them.
+ if (lcolor.getX11HexName(Color_mathbg) == lcolor.getX11HexName(pen_color))
+ return;
+
+ // Lower corners in all cases
+ int const d = y + dim.descent();
+ pi.pain.line(l, d - 3, l, d, pen_color);
+ pi.pain.line(r, d - 3, r, d, pen_color);
+ pi.pain.line(l, d, l + 3, d, pen_color);
+ pi.pain.line(r - 3, d, r, d, pen_color);
+
+ // Upper corners
+ if (e.marker == marker_type::BOX_MARKER
+ || e.marker == marker_type::MARKER2) {
+ int const a = y - dim.ascent();
+ pi.pain.line(l, a + 3, l, a, pen_color);
+ pi.pain.line(r, a + 3, r, a, pen_color);
+ pi.pain.line(l, a, l + 3, a, pen_color);
+ pi.pain.line(r - 3, a, r, a, pen_color);
+ }
+}
+
+} // namespace
+
+
MathRow::MathRow(MetricsInfo & mi, MathData const * ar)
{
// First there is a dummy element of type "open"
for (int i = 1 ; i != static_cast<int>(elements_.size()) ; ++i) {
Element & e = elements_[i];
- if (e.mclass == MC_UNKNOWN)
- continue;
-
Element & bef = elements_[before(i)];
- if (dospacing) {
+ if (dospacing && e.mclass != MC_UNKNOWN) {
int spc = class_spacing(bef.mclass, e.mclass, mi.base);
bef.after += spc / 2;
// this is better than spc / 2 to avoid rounding problems
}
// finally reserve space for markers
- if (bef.marker != Inset::NO_MARKER)
- bef.after = max(bef.after, 1);
- if (e.marker != Inset::NO_MARKER)
- e.before = max(e.before, 1);
+ bef.after = max(bef.after, markerMargin(bef));
+ if (e.mclass != MC_UNKNOWN)
+ e.before = max(e.before, markerMargin(e));
+ // for linearized insets (macros...) too
+ if (e.type == BEGIN)
+ bef.after = max(bef.after, markerMargin(e));
+ if (e.type == END && e.marker != marker_type::NO_MARKER) {
+ Element & aft = elements_[after(i)];
+ aft.before = max(aft.before, markerMargin(e));
+ }
}
// Do not lose spacing allocated to extremities
}
-void MathRow::metrics(MetricsInfo & mi, Dimension & dim) const
+void MathRow::metrics(MetricsInfo & mi, Dimension & dim)
{
- 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<InsetMath const *, Dimension> dim_insets;
- map<MathData const *, Dimension> dim_arrays;
+ vector<pair<InsetMath const *, Dimension>> dim_insets;
+ vector<pair<MathData const *, Dimension>> dim_arrays;
CoordCache & coords = mi.base.bv->coordCache();
- for (Element const & e : elements_) {
+ for (Element & e : elements_) {
mi.base.macro_nesting = e.macro_nesting;
Dimension d;
switch (e.type) {
d.wid += e.before + e.after;
coords.insets().add(e.inset, d);
break;
- case BEG_MACRO:
- e.inset->beforeMetrics();
- // Add a macro to current list
- dim_insets[e.inset] = Dimension();
- break;
- case END_MACRO:
- LATTEST(dim_insets.find(e.inset) != dim_insets.end());
- e.inset->afterMetrics();
- // Cache the dimension of the macro and remove it from
- // tracking map.
- coords.insets().add(e.inset, dim_insets[e.inset]);
- dim_insets.erase(e.inset);
- break;
- // This is basically like macros
- case BEG_ARG:
- e.inset->beforeMetrics();
- dim_arrays[e.ar] = Dimension();
+ case BEGIN:
+ if (e.inset) {
+ dim_insets.push_back(make_pair(e.inset, Dimension()));
+ dim_insets.back().second.wid += e.before + e.after;
+ d.wid = e.before + e.after;
+ e.inset->beforeMetrics();
+ }
+ if (e.ar)
+ dim_arrays.push_back(make_pair(e.ar, Dimension()));
break;
- case END_ARG:
- LATTEST(dim_arrays.find(e.ar) != dim_arrays.end());
- e.inset->afterMetrics();
- coords.arrays().add(e.ar, dim_arrays[e.ar]);
- dim_arrays.erase(e.ar);
+ case END:
+ if (e.inset) {
+ e.inset->afterMetrics();
+ LATTEST(dim_insets.back().first == e.inset);
+ d = dim_insets.back().second;
+ afterMetricsMarkers(mi, e, d);
+ d.wid += e.before + e.after;
+ coords.insets().add(e.inset, d);
+ dim_insets.pop_back();
+ // We do not want to count the width again, but the
+ // padding and the vertical dimension are meaningful.
+ d.wid = e.before + e.after;
+ }
+ if (e.ar) {
+ LATTEST(dim_arrays.back().first == e.ar);
+ coords.arrays().add(e.ar, dim_arrays.back().second);
+ dim_arrays.pop_back();
+ }
break;
case BOX:
d = theFontMetrics(mi.base.font).dimension('I');
// allow for one pixel before/after the box.
d.wid += e.before + e.after + 2;
} else {
- // hide the box, but give it some height
+ // hide the box, but keep its height
d.wid = 0;
}
break;
}
- // handle vertical space for markers
- switch(e.marker) {
- case InsetMath::NO_MARKER:
- break;
- case InsetMath::MARKER:
- ++d.des;
- break;
- case InsetMath::MARKER2:
- ++d.asc;
- ++d.des;
- }
-
if (!d.empty()) {
dim += d;
// Now add the dimension to current macros and arguments.
}
-namespace {
-
-void drawMarkers(PainterInfo const & pi, MathRow::Element const & e, int const x, int const y)
-{
- if (e.marker == InsetMath::NO_MARKER)
- return;
-
- CoordCache const & coords = pi.base.bv->coordCache();
- Dimension const dim = coords.getInsets().dim(e.inset);
-
- // the marker is before/after the inset. Normally some space has been reserved already.
- int const l = x + e.before - 1;
- int const r = x + dim.width() - e.after;
-
- // Duplicated from Inset.cpp and adapted. It is believed that the
- // Inset version should die eventually
- ColorCode pen_color = e.inset->mouseHovered(pi.base.bv) || e.inset->editing(pi.base.bv)?
- Color_mathframe : Color_mathcorners;
-
- int const d = y + dim.descent();
- pi.pain.line(l, d - 3, l, d, pen_color);
- pi.pain.line(r, d - 3, r, d, pen_color);
- pi.pain.line(l, d, l + 3, d, pen_color);
- pi.pain.line(r - 3, d, r, d, pen_color);
-
- if (e.marker == InsetMath::MARKER)
- return;
-
- int const a = y - dim.ascent();
- pi.pain.line(l, a + 3, l, a, pen_color);
- pi.pain.line(r, a + 3, r, a, pen_color);
- pi.pain.line(l, a, l + 3, a, pen_color);
- pi.pain.line(r - 3, a, r, a, pen_color);
-}
-
-}
-
void MathRow::draw(PainterInfo & pi, int x, int const y) const
{
CoordCache & coords = pi.base.bv->coordCache();
// This is hackish: the math inset does not know that space
// has been added before and after it; we alter its dimension
// while it is drawing, because it relies on this value.
- Dimension const d = coords.insets().dim(e.inset);
- Dimension d2 = d;
- d2.wid -= e.before + e.after;
- coords.insets().add(e.inset, d2);
- e.inset->drawSelection(pi, x + e.before, y);
+ Geometry & g = coords.insets().geometry(e.inset);
+ g.dim.wid -= e.before + e.after;
+ if (pi.pain.develMode() && !e.inset->isBufferValid())
+ pi.pain.fillRectangle(x + e.before, y - g.dim.ascent(),
+ g.dim.width(), g.dim.height(), Color_error);
e.inset->draw(pi, x + e.before, y);
- coords.insets().add(e.inset, x, y);
- coords.insets().add(e.inset, d);
+ g.pos = {x, y};
+ g.dim.wid += e.before + e.after;
drawMarkers(pi, e, x, y);
- x += d.wid;
+ x += g.dim.wid;
break;
}
- case BEG_MACRO:
- coords.insets().add(e.inset, x, y);
- drawMarkers(pi, e, x, y);
- e.inset->beforeDraw(pi);
- break;
- case END_MACRO:
- e.inset->afterDraw(pi);
- break;
- case BEG_ARG:
- coords.arrays().add(e.ar, x, y);
- e.inset->beforeDraw(pi);
+ case BEGIN:
+ if (e.ar) {
+ coords.arrays().add(e.ar, x, y);
+ e.ar->drawSelection(pi, x, y);
+ }
+ if (e.inset) {
+ coords.insets().add(e.inset, x, y);
+ drawMarkers(pi, e, x, y);
+ e.inset->beforeDraw(pi);
+ }
+ x += e.before + e.after;
break;
- case END_ARG:
- e.inset->afterDraw(pi);
+ case END:
+ if (e.inset)
+ e.inset->afterDraw(pi);
+ x += e.before + e.after;
break;
case BOX: {
if (e.color == Color_none)
if (!s1.empty()) {
f.setColor(Color_inlinecompletion);
- pi.pain.text(x, y, s1, f);
+ // offset the text by e.after to make sure that the
+ // spacing is after the completion, not before.
+ pi.pain.text(x - e.after, y, s1, f);
x += mathed_string_width(f, s1);
}
if (!s2.empty()) {
f.setColor(Color_nonunique_inlinecompletion);
- pi.pain.text(x, y, s2, f);
+ pi.pain.text(x - e.after, y, s2, f);
x += mathed_string_width(f, s2);
}
}
<< to_utf8(class_to_string(e.mclass))
<< "-" << e.after << ">";
break;
- case MathRow::BEG_MACRO:
- os << "\\" << to_utf8(e.inset->name())
- << "^" << e.macro_nesting << "[";
- break;
- case MathRow::END_MACRO:
- os << "]";
- break;
- case MathRow::BEG_ARG:
- os << "#(";
+ case MathRow::BEGIN:
+ if (e.inset)
+ os << "\\" << to_utf8(e.inset->name())
+ << "^" << e.macro_nesting << "[";
+ if (e.ar)
+ os << "(";
break;
- case MathRow::END_ARG:
- os << ")";
+ case MathRow::END:
+ if (e.ar)
+ os << ")";
+ if (e.inset)
+ os << "]";
break;
case MathRow::BOX:
os << "<" << e.before << "-[]-" << e.after << ">";