mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-16 16:18:22 +00:00
de5e348727
Actually, the test case showed several problems: - ERT insets did use layout "Standard", not "Plain Layout" - The font scale was read correctly, but tex2lyx claimed that it did ignore the option "scaled=0.95" - If a third argument of the CJK environment was given, it caused the whole environment to be put in ERT with a broken encoding. This is now fixed for the bug test case by using the \font_cjk header variable, but the encoding problem still exists for unsupported encodings. I'll file a separate bug for that. - The CJKutf8 package was not handled in the preamble parsing. Therefore the chinese comment in the preamble was read with a wrong encoding, and guessing the document language did not work. The new file CJKutf8.tex was created by copying and modifying CJK.tex, but unfortunately it is impossible to tell git to inherit the history of CJK.tex for the new file (search the web for git svn copy if you want to know details).
907 lines
22 KiB
C++
907 lines
22 KiB
C++
/**
|
|
* \file MathMacro.cpp
|
|
* This file is part of LyX, the document processor.
|
|
* Licence details can be found in the file COPYING.
|
|
*
|
|
* \author Alejandro Aguilar Sierra
|
|
* \author André Pönitz
|
|
* \author Stefan Schimanski
|
|
*
|
|
* Full author contact details are available in file CREDITS.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "MathMacro.h"
|
|
|
|
#include "InsetMathChar.h"
|
|
#include "MathCompletionList.h"
|
|
#include "MathExtern.h"
|
|
#include "MathFactory.h"
|
|
#include "MathStream.h"
|
|
#include "MathSupport.h"
|
|
|
|
#include "Buffer.h"
|
|
#include "BufferView.h"
|
|
#include "CoordCache.h"
|
|
#include "Cursor.h"
|
|
#include "FuncStatus.h"
|
|
#include "FuncRequest.h"
|
|
#include "LaTeXFeatures.h"
|
|
#include "LyX.h"
|
|
#include "LyXRC.h"
|
|
|
|
#include "frontends/Painter.h"
|
|
|
|
#include "support/debug.h"
|
|
#include "support/lassert.h"
|
|
#include "support/textutils.h"
|
|
|
|
#include <ostream>
|
|
#include <vector>
|
|
|
|
using namespace std;
|
|
|
|
namespace lyx {
|
|
|
|
|
|
/// A proxy for the macro values
|
|
class ArgumentProxy : public InsetMath {
|
|
public:
|
|
///
|
|
ArgumentProxy(MathMacro & mathMacro, size_t idx)
|
|
: mathMacro_(mathMacro), idx_(idx) {}
|
|
///
|
|
ArgumentProxy(MathMacro & mathMacro, size_t idx, docstring const & def)
|
|
: mathMacro_(mathMacro), idx_(idx)
|
|
{
|
|
asArray(def, def_);
|
|
}
|
|
///
|
|
InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
|
|
///
|
|
void metrics(MetricsInfo & mi, Dimension & dim) const {
|
|
mathMacro_.macro()->unlock();
|
|
mathMacro_.cell(idx_).metrics(mi, dim);
|
|
|
|
if (!mathMacro_.editMetrics(mi.base.bv)
|
|
&& mathMacro_.cell(idx_).empty())
|
|
def_.metrics(mi, dim);
|
|
|
|
mathMacro_.macro()->lock();
|
|
}
|
|
// FIXME Other external things need similar treatment.
|
|
///
|
|
void mathmlize(MathStream & ms) const { ms << mathMacro_.cell(idx_); }
|
|
///
|
|
void htmlize(HtmlStream & ms) const { ms << mathMacro_.cell(idx_); }
|
|
///
|
|
void draw(PainterInfo & pi, int x, int y) const {
|
|
if (mathMacro_.editMetrics(pi.base.bv)) {
|
|
// The only way a ArgumentProxy can appear is in a cell of the
|
|
// MathMacro. Moreover the cells are only drawn in the DISPLAY_FOLDED
|
|
// mode and then, if the macro is edited the monochrome
|
|
// mode is entered by the MathMacro before calling the cells' draw
|
|
// method. Then eventually this code is reached and the proxy leaves
|
|
// monochrome mode temporarely. Hence, if it is not in monochrome
|
|
// here (and the assert triggers in pain.leaveMonochromeMode())
|
|
// it's a bug.
|
|
pi.pain.leaveMonochromeMode();
|
|
mathMacro_.cell(idx_).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);
|
|
def_.draw(pi, x, y);
|
|
} else
|
|
mathMacro_.cell(idx_).draw(pi, x, y);
|
|
}
|
|
///
|
|
size_t idx() const { return idx_; }
|
|
///
|
|
int kerning(BufferView const * bv) const
|
|
{
|
|
if (mathMacro_.editMetrics(bv)
|
|
|| !mathMacro_.cell(idx_).empty())
|
|
return mathMacro_.cell(idx_).kerning(bv);
|
|
else
|
|
return def_.kerning(bv);
|
|
}
|
|
|
|
private:
|
|
///
|
|
Inset * clone() const
|
|
{
|
|
return new ArgumentProxy(*this);
|
|
}
|
|
///
|
|
MathMacro & mathMacro_;
|
|
///
|
|
size_t idx_;
|
|
///
|
|
MathData def_;
|
|
};
|
|
|
|
|
|
MathMacro::MathMacro(Buffer * buf, docstring const & name)
|
|
: InsetMathNest(buf, 0), name_(name), displayMode_(DISPLAY_INIT),
|
|
expanded_(buf), attachedArgsNum_(0), optionals_(0), nextFoldMode_(true),
|
|
macroBackup_(buf), macro_(0), needsUpdate_(false), appetite_(9)
|
|
{}
|
|
|
|
|
|
Inset * MathMacro::clone() const
|
|
{
|
|
MathMacro * copy = new MathMacro(*this);
|
|
copy->needsUpdate_ = true;
|
|
//copy->expanded_.cell(0).clear();
|
|
return copy;
|
|
}
|
|
|
|
|
|
void MathMacro::normalize(NormalStream & os) const
|
|
{
|
|
os << "[macro " << name();
|
|
for (size_t i = 0; i < nargs(); ++i)
|
|
os << ' ' << cell(i);
|
|
os << ']';
|
|
}
|
|
|
|
|
|
docstring MathMacro::name() const
|
|
{
|
|
if (displayMode_ == DISPLAY_UNFOLDED)
|
|
return asString(cell(0));
|
|
|
|
return name_;
|
|
}
|
|
|
|
|
|
void MathMacro::cursorPos(BufferView const & bv,
|
|
CursorSlice const & sl, bool boundary, int & x, int & y) const
|
|
{
|
|
// We may have 0 arguments, but InsetMathNest requires at least one.
|
|
if (nargs() > 0)
|
|
InsetMathNest::cursorPos(bv, sl, boundary, x, y);
|
|
}
|
|
|
|
|
|
bool MathMacro::editMode(BufferView const * bv) const {
|
|
// find this in cursor trace
|
|
Cursor const & cur = bv->cursor();
|
|
for (size_t i = 0; i != cur.depth(); ++i)
|
|
if (&cur[i].inset() == this) {
|
|
// look if there is no other macro in edit mode above
|
|
++i;
|
|
for (; i != cur.depth(); ++i) {
|
|
InsetMath * im = cur[i].asInsetMath();
|
|
if (im) {
|
|
MathMacro const * macro = im->asMacro();
|
|
if (macro && macro->displayMode() == DISPLAY_NORMAL)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// ok, none found, I am the highest one
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool MathMacro::editMetrics(BufferView const * bv) const
|
|
{
|
|
return editing_[bv];
|
|
}
|
|
|
|
|
|
void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
|
|
{
|
|
// set edit mode for which we will have calculated metrics. But only
|
|
editing_[mi.base.bv] = editMode(mi.base.bv);
|
|
|
|
// calculate new metrics according to display mode
|
|
if (displayMode_ == DISPLAY_INIT || displayMode_ == DISPLAY_INTERACTIVE_INIT) {
|
|
mathed_string_dim(mi.base.font, from_ascii("\\") + name(), dim);
|
|
} else if (displayMode_ == DISPLAY_UNFOLDED) {
|
|
cell(0).metrics(mi, dim);
|
|
Dimension bsdim;
|
|
mathed_string_dim(mi.base.font, from_ascii("\\"), bsdim);
|
|
dim.wid += bsdim.width() + 1;
|
|
dim.asc = max(bsdim.ascent(), dim.ascent());
|
|
dim.des = max(bsdim.descent(), dim.descent());
|
|
metricsMarkers(dim);
|
|
} else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST
|
|
&& editing_[mi.base.bv]) {
|
|
// Macro will be edited in a old-style list mode here:
|
|
|
|
LASSERT(macro_ != 0, /**/);
|
|
Dimension fontDim;
|
|
FontInfo labelFont = sane_font;
|
|
math_font_max_dim(labelFont, fontDim.asc, fontDim.des);
|
|
|
|
// get dimension of components of list view
|
|
Dimension nameDim;
|
|
nameDim.wid = mathed_string_width(mi.base.font, from_ascii("Macro \\") + name() + ": ");
|
|
nameDim.asc = fontDim.asc;
|
|
nameDim.des = fontDim.des;
|
|
|
|
Dimension argDim;
|
|
argDim.wid = mathed_string_width(labelFont, from_ascii("#9: "));
|
|
argDim.asc = fontDim.asc;
|
|
argDim.des = fontDim.des;
|
|
|
|
Dimension defDim;
|
|
definition_.metrics(mi, defDim);
|
|
|
|
// add them up
|
|
dim.wid = nameDim.wid + defDim.wid;
|
|
dim.asc = max(nameDim.asc, defDim.asc);
|
|
dim.des = max(nameDim.des, defDim.des);
|
|
|
|
for (idx_type i = 0; i < nargs(); ++i) {
|
|
Dimension cdim;
|
|
cell(i).metrics(mi, cdim);
|
|
dim.des += max(argDim.height(), cdim.height()) + 1;
|
|
dim.wid = max(dim.wid, argDim.wid + cdim.wid);
|
|
}
|
|
|
|
// make space for box and markers, 2 pixels
|
|
dim.asc += 1;
|
|
dim.des += 1;
|
|
dim.wid += 2;
|
|
metricsMarkers2(dim);
|
|
} else {
|
|
LASSERT(macro_ != 0, /**/);
|
|
|
|
// calculate metrics, hoping that all cells are seen
|
|
macro_->lock();
|
|
expanded_.cell(0).metrics(mi, dim);
|
|
|
|
// otherwise do a manual metrics call
|
|
CoordCache & coords = mi.base.bv->coordCache();
|
|
for (idx_type i = 0; i < nargs(); ++i) {
|
|
if (!coords.getArrays().hasDim(&cell(i))) {
|
|
Dimension tdim;
|
|
cell(i).metrics(mi, tdim);
|
|
}
|
|
}
|
|
macro_->unlock();
|
|
|
|
// calculate dimension with label while editing
|
|
if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_INLINE_BOX
|
|
&& editing_[mi.base.bv]) {
|
|
FontInfo font = mi.base.font;
|
|
augmentFont(font, from_ascii("lyxtex"));
|
|
Dimension namedim;
|
|
mathed_string_dim(font, name(), namedim);
|
|
#if 0
|
|
dim.wid += 2 + namedim.wid + 2 + 2;
|
|
dim.asc = max(dim.asc, namedim.asc) + 2;
|
|
dim.des = max(dim.des, namedim.des) + 2;
|
|
#endif
|
|
dim.wid = max(1 + namedim.wid + 1, 2 + dim.wid + 2);
|
|
dim.asc += 1 + namedim.height() + 1;
|
|
dim.des += 2;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
int MathMacro::kerning(BufferView const * bv) const {
|
|
if (displayMode_ == DISPLAY_NORMAL && !editing_[bv])
|
|
return expanded_.kerning(bv);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
void MathMacro::updateMacro(MacroContext const & mc)
|
|
{
|
|
if (validName()) {
|
|
macro_ = mc.get(name());
|
|
if (macro_ && macroBackup_ != *macro_) {
|
|
macroBackup_ = *macro_;
|
|
needsUpdate_ = true;
|
|
}
|
|
} else {
|
|
macro_ = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void MathMacro::updateRepresentation(Cursor * cur, MacroContext const & mc,
|
|
UpdateType utype)
|
|
{
|
|
// known macro?
|
|
if (macro_ == 0)
|
|
return;
|
|
|
|
// update requires
|
|
requires_ = macro_->requires();
|
|
|
|
if (!needsUpdate_
|
|
// non-normal mode? We are done!
|
|
|| (displayMode_ != DISPLAY_NORMAL))
|
|
return;
|
|
|
|
needsUpdate_ = false;
|
|
|
|
// get default values of macro
|
|
vector<docstring> const & defaults = macro_->defaults();
|
|
|
|
// create MathMacroArgumentValue objects pointing to the cells of the macro
|
|
vector<MathData> values(nargs());
|
|
for (size_t i = 0; i < nargs(); ++i) {
|
|
ArgumentProxy * proxy;
|
|
if (i < defaults.size())
|
|
proxy = new ArgumentProxy(*this, i, defaults[i]);
|
|
else
|
|
proxy = new ArgumentProxy(*this, i);
|
|
values[i].insert(0, MathAtom(proxy));
|
|
}
|
|
// expanding macro with the values
|
|
macro_->expand(values, expanded_.cell(0));
|
|
if (utype == OutputUpdate && !expanded_.cell(0).empty())
|
|
expanded_.cell(0).updateMacros(cur, mc, utype);
|
|
// get definition for list edit mode
|
|
docstring const & display = macro_->display();
|
|
asArray(display.empty() ? macro_->definition() : display, definition_);
|
|
}
|
|
|
|
|
|
void MathMacro::draw(PainterInfo & pi, int x, int y) const
|
|
{
|
|
Dimension const dim = dimension(*pi.base.bv);
|
|
|
|
setPosCache(pi, x, y);
|
|
int expx = x;
|
|
int expy = y;
|
|
|
|
if (displayMode_ == DISPLAY_INIT || displayMode_ == DISPLAY_INTERACTIVE_INIT) {
|
|
FontSetChanger dummy(pi.base, "lyxtex");
|
|
pi.pain.text(x, y, from_ascii("\\") + name(), pi.base.font);
|
|
} else if (displayMode_ == DISPLAY_UNFOLDED) {
|
|
FontSetChanger dummy(pi.base, "lyxtex");
|
|
pi.pain.text(x, y, from_ascii("\\"), pi.base.font);
|
|
x += mathed_string_width(pi.base.font, from_ascii("\\")) + 1;
|
|
cell(0).draw(pi, x, y);
|
|
drawMarkers(pi, expx, expy);
|
|
} else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST
|
|
&& editing_[pi.base.bv]) {
|
|
// Macro will be edited in a old-style list mode here:
|
|
|
|
CoordCache const & coords = pi.base.bv->coordCache();
|
|
FontInfo const & labelFont = sane_font;
|
|
|
|
// markers and box needs two pixels
|
|
x += 2;
|
|
|
|
// get maximal font height
|
|
Dimension fontDim;
|
|
math_font_max_dim(pi.base.font, fontDim.asc, fontDim.des);
|
|
|
|
// draw label
|
|
docstring label = from_ascii("Macro \\") + name() + from_ascii(": ");
|
|
pi.pain.text(x, y, label, labelFont);
|
|
x += mathed_string_width(labelFont, label);
|
|
|
|
// draw definition
|
|
definition_.draw(pi, x, y);
|
|
Dimension const & defDim = coords.getArrays().dim(&definition_);
|
|
y += max(fontDim.des, defDim.des);
|
|
|
|
// draw parameters
|
|
docstring str = from_ascii("#9");
|
|
int strw1 = mathed_string_width(labelFont, from_ascii("#9"));
|
|
int strw2 = mathed_string_width(labelFont, from_ascii(": "));
|
|
|
|
for (idx_type i = 0; i < nargs(); ++i) {
|
|
// position of label
|
|
Dimension const & cdim = coords.getArrays().dim(&cell(i));
|
|
x = expx + 2;
|
|
y += max(fontDim.asc, cdim.asc) + 1;
|
|
|
|
// draw label
|
|
str[1] = '1' + i;
|
|
pi.pain.text(x, y, str, labelFont);
|
|
x += strw1;
|
|
pi.pain.text(x, y, from_ascii(":"), labelFont);
|
|
x += strw2;
|
|
|
|
// draw paramter
|
|
cell(i).draw(pi, x, y);
|
|
|
|
// next line
|
|
y += max(fontDim.des, cdim.des);
|
|
}
|
|
|
|
pi.pain.rectangle(expx + 1, expy - dim.asc + 1, dim.wid - 3,
|
|
dim.height() - 2, Color_mathmacroframe);
|
|
drawMarkers2(pi, expx, expy);
|
|
} else {
|
|
bool drawBox = lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_INLINE_BOX;
|
|
|
|
// warm up cells
|
|
for (size_t i = 0; i < nargs(); ++i)
|
|
cell(i).setXY(*pi.base.bv, x, y);
|
|
|
|
if (drawBox && editing_[pi.base.bv]) {
|
|
// draw header and rectangle around
|
|
FontInfo font = pi.base.font;
|
|
augmentFont(font, from_ascii("lyxtex"));
|
|
font.setSize(FONT_SIZE_TINY);
|
|
font.setColor(Color_mathmacrolabel);
|
|
Dimension namedim;
|
|
mathed_string_dim(font, name(), namedim);
|
|
|
|
pi.pain.fillRectangle(x, y - dim.asc, dim.wid, 1 + namedim.height() + 1, Color_mathmacrobg);
|
|
pi.pain.text(x + 1, y - dim.asc + namedim.asc + 2, name(), font);
|
|
expx += (dim.wid - expanded_.cell(0).dimension(*pi.base.bv).width()) / 2;
|
|
}
|
|
|
|
if (editing_[pi.base.bv]) {
|
|
pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
|
|
expanded_.cell(0).draw(pi, expx, expy);
|
|
pi.pain.leaveMonochromeMode();
|
|
|
|
if (drawBox)
|
|
pi.pain.rectangle(x, y - dim.asc, dim.wid,
|
|
dim.height(), Color_mathmacroframe);
|
|
} else
|
|
expanded_.cell(0).draw(pi, expx, expy);
|
|
|
|
if (!drawBox)
|
|
drawMarkers(pi, x, y);
|
|
}
|
|
|
|
// edit mode changed?
|
|
if (editing_[pi.base.bv] != editMode(pi.base.bv))
|
|
pi.base.bv->cursor().screenUpdateFlags(Update::SinglePar);
|
|
}
|
|
|
|
|
|
void MathMacro::drawSelection(PainterInfo & pi, int x, int y) const
|
|
{
|
|
// We may have 0 arguments, but InsetMathNest requires at least one.
|
|
if (!cells_.empty())
|
|
InsetMathNest::drawSelection(pi, x, y);
|
|
}
|
|
|
|
|
|
void MathMacro::setDisplayMode(MathMacro::DisplayMode mode, int appetite)
|
|
{
|
|
if (displayMode_ != mode) {
|
|
// transfer name if changing from or to DISPLAY_UNFOLDED
|
|
if (mode == DISPLAY_UNFOLDED) {
|
|
cells_.resize(1);
|
|
asArray(name_, cell(0));
|
|
} else if (displayMode_ == DISPLAY_UNFOLDED) {
|
|
name_ = asString(cell(0));
|
|
cells_.resize(0);
|
|
}
|
|
|
|
displayMode_ = mode;
|
|
needsUpdate_ = true;
|
|
}
|
|
|
|
// the interactive init mode is non-greedy by default
|
|
if (appetite == -1)
|
|
appetite_ = (mode == DISPLAY_INTERACTIVE_INIT) ? 0 : 9;
|
|
else
|
|
appetite_ = size_t(appetite);
|
|
}
|
|
|
|
|
|
MathMacro::DisplayMode MathMacro::computeDisplayMode() const
|
|
{
|
|
if (nextFoldMode_ == true && macro_ && !macro_->locked())
|
|
return DISPLAY_NORMAL;
|
|
else
|
|
return DISPLAY_UNFOLDED;
|
|
}
|
|
|
|
|
|
bool MathMacro::validName() const
|
|
{
|
|
docstring n = name();
|
|
|
|
if (n.empty())
|
|
return false;
|
|
|
|
// converting back and force doesn't swallow anything?
|
|
/*MathData ma;
|
|
asArray(n, ma);
|
|
if (asString(ma) != n)
|
|
return false;*/
|
|
|
|
// valid characters?
|
|
for (size_t i = 0; i<n.size(); ++i) {
|
|
if (!(n[i] >= 'a' && n[i] <= 'z')
|
|
&& !(n[i] >= 'A' && n[i] <= 'Z')
|
|
&& n[i] != '*')
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void MathMacro::validate(LaTeXFeatures & features) const
|
|
{
|
|
if (!requires_.empty())
|
|
features.require(requires_);
|
|
|
|
if (name() == "binom")
|
|
features.require("binom");
|
|
|
|
// validate the cells and the definition
|
|
if (displayMode() == DISPLAY_NORMAL) {
|
|
definition_.validate(features);
|
|
InsetMathNest::validate(features);
|
|
}
|
|
}
|
|
|
|
|
|
void MathMacro::edit(Cursor & cur, bool front, EntryDirection entry_from)
|
|
{
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
|
InsetMathNest::edit(cur, front, entry_from);
|
|
}
|
|
|
|
|
|
Inset * MathMacro::editXY(Cursor & cur, int x, int y)
|
|
{
|
|
// We may have 0 arguments, but InsetMathNest requires at least one.
|
|
if (nargs() > 0) {
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
|
return InsetMathNest::editXY(cur, x, y);
|
|
} else
|
|
return this;
|
|
}
|
|
|
|
|
|
void MathMacro::removeArgument(Inset::pos_type pos) {
|
|
if (displayMode_ == DISPLAY_NORMAL) {
|
|
LASSERT(size_t(pos) < cells_.size(), /**/);
|
|
cells_.erase(cells_.begin() + pos);
|
|
if (size_t(pos) < attachedArgsNum_)
|
|
--attachedArgsNum_;
|
|
if (size_t(pos) < optionals_) {
|
|
--optionals_;
|
|
}
|
|
|
|
needsUpdate_ = true;
|
|
}
|
|
}
|
|
|
|
|
|
void MathMacro::insertArgument(Inset::pos_type pos) {
|
|
if (displayMode_ == DISPLAY_NORMAL) {
|
|
LASSERT(size_t(pos) <= cells_.size(), /**/);
|
|
cells_.insert(cells_.begin() + pos, MathData());
|
|
if (size_t(pos) < attachedArgsNum_)
|
|
++attachedArgsNum_;
|
|
if (size_t(pos) < optionals_)
|
|
++optionals_;
|
|
|
|
needsUpdate_ = true;
|
|
}
|
|
}
|
|
|
|
|
|
void MathMacro::detachArguments(vector<MathData> & args, bool strip)
|
|
{
|
|
LASSERT(displayMode_ == DISPLAY_NORMAL, /**/);
|
|
args = cells_;
|
|
|
|
// strip off empty cells, but not more than arity-attachedArgsNum_
|
|
if (strip) {
|
|
size_t i;
|
|
for (i = cells_.size(); i > attachedArgsNum_; --i)
|
|
if (!cell(i - 1).empty()) break;
|
|
args.resize(i);
|
|
}
|
|
|
|
attachedArgsNum_ = 0;
|
|
expanded_.cell(0) = MathData();
|
|
cells_.resize(0);
|
|
|
|
needsUpdate_ = true;
|
|
}
|
|
|
|
|
|
void MathMacro::attachArguments(vector<MathData> const & args, size_t arity, int optionals)
|
|
{
|
|
LASSERT(displayMode_ == DISPLAY_NORMAL, /**/);
|
|
cells_ = args;
|
|
attachedArgsNum_ = args.size();
|
|
cells_.resize(arity);
|
|
expanded_.cell(0) = MathData();
|
|
optionals_ = optionals;
|
|
|
|
needsUpdate_ = true;
|
|
}
|
|
|
|
|
|
bool MathMacro::idxFirst(Cursor & cur) const
|
|
{
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
|
return InsetMathNest::idxFirst(cur);
|
|
}
|
|
|
|
|
|
bool MathMacro::idxLast(Cursor & cur) const
|
|
{
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
|
return InsetMathNest::idxLast(cur);
|
|
}
|
|
|
|
|
|
bool MathMacro::notifyCursorLeaves(Cursor const & old, Cursor & cur)
|
|
{
|
|
if (displayMode_ == DISPLAY_UNFOLDED) {
|
|
docstring const & unfolded_name = name();
|
|
if (unfolded_name != name_) {
|
|
// The macro name was changed
|
|
Cursor inset_cursor = old;
|
|
int macroSlice = inset_cursor.find(this);
|
|
LASSERT(macroSlice != -1, /**/);
|
|
inset_cursor.cutOff(macroSlice);
|
|
inset_cursor.recordUndoInset();
|
|
inset_cursor.pop();
|
|
inset_cursor.cell().erase(inset_cursor.pos());
|
|
inset_cursor.cell().insert(inset_cursor.pos(),
|
|
createInsetMath(unfolded_name, cur.buffer()));
|
|
cur.resetAnchor();
|
|
cur.screenUpdateFlags(cur.result().screenUpdate() | Update::SinglePar);
|
|
return true;
|
|
}
|
|
}
|
|
cur.screenUpdateFlags(Update::Force);
|
|
return InsetMathNest::notifyCursorLeaves(old, cur);
|
|
}
|
|
|
|
|
|
void MathMacro::fold(Cursor & cur)
|
|
{
|
|
if (!nextFoldMode_) {
|
|
nextFoldMode_ = true;
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
|
}
|
|
}
|
|
|
|
|
|
void MathMacro::unfold(Cursor & cur)
|
|
{
|
|
if (nextFoldMode_) {
|
|
nextFoldMode_ = false;
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
|
}
|
|
}
|
|
|
|
|
|
bool MathMacro::folded() const
|
|
{
|
|
return nextFoldMode_;
|
|
}
|
|
|
|
|
|
void MathMacro::write(WriteStream & os) const
|
|
{
|
|
MathEnsurer ensurer(os, macro_ != 0, true);
|
|
|
|
// non-normal mode
|
|
if (displayMode_ != DISPLAY_NORMAL) {
|
|
os << "\\" << name();
|
|
if (name().size() != 1 || isAlphaASCII(name()[0]))
|
|
os.pendingSpace(true);
|
|
return;
|
|
}
|
|
|
|
// normal mode
|
|
LASSERT(macro_, /**/);
|
|
|
|
// optional arguments make macros fragile
|
|
if (optionals_ > 0 && os.fragile())
|
|
os << "\\protect";
|
|
|
|
os << "\\" << name();
|
|
bool first = true;
|
|
|
|
// Optional arguments:
|
|
// First find last non-empty optional argument
|
|
idx_type emptyOptFrom = 0;
|
|
idx_type i = 0;
|
|
for (; i < cells_.size() && i < optionals_; ++i) {
|
|
if (!cell(i).empty())
|
|
emptyOptFrom = i + 1;
|
|
}
|
|
|
|
// print out optionals
|
|
for (i=0; i < cells_.size() && i < emptyOptFrom; ++i) {
|
|
first = false;
|
|
os << "[" << cell(i) << "]";
|
|
}
|
|
|
|
// skip the tailing empty optionals
|
|
i = optionals_;
|
|
|
|
// Print remaining arguments
|
|
for (; i < cells_.size(); ++i) {
|
|
if (cell(i).size() == 1
|
|
&& cell(i)[0].nucleus()->asCharInset()
|
|
&& isASCII(cell(i)[0].nucleus()->asCharInset()->getChar())) {
|
|
if (first)
|
|
os << " ";
|
|
os << cell(i);
|
|
} else
|
|
os << "{" << cell(i) << "}";
|
|
first = false;
|
|
}
|
|
|
|
// add space if there was no argument
|
|
if (first)
|
|
os.pendingSpace(true);
|
|
}
|
|
|
|
|
|
void MathMacro::maple(MapleStream & os) const
|
|
{
|
|
lyx::maple(expanded_.cell(0), os);
|
|
}
|
|
|
|
|
|
void MathMacro::mathmlize(MathStream & os) const
|
|
{
|
|
MathData const & data = expanded_.cell(0);
|
|
if (data.empty()) {
|
|
// this means that we do not recognize the macro
|
|
throw MathExportException();
|
|
}
|
|
os << data;
|
|
}
|
|
|
|
|
|
void MathMacro::htmlize(HtmlStream & os) const
|
|
{
|
|
MathData const & data = expanded_.cell(0);
|
|
if (data.empty()) {
|
|
// this means that we do not recognize the macro
|
|
throw MathExportException();
|
|
}
|
|
os << data;
|
|
}
|
|
|
|
|
|
void MathMacro::octave(OctaveStream & os) const
|
|
{
|
|
lyx::octave(expanded_.cell(0), os);
|
|
}
|
|
|
|
|
|
void MathMacro::infoize(odocstream & os) const
|
|
{
|
|
os << "Macro: " << name();
|
|
}
|
|
|
|
|
|
void MathMacro::infoize2(odocstream & os) const
|
|
{
|
|
os << "Macro: " << name();
|
|
|
|
}
|
|
|
|
|
|
bool MathMacro::completionSupported(Cursor const & cur) const
|
|
{
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
return InsetMathNest::completionSupported(cur);
|
|
|
|
return lyxrc.completion_popup_math
|
|
&& displayMode() == DISPLAY_UNFOLDED
|
|
&& cur.bv().cursor().pos() == int(name().size());
|
|
}
|
|
|
|
|
|
bool MathMacro::inlineCompletionSupported(Cursor const & cur) const
|
|
{
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
return InsetMathNest::inlineCompletionSupported(cur);
|
|
|
|
return lyxrc.completion_inline_math
|
|
&& displayMode() == DISPLAY_UNFOLDED
|
|
&& cur.bv().cursor().pos() == int(name().size());
|
|
}
|
|
|
|
|
|
bool MathMacro::automaticInlineCompletion() const
|
|
{
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
return InsetMathNest::automaticInlineCompletion();
|
|
|
|
return lyxrc.completion_inline_math;
|
|
}
|
|
|
|
|
|
bool MathMacro::automaticPopupCompletion() const
|
|
{
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
return InsetMathNest::automaticPopupCompletion();
|
|
|
|
return lyxrc.completion_popup_math;
|
|
}
|
|
|
|
|
|
CompletionList const *
|
|
MathMacro::createCompletionList(Cursor const & cur) const
|
|
{
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
return InsetMathNest::createCompletionList(cur);
|
|
|
|
return new MathCompletionList(cur.bv().cursor());
|
|
}
|
|
|
|
|
|
docstring MathMacro::completionPrefix(Cursor const & cur) const
|
|
{
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
return InsetMathNest::completionPrefix(cur);
|
|
|
|
if (!completionSupported(cur))
|
|
return docstring();
|
|
|
|
return "\\" + name();
|
|
}
|
|
|
|
|
|
bool MathMacro::insertCompletion(Cursor & cur, docstring const & s,
|
|
bool finished)
|
|
{
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
return InsetMathNest::insertCompletion(cur, s, finished);
|
|
|
|
if (!completionSupported(cur))
|
|
return false;
|
|
|
|
// append completion
|
|
docstring newName = name() + s;
|
|
asArray(newName, cell(0));
|
|
cur.bv().cursor().pos() = name().size();
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
|
|
|
// finish macro
|
|
if (finished) {
|
|
cur.bv().cursor().pop();
|
|
++cur.bv().cursor().pos();
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void MathMacro::completionPosAndDim(Cursor const & cur, int & x, int & y,
|
|
Dimension & dim) const
|
|
{
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
InsetMathNest::completionPosAndDim(cur, x, y, dim);
|
|
|
|
// get inset dimensions
|
|
dim = cur.bv().coordCache().insets().dim(this);
|
|
// FIXME: these 3 are no accurate, but should depend on the font.
|
|
// Now the popup jumps down if you enter a char with descent > 0.
|
|
dim.des += 3;
|
|
dim.asc += 3;
|
|
|
|
// and position
|
|
Point xy
|
|
= cur.bv().coordCache().insets().xy(this);
|
|
x = xy.x_;
|
|
y = xy.y_;
|
|
}
|
|
|
|
|
|
} // namespace lyx
|