#include "CoordCache.h"
#include "MetricsInfo.h"
+#include "frontends/FontMetrics.h"
#include "frontends/Painter.h"
#include "support/debug.h"
namespace lyx {
-MathRow::Element::Element(Type t, MathClass const mc)
- : type(t),
- inset(0), mclass(mc), before(0), after(0), compl_unique_to(0),
- macro(0)
+MathRow::Element::Element(Type t, MathClass mc)
+ : type(t), mclass(mc), before(0), after(0), inset(0),
+ compl_unique_to(0), macro(0), color(Color_red)
{}
-MathRow::MathRow(MetricsInfo const & mi, MathData const * ar)
+MathRow::MathRow(MetricsInfo & mi, MathData const * ar)
{
- if (ar->empty())
- return;
-
// First there is a dummy element of type "open"
- push_back(Element(BEGIN, MC_OPEN));
+ push_back(Element(DUMMY, MC_OPEN));
// Then insert the MathData argument
- ar->addToMathRow(*this, mi);
+ bool const has_contents = ar->addToMathRow(*this, mi);
+
+ // empty arrays are visible when they are editable
+ // we reserve the necessary space anyway (even if nothing gets drawn)
+ if (!has_contents) {
+ Element e(BOX, MC_ORD);
+ e.color = mi.base.macro_nesting == 0 ? Color_mathline : Color_none;
+ push_back(e);
+ }
// Finally there is a dummy element of type "close"
- push_back(Element(END, MC_CLOSE));
+ push_back(Element(DUMMY, MC_CLOSE));
/* Do spacing only in math mode. This test is a bit clumsy,
* but it is used in other places for guessing the current mode.
// update classes
for (int i = 1 ; i != static_cast<int>(elements_.size()) - 1 ; ++i) {
- if (elements_[i].type != INSET)
+ if (elements_[i].mclass == MC_UNKNOWN)
continue;
update_class(elements_[i].mclass, elements_[before(i)].mclass,
elements_[after(i)].mclass);
// set spacing
// We go to the end to handle spacing at the end of equation
for (int i = 1 ; i != static_cast<int>(elements_.size()) ; ++i) {
- if (elements_[i].type != INSET)
+ if (elements_[i].mclass == MC_UNKNOWN)
continue;
Element & bef = elements_[before(i)];
int spc = class_spacing(bef.mclass, elements_[i].mclass, mi.base);
{
do
--i;
- while (elements_[i].type != BEGIN
- && elements_[i].type != INSET);
+ while (elements_[i].mclass == MC_UNKNOWN);
return i;
}
{
do
++i;
- while (elements_[i].type != END
- && elements_[i].type != INSET);
+ while (elements_[i].mclass == MC_UNKNOWN);
return i;
}
// arguments, it is necessary to keep track of them.
map<MathMacro const *, Dimension> dim_macros;
map<MathData const *, Dimension> dim_arrays;
+ // this vector remembers the stack of macro nesting values
+ vector<int> macro_nesting;
+ macro_nesting.push_back(mi.base.macro_nesting);
CoordCache & coords = mi.base.bv->coordCache();
-
for (Element const & e : elements_) {
+ mi.base.macro_nesting = macro_nesting.back();
Dimension d;
switch (e.type) {
- case BEGIN:
- case END:
+ case DUMMY:
break;
case INSET:
e.inset->metrics(mi, d);
d.wid += e.before + e.after;
coords.insets().add(e.inset, d);
- dim += d;
- // Now add the dimension to current macros and arguments.
- for (auto & dim_macro : dim_macros)
- dim_macro.second += d;
- for (auto & dim_array : dim_arrays)
- dim_array.second += d;
break;
case BEG_MACRO:
+ macro_nesting.push_back(e.macro->nesting());
e.macro->macro()->lock();
// Add a macro to current list
dim_macros[e.macro] = Dimension();
break;
case END_MACRO:
LATTEST(dim_macros.find(e.macro) != dim_macros.end());
+ macro_nesting.pop_back();
e.macro->macro()->unlock();
// Cache the dimension of the macro and remove it from
// tracking map.
break;
// This is basically like macros
case BEG_ARG:
- if (e.macro)
+ if (e.macro) {
+ macro_nesting.push_back(e.macro->nesting());
e.macro->macro()->unlock();
+ }
dim_arrays[e.ar] = Dimension();
break;
case END_ARG:
LATTEST(dim_arrays.find(e.ar) != dim_arrays.end());
- if (e.macro)
+ if (e.macro) {
+ macro_nesting.pop_back();
e.macro->macro()->lock();
+ }
coords.arrays().add(e.ar, dim_arrays[e.ar]);
dim_arrays.erase(e.ar);
break;
+ case BOX:
+ d = theFontMetrics(mi.base.font).dimension('I');
+ d.wid += e.before + e.after;
+ break;
+ }
+
+ if (!d.empty()) {
+ dim += d;
+ // Now add the dimension to current macros and arguments.
+ for (auto & dim_macro : dim_macros)
+ dim_macro.second += d;
+ for (auto & dim_array : dim_arrays)
+ dim_array.second += d;
}
if (e.compl_text.empty())
{
CoordCache & coords = pi.base.bv->coordCache();
for (Element const & e : elements_) {
- Dimension d;
switch (e.type) {
case INSET: {
// This is hackish: the math inset does not know that space
if (e.macro->editMetrics(pi.base.bv))
pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
break;
- case BEGIN:
- case END:
+ case BOX: {
+ Dimension const d = theFontMetrics(pi.base.font).dimension('I');
+ // the box is not visible in non-editable context (except for grey macro boxes).
+ if (e.color != Color_none)
+ pi.pain.rectangle(x + e.before, y - d.ascent(),
+ d.width(), d.height(), e.color);
+ x += d.wid + e.before + e.after;
+ break;
+ }
+ case DUMMY:
case END_MACRO:
break;
}
ostream & operator<<(ostream & os, MathRow::Element const & e)
{
switch (e.type) {
- case MathRow::BEGIN:
- os << "{";
- break;
- case MathRow::END:
- os << "}";
+ case MathRow::DUMMY:
+ os << (e.mclass == MC_OPEN ? "{" : "}");
break;
case MathRow::INSET:
os << "<" << e.before << "-"
<< "-" << e.after << ">";
break;
case MathRow::BEG_MACRO:
- os << "\\" << to_utf8(e.macro->name()) << "[";
+ os << "\\" << to_utf8(e.macro->name())
+ << "^" << e.macro->nesting() << "[";
break;
case MathRow::END_MACRO:
os << "]";
case MathRow::END_ARG:
os << ")";
break;
+ case MathRow::BOX:
+ os << "<" << e.before << "-[]-" << e.after << ">";
+ break;
}
return os;
}