mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-12-22 13:18:28 +00:00
Set correctly the spacing between atoms in MathData
* new MathRow class which contains the description of a MathData object in terms of math class and spacing + 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 method addToMathRow to InsetMath and MathData. This method allows to linearize recursively a MathData object. + It is then necessary to set manually the dimension and position of the macros and arguments. + the class class and spacing are computed using the MathClass helpers. The MathRow data is cached in the MathData object in a bufferview-dependent way (different dpi for different screens). * delegate most of the work MathData::metrics/draw to MathRow metrics/draw. The case of draw is trickier, since many draw() methods rely on their metrics without any spacing added.
This commit is contained in:
parent
f6df4e7985
commit
bf56e2c8e1
@ -458,6 +458,7 @@ SOURCEFILESMATHED = \
|
|||||||
mathed/MathMacroArgument.cpp \
|
mathed/MathMacroArgument.cpp \
|
||||||
mathed/MathMacroTemplate.cpp \
|
mathed/MathMacroTemplate.cpp \
|
||||||
mathed/MathParser.cpp \
|
mathed/MathParser.cpp \
|
||||||
|
mathed/MathRow.cpp \
|
||||||
mathed/MathStream.cpp \
|
mathed/MathStream.cpp \
|
||||||
mathed/MathSupport.cpp \
|
mathed/MathSupport.cpp \
|
||||||
mathed/TextPainter.cpp
|
mathed/TextPainter.cpp
|
||||||
@ -530,6 +531,7 @@ HEADERFILESMATHED = \
|
|||||||
mathed/MathMacroTemplate.h \
|
mathed/MathMacroTemplate.h \
|
||||||
mathed/MathParser.h \
|
mathed/MathParser.h \
|
||||||
mathed/MathParser_flags.h \
|
mathed/MathParser_flags.h \
|
||||||
|
mathed/MathRow.h \
|
||||||
mathed/ReplaceData.h \
|
mathed/ReplaceData.h \
|
||||||
mathed/MathStream.h \
|
mathed/MathStream.h \
|
||||||
mathed/MathSupport.h \
|
mathed/MathSupport.h \
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include "InsetMath.h"
|
#include "InsetMath.h"
|
||||||
#include "MathData.h"
|
#include "MathData.h"
|
||||||
|
#include "MathRow.h"
|
||||||
#include "MathStream.h"
|
#include "MathStream.h"
|
||||||
|
|
||||||
#include "support/debug.h"
|
#include "support/debug.h"
|
||||||
@ -55,6 +56,16 @@ MathClass InsetMath::mathClass() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo const &) const
|
||||||
|
{
|
||||||
|
MathRow::Element e;
|
||||||
|
e.inset = this;
|
||||||
|
e.mclass = mathClass();
|
||||||
|
mrow.push_back(e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void InsetMath::dump() const
|
void InsetMath::dump() const
|
||||||
{
|
{
|
||||||
lyxerr << "---------------------------------------------" << endl;
|
lyxerr << "---------------------------------------------" << endl;
|
||||||
|
@ -53,7 +53,10 @@ inclusion in the "real LyX insets" FormulaInset and FormulaMacroInset.
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
class Cursor;
|
||||||
class OutputParams;
|
class OutputParams;
|
||||||
|
class MetricsInfo;
|
||||||
|
|
||||||
class InsetMathArray;
|
class InsetMathArray;
|
||||||
class InsetMathAMSArray;
|
class InsetMathAMSArray;
|
||||||
class InsetMathBrace;
|
class InsetMathBrace;
|
||||||
@ -72,7 +75,6 @@ class InsetMathSpace;
|
|||||||
class InsetMathSpecialChar;
|
class InsetMathSpecialChar;
|
||||||
class InsetMathSymbol;
|
class InsetMathSymbol;
|
||||||
class InsetMathUnknown;
|
class InsetMathUnknown;
|
||||||
|
|
||||||
class InsetMathRef;
|
class InsetMathRef;
|
||||||
|
|
||||||
class HtmlStream;
|
class HtmlStream;
|
||||||
@ -87,7 +89,7 @@ class WriteStream;
|
|||||||
class MathData;
|
class MathData;
|
||||||
class MathMacroTemplate;
|
class MathMacroTemplate;
|
||||||
class MathMacro;
|
class MathMacro;
|
||||||
class Cursor;
|
class MathRow;
|
||||||
class TextPainter;
|
class TextPainter;
|
||||||
class TextMetricsInfo;
|
class TextMetricsInfo;
|
||||||
class ReplaceData;
|
class ReplaceData;
|
||||||
@ -164,6 +166,8 @@ public:
|
|||||||
|
|
||||||
/// The class of the math object (used primarily for spacing)
|
/// The class of the math object (used primarily for spacing)
|
||||||
virtual MathClass mathClass() const;
|
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
|
/// identifies things that can get scripts
|
||||||
virtual bool isScriptable() const { return false; }
|
virtual bool isScriptable() const { return false; }
|
||||||
|
@ -30,12 +30,11 @@
|
|||||||
|
|
||||||
#include "mathed/InsetMathUnknown.h"
|
#include "mathed/InsetMathUnknown.h"
|
||||||
|
|
||||||
#include "support/debug.h"
|
|
||||||
#include "support/docstream.h"
|
|
||||||
|
|
||||||
#include "frontends/FontMetrics.h"
|
#include "frontends/FontMetrics.h"
|
||||||
#include "frontends/Painter.h"
|
#include "frontends/Painter.h"
|
||||||
|
|
||||||
|
#include "support/debug.h"
|
||||||
|
#include "support/docstream.h"
|
||||||
#include "support/gettext.h"
|
#include "support/gettext.h"
|
||||||
#include "support/lassert.h"
|
#include "support/lassert.h"
|
||||||
#include "support/lyxalgo.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
|
#if 0
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -247,7 +274,6 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) const
|
|||||||
mindes_ = (3 * xascent) / 4;
|
mindes_ = (3 * xascent) / 4;
|
||||||
slevel_ = (4 * xascent) / 5;
|
slevel_ = (4 * xascent) / 5;
|
||||||
sshift_ = xascent / 4;
|
sshift_ = xascent / 4;
|
||||||
kerning_ = 0;
|
|
||||||
|
|
||||||
if (empty()) {
|
if (empty()) {
|
||||||
// Cache the dimension.
|
// Cache the dimension.
|
||||||
@ -255,45 +281,17 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) const
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cursor & cur = mi.base.bv->cursor();
|
MathRow mrow(mi, this);
|
||||||
const_cast<MathData*>(this)->updateMacros(&cur, mi.macrocontext, InternalUpdate);
|
mrow_cache_[mi.base.bv] = mrow;
|
||||||
|
mrow.metrics(mi, dim);
|
||||||
|
kerning_ = mrow.kerning(mi.base.bv);
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
FontInfo font = mi.base.font;
|
|
||||||
augmentFont(font, "mathnormal");
|
|
||||||
dim.wid += mathed_string_width(font, completion);
|
|
||||||
}
|
|
||||||
// Cache the dimension.
|
// Cache the dimension.
|
||||||
mi.base.bv->coordCache().arrays().add(this, dim);
|
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;
|
//lyxerr << "MathData::draw: x: " << x << " y: " << y << endl;
|
||||||
BufferView & bv = *pi.base.bv;
|
BufferView & bv = *pi.base.bv;
|
||||||
@ -313,48 +311,8 @@ void MathData::draw(PainterInfo & pi, int x, int y) const
|
|||||||
|| x >= bv. workWidth())
|
|| x >= bv. workWidth())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DocIterator const & inlineCompletionPos = bv.inlineCompletionPos();
|
MathRow const & mrow = mrow_cache_[pi.base.bv];
|
||||||
MathData const * inlineCompletionData = 0;
|
mrow.draw(pi, x, y);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
#define MATH_DATA_H
|
#define MATH_DATA_H
|
||||||
|
|
||||||
#include "Dimension.h"
|
#include "Dimension.h"
|
||||||
|
|
||||||
#include "MathAtom.h"
|
#include "MathAtom.h"
|
||||||
|
#include "MathRow.h"
|
||||||
|
|
||||||
#include "OutputEnums.h"
|
#include "OutputEnums.h"
|
||||||
|
|
||||||
@ -24,6 +26,7 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
|
||||||
namespace lyx {
|
namespace lyx {
|
||||||
@ -117,6 +120,10 @@ public:
|
|||||||
MathAtom & operator[](pos_type);
|
MathAtom & operator[](pos_type);
|
||||||
/// checked read access
|
/// checked read access
|
||||||
MathAtom const & operator[](pos_type) const;
|
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
|
/// rebuild cached metrics information
|
||||||
void metrics(MetricsInfo & mi, Dimension & dim) const;
|
void metrics(MetricsInfo & mi, Dimension & dim) const;
|
||||||
///
|
///
|
||||||
@ -177,6 +184,9 @@ protected:
|
|||||||
mutable int kerning_;
|
mutable int kerning_;
|
||||||
Buffer * buffer_;
|
Buffer * buffer_;
|
||||||
|
|
||||||
|
/// cached object that describes typeset data
|
||||||
|
mutable std::map<BufferView*, MathRow> mrow_cache_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// is this an exact match at this position?
|
/// is this an exact match at this position?
|
||||||
bool find1(MathData const & ar, size_type pos) const;
|
bool find1(MathData const & ar, size_type pos) const;
|
||||||
|
@ -68,6 +68,27 @@ public:
|
|||||||
///
|
///
|
||||||
InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
|
InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
|
||||||
///
|
///
|
||||||
|
bool addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
|
||||||
|
{
|
||||||
|
MathRow::Element e(MathRow::BEG_ARG);
|
||||||
|
e.macro = mathMacro_;
|
||||||
|
e.ar = &mathMacro_->cell(idx_);
|
||||||
|
mrow.push_back(e);
|
||||||
|
|
||||||
|
mathMacro_->macro()->unlock();
|
||||||
|
bool const has_contents = mathMacro_->cell(idx_).addToMathRow(mrow, mi);
|
||||||
|
mathMacro_->macro()->lock();
|
||||||
|
|
||||||
|
e.type = MathRow::END_ARG;
|
||||||
|
mrow.push_back(e);
|
||||||
|
|
||||||
|
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 {
|
void metrics(MetricsInfo & mi, Dimension & dim) const {
|
||||||
mathMacro_->macro()->unlock();
|
mathMacro_->macro()->unlock();
|
||||||
mathMacro_->cell(idx_).metrics(mi, dim);
|
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]) {
|
||||||
|
MathRow::Element e(MathRow::BEG_MACRO);
|
||||||
|
e.macro = this;
|
||||||
|
mrow.push_back(e);
|
||||||
|
|
||||||
|
d->macro_->lock();
|
||||||
|
bool const has_contents = d->expanded_.addToMathRow(mrow, mi);
|
||||||
|
d->macro_->unlock();
|
||||||
|
|
||||||
|
e.type = MathRow::END_MACRO;
|
||||||
|
mrow.push_back(e);
|
||||||
|
|
||||||
|
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
|
Inset * MathMacro::clone() const
|
||||||
{
|
{
|
||||||
MathMacro * copy = new MathMacro(*this);
|
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_;
|
return d->macro_;
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,10 @@ public:
|
|||||||
///
|
///
|
||||||
virtual MathMacro const * asMacro() const { return this; }
|
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;
|
void draw(PainterInfo & pi, int x, int y) const;
|
||||||
/// draw selection background
|
/// draw selection background
|
||||||
void drawSelection(PainterInfo & pi, int x, int y) const;
|
void drawSelection(PainterInfo & pi, int x, int y) const;
|
||||||
@ -45,6 +49,8 @@ public:
|
|||||||
{ drawMarkers2(pi, x, y); }
|
{ drawMarkers2(pi, x, y); }
|
||||||
///
|
///
|
||||||
void metrics(MetricsInfo & mi, Dimension & dim) const;
|
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;
|
int kerning(BufferView const * bv) const;
|
||||||
/// get cursor position
|
/// get cursor position
|
||||||
@ -117,6 +123,8 @@ public:
|
|||||||
///
|
///
|
||||||
docstring name() const;
|
docstring name() const;
|
||||||
///
|
///
|
||||||
|
MacroData const * macro() const;
|
||||||
|
///
|
||||||
docstring macroName() const;
|
docstring macroName() const;
|
||||||
///
|
///
|
||||||
bool validName() const;
|
bool validName() const;
|
||||||
@ -151,10 +159,6 @@ protected:
|
|||||||
/// attach arguments (maybe less than arity at the end of an MathData),
|
/// attach arguments (maybe less than arity at the end of an MathData),
|
||||||
/// including the optional ones (even if it can be empty here)
|
/// including the optional ones (even if it can be empty here)
|
||||||
void attachArguments(std::vector<MathData> const & args, size_t arity, int optionals);
|
void attachArguments(std::vector<MathData> const & args, size_t arity, int optionals);
|
||||||
///
|
|
||||||
MacroData const * macro();
|
|
||||||
///
|
|
||||||
bool editMetrics(BufferView const * bv) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
///
|
///
|
||||||
|
292
src/mathed/MathRow.cpp
Normal file
292
src/mathed/MathRow.cpp
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
/**
|
||||||
|
* \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 {
|
||||||
|
|
||||||
|
|
||||||
|
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::MathRow(MetricsInfo const & mi, MathData const * ar)
|
||||||
|
{
|
||||||
|
if (ar->empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// First there is a dummy element of type "open"
|
||||||
|
push_back(Element(BEGIN, MC_OPEN));
|
||||||
|
|
||||||
|
// Then insert the MathData argument
|
||||||
|
ar->addToMathRow(*this, mi);
|
||||||
|
|
||||||
|
// Finally there is a dummy element of type "close"
|
||||||
|
push_back(Element(END, 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.
|
||||||
|
*/
|
||||||
|
if (!isMathFont(mi.base.fontname))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// update classes
|
||||||
|
for (int i = 1 ; i != static_cast<int>(elements_.size()) - 1 ; ++i) {
|
||||||
|
if (elements_[i].type != INSET)
|
||||||
|
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)
|
||||||
|
continue;
|
||||||
|
Element & bef = elements_[before(i)];
|
||||||
|
int spc = class_spacing(bef.mclass, elements_[i].mclass, mi.base);
|
||||||
|
bef.after = spc / 2;
|
||||||
|
// this is better than spc / 2 to avoid rounding problems
|
||||||
|
elements_[i].before = spc - spc / 2;
|
||||||
|
}
|
||||||
|
// Do not lose spacing allocated to extremities
|
||||||
|
if (!elements_.empty()) {
|
||||||
|
elements_[after(0)].before += elements_.front().after;
|
||||||
|
elements_[before(elements_.size() - 1)].after += elements_.back().before;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MathRow::before(int i) const
|
||||||
|
{
|
||||||
|
do
|
||||||
|
--i;
|
||||||
|
while (elements_[i].type != BEGIN
|
||||||
|
&& elements_[i].type != INSET);
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MathRow::after(int i) const
|
||||||
|
{
|
||||||
|
do
|
||||||
|
++i;
|
||||||
|
while (elements_[i].type != END
|
||||||
|
&& elements_[i].type != 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 (Element const & e : elements_) {
|
||||||
|
Dimension d;
|
||||||
|
switch (e.type) {
|
||||||
|
case BEGIN:
|
||||||
|
case END:
|
||||||
|
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:
|
||||||
|
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());
|
||||||
|
e.macro->macro()->unlock();
|
||||||
|
// Cache the dimension of the macro and remove it from
|
||||||
|
// tracking map.
|
||||||
|
coords.insets().add(e.macro, dim_macros[e.macro]);
|
||||||
|
dim_macros.erase(e.macro);
|
||||||
|
break;
|
||||||
|
// This is basically like macros
|
||||||
|
case BEG_ARG:
|
||||||
|
if (e.macro)
|
||||||
|
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)
|
||||||
|
e.macro->macro()->lock();
|
||||||
|
coords.arrays().add(e.ar, dim_arrays[e.ar]);
|
||||||
|
dim_arrays.erase(e.ar);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.compl_text.empty())
|
||||||
|
continue;
|
||||||
|
FontInfo font = mi.base.font;
|
||||||
|
augmentFont(font, "mathnormal");
|
||||||
|
dim.wid += mathed_string_width(font, e.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 (Element const & e : elements_) {
|
||||||
|
Dimension d;
|
||||||
|
switch (e.type) {
|
||||||
|
case INSET: {
|
||||||
|
// 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);
|
||||||
|
e.inset->draw(pi, x + e.before, y);
|
||||||
|
coords.insets().add(e.inset, x, y);
|
||||||
|
coords.insets().add(e.inset, d);
|
||||||
|
x += d.wid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BEG_MACRO:
|
||||||
|
coords.insets().add(e.macro, x, y);
|
||||||
|
break;
|
||||||
|
case BEG_ARG:
|
||||||
|
coords.arrays().add(e.ar, x, y);
|
||||||
|
// if the macro is being edited, then the painter is in
|
||||||
|
// monochrome mode.
|
||||||
|
if (e.macro->editMetrics(pi.base.bv))
|
||||||
|
pi.pain.leaveMonochromeMode();
|
||||||
|
break;
|
||||||
|
case END_ARG:
|
||||||
|
if (e.macro->editMetrics(pi.base.bv))
|
||||||
|
pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
|
||||||
|
break;
|
||||||
|
case BEGIN:
|
||||||
|
case END:
|
||||||
|
case END_MACRO:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.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 = e.compl_text.substr(0, e.compl_unique_to);
|
||||||
|
docstring const s2 = e.compl_text.substr(e.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 (elements_.empty())
|
||||||
|
return 0;
|
||||||
|
InsetMath const * inset = elements_[before(elements_.size() - 1)].inset;
|
||||||
|
return inset ? inset->kerning(bv) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ostream & operator<<(ostream & os, MathRow::Element const & e)
|
||||||
|
{
|
||||||
|
switch (e.type) {
|
||||||
|
case MathRow::BEGIN:
|
||||||
|
os << "{";
|
||||||
|
break;
|
||||||
|
case MathRow::END:
|
||||||
|
os << "}";
|
||||||
|
break;
|
||||||
|
case MathRow::INSET:
|
||||||
|
os << "<" << e.before << "-"
|
||||||
|
<< to_utf8(class_to_string(e.mclass))
|
||||||
|
<< "-" << e.after << ">";
|
||||||
|
break;
|
||||||
|
case MathRow::BEG_MACRO:
|
||||||
|
os << "\\" << to_utf8(e.macro->name()) << "[";
|
||||||
|
break;
|
||||||
|
case MathRow::END_MACRO:
|
||||||
|
os << "]";
|
||||||
|
break;
|
||||||
|
case MathRow::BEG_ARG:
|
||||||
|
os << "#(";
|
||||||
|
break;
|
||||||
|
case MathRow::END_ARG:
|
||||||
|
os << ")";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ostream & operator<<(ostream & os, MathRow const & mrow)
|
||||||
|
{
|
||||||
|
for (MathRow::Element const & e : mrow)
|
||||||
|
os << e << " ";
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lyx
|
137
src/mathed/MathRow.h
Normal file
137
src/mathed/MathRow.h
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// -*- 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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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:
|
||||||
|
// What row elements can be
|
||||||
|
enum Type {
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
// An elements, together with its spacing
|
||||||
|
struct Element
|
||||||
|
{
|
||||||
|
///
|
||||||
|
Element(Type t = INSET, MathClass const mc = MC_ORD);
|
||||||
|
|
||||||
|
/// Classifies the contents of the object
|
||||||
|
Type type;
|
||||||
|
|
||||||
|
/// When type 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 type is BEG_MACRO, END_MACRO, BEG_ARG, END_ARG
|
||||||
|
/// the math macro
|
||||||
|
MathMacro const * macro;
|
||||||
|
|
||||||
|
// type is BEG_ARG, END_ARG
|
||||||
|
MathData const * ar;
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
MathRow() {};
|
||||||
|
///
|
||||||
|
typedef std::vector<Element> Elements;
|
||||||
|
///
|
||||||
|
typedef Elements::iterator iterator;
|
||||||
|
///
|
||||||
|
typedef Elements::const_iterator const_iterator;
|
||||||
|
///
|
||||||
|
iterator begin() { return elements_.begin(); }
|
||||||
|
///
|
||||||
|
iterator end() { return elements_.end(); }
|
||||||
|
///
|
||||||
|
const_iterator begin() const { return elements_.begin(); }
|
||||||
|
///
|
||||||
|
const_iterator end() const { return elements_.end(); }
|
||||||
|
//
|
||||||
|
void push_back(Element const & e) { elements_.push_back(e); }
|
||||||
|
//
|
||||||
|
Element & back() { return elements_.back(); }
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
///
|
||||||
|
Elements elements_;
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
std::ostream & operator<<(std::ostream & os, MathRow::Element const & elt);
|
||||||
|
|
||||||
|
///
|
||||||
|
std::ostream & operator<<(std::ostream & os, MathRow const & mrow);
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace lyx
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user