lyx_mirror/src/mathed/MathMacro.cpp
Abdelrazak Younes 9abb7db468 getting rid of superfluous std:: statements.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22104 a592a061-630c-0410-9148-cb99ea01b6c8
2007-12-12 19:28:07 +00:00

617 lines
14 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 "MathSupport.h"
#include "MathExtern.h"
#include "MathStream.h"
#include "Buffer.h"
#include "BufferView.h"
#include "Cursor.h"
#include "support/debug.h"
#include "LaTeXFeatures.h"
#include "FuncStatus.h"
#include "FuncRequest.h"
#include "Undo.h"
#include "frontends/Painter.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_);
}
///
void metrics(MetricsInfo & mi, Dimension & dim) const {
mathMacro_.macro()->unlock();
mathMacro_.cell(idx_).metrics(mi, dim);
if (!mathMacro_.editing() && !def_.empty())
def_.metrics(mi, dim);
mathMacro_.macro()->lock();
}
///
void draw(PainterInfo & pi, int x, int y) const {
if (mathMacro_.editing()) {
// 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, in the case of "editing_ == true" 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 (def_.empty())
mathMacro_.cell(idx_).draw(pi, x, y);
else {
mathMacro_.cell(idx_).setXY(*pi.base.bv, x, y);
def_.draw(pi, x, y);
}
}
}
///
size_t idx() const { return idx_; }
///
int kerning() const { return mathMacro_.cell(idx_).kerning(); }
private:
///
Inset * clone() const
{
return new ArgumentProxy(*this);
}
///
MathMacro & mathMacro_;
///
size_t idx_;
///
MathData def_;
};
MathMacro::MathMacro(docstring const & name)
: InsetMathNest(0), name_(name), displayMode_(DISPLAY_INIT),
attachedArgsNum_(0), previousCurIdx_(-1),
optionals_(0), nextFoldMode_(true),
macro_(0), editing_(false), needsUpdate_(false)
{}
Inset * MathMacro::clone() const
{
MathMacro * copy = new MathMacro(*this);
copy->needsUpdate_ = true;
copy->expanded_.cell(0).clear();
return copy;
}
docstring MathMacro::name() const
{
if (displayMode_ == DISPLAY_UNFOLDED)
return asString(cell(0));
else
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);
}
int MathMacro::cursorIdx(Cursor const & cur) const {
for (size_t i = 0; i != cur.depth(); ++i)
if (&cur[i].inset() == this)
return cur[i].idx();
return -1;
}
bool MathMacro::editMode(Cursor const & cur) const {
// find this in cursor trace
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) {
MathMacro const * macro = dynamic_cast<MathMacro const *>(&cur[i].inset());
if (macro && macro->displayMode() == DISPLAY_NORMAL)
return false;
}
// ok, none found, I am the highest one
return true;
}
return false;
}
void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
{
// 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 {
BOOST_ASSERT(macro_ != 0);
// calculate metric finally
macro_->lock();
expanded_.cell(0).metrics(mi, dim);
macro_->unlock();
// calculate dimension with label while editing
if (editing_) {
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;
}
}
// Cache the inset dimension.
setDimCache(mi, dim);
}
int MathMacro::kerning() const {
if (displayMode_ == DISPLAY_NORMAL && !editing_)
return expanded_.kerning();
else
return 0;
}
void MathMacro::updateMacro(MetricsInfo & mi)
{
if (validName() && mi.macrocontext.has(name())) {
macro_ = &mi.macrocontext.get(name());
if (macroBackup_ != *macro_) {
macroBackup_ = *macro_;
needsUpdate_ = true;
}
} else {
macro_ = 0;
}
}
void MathMacro::updateRepresentation(MetricsInfo & mi)
{
// index of child where the cursor is (or -1 if none is edited)
int curIdx = cursorIdx(mi.base.bv->cursor());
previousCurIdx_ = curIdx;
// known macro?
if (macro_) {
requires_ = macro_->requires();
if (displayMode_ == DISPLAY_NORMAL) {
// set edit mode to draw box around if needed
bool prevEditing = editing_;
editing_ = editMode(mi.base.bv->cursor());
// editMode changed and we have to switch default value and hole of optional?
if (optionals_ > 0 && nargs() > 0 &&
prevEditing != editing_)
needsUpdate_ = true;
// macro changed?
if (needsUpdate_) {
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) {
if (!cell(i).empty() || i >= defaults.size() ||
defaults[i].empty() || curIdx == (int)i)
values[i].insert(0, MathAtom(new ArgumentProxy(*this, i)));
else
values[i].insert(0, MathAtom(new ArgumentProxy(*this, i, defaults[i])));
}
// expanding macro with the values
macro_->expand(values, expanded_.cell(0));
}
}
}
}
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) {
PainterInfo pi2(pi.base.bv, pi.pain);
pi2.base.font.setColor(macro_ ? Color_latex : Color_error);
//pi2.base.style = LM_ST_TEXT;
pi2.pain.text(x, y, from_ascii("\\") + name(), pi2.base.font);
} else if (displayMode_ == DISPLAY_UNFOLDED) {
PainterInfo pi2(pi.base.bv, pi.pain);
pi2.base.font.setColor(macro_ ? Color_latex : Color_error);
//pi2.base.style = LM_ST_TEXT;
pi2.pain.text(x, y, from_ascii("\\"), pi2.base.font);
x += mathed_string_width(pi2.base.font, from_ascii("\\")) + 1;
cell(0).draw(pi2, x, y);
drawMarkers(pi2, expx, expy);
} else {
// warm up cells
for (size_t i = 0; i < nargs(); ++i)
cell(i).setXY(*pi.base.bv, x, y);
if (editing_) {
// 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);
#if 0
pi.pain.fillRectangle(x, y - dim.asc, 2 + namedim.width() + 2, dim.height(), Color_mathmacrobg);
pi.pain.text(x + 2, y, name(), font);
expx += 2 + namew + 2;
#endif
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;
pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
expanded_.cell(0).draw(pi, expx, expy);
pi.pain.leaveMonochromeMode();
} else
expanded_.cell(0).draw(pi, expx, expy);
// draw frame while editing
if (editing_)
pi.pain.rectangle(x, y - dim.asc, dim.wid, dim.height(), Color_mathmacroframe);
}
// another argument selected?
idx_type curIdx = cursorIdx(pi.base.bv->cursor());
if (previousCurIdx_ != curIdx || editing_ != editMode(pi.base.bv->cursor()))
pi.base.bv->cursor().updateFlags(Update::Force);
}
void MathMacro::drawSelection(PainterInfo & pi, int x, int y) const
{
// We may have 0 arguments, but InsetMathNest requires at least one.
if (cells_.size() > 0)
InsetMathNest::drawSelection(pi, x, y);
}
void MathMacro::setDisplayMode(MathMacro::DisplayMode mode)
{
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;
}
}
MathMacro::DisplayMode MathMacro::computeDisplayMode(MetricsInfo const &) const
{
if (nextFoldMode_ == true && macro_ && !macro_->locked())
return DISPLAY_NORMAL;
else
return DISPLAY_UNFOLDED;
}
bool MathMacro::validName() const
{
docstring n = name();
// empty name?
if (n.size() == 0)
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'))
return false;
}
return true;
}
void MathMacro::validate(LaTeXFeatures & features) const
{
if (!requires_.empty())
features.require(requires_);
if (name() == "binom" || name() == "mathcircumflex")
features.require(to_utf8(name()));
}
void MathMacro::edit(Cursor & cur, bool left)
{
cur.updateFlags(Update::Force);
InsetMathNest::edit(cur, left);
}
Inset * MathMacro::editXY(Cursor & cur, int x, int y)
{
// We may have 0 arguments, but InsetMathNest requires at least one.
if (nargs() > 0) {
cur.updateFlags(Update::Force);
return InsetMathNest::editXY(cur, x, y);
} else
return this;
}
void MathMacro::removeArgument(Inset::pos_type pos) {
if (displayMode_ == DISPLAY_NORMAL) {
BOOST_ASSERT(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) {
BOOST_ASSERT(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)
{
BOOST_ASSERT(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)
{
BOOST_ASSERT(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.updateFlags(Update::Force);
return InsetMathNest::idxFirst(cur);
}
bool MathMacro::idxLast(Cursor & cur) const
{
cur.updateFlags(Update::Force);
return InsetMathNest::idxLast(cur);
}
bool MathMacro::notifyCursorLeaves(Cursor & cur)
{
cur.updateFlags(Update::Force);
return InsetMathNest::notifyCursorLeaves(cur);
}
void MathMacro::fold(Cursor & cur)
{
if (!nextFoldMode_) {
nextFoldMode_ = true;
cur.updateFlags(Update::Force);
}
}
void MathMacro::unfold(Cursor & cur)
{
if (nextFoldMode_) {
nextFoldMode_ = false;
cur.updateFlags(Update::Force);
}
}
bool MathMacro::folded() const
{
return nextFoldMode_;
}
void MathMacro::write(WriteStream & os) const
{
if (displayMode_ == DISPLAY_NORMAL) {
BOOST_ASSERT(macro_);
os << "\\" << name();
bool first = true;
idx_type i = 0;
// Use macroBackup_ instead of macro_ here, because
// this is outside the metrics/draw calls, hence the macro_
// variable can point to a MacroData which was freed already.
vector<docstring> const & defaults = macroBackup_.defaults();
// Optional argument
if (os.latex()) {
if (i < optionals_) {
// the first real optional, the others are non-optional in latex
if (!cell(i).empty()) {
first = false;
os << "[" << cell(0) << "]";
}
++i;
}
} else {
// In lyx mode print all in any case
for (; i < cells_.size() && i < optionals_; ++i) {
first = false;
os << "[" << cell(i) << "]";
}
}
for (; i < cells_.size(); ++i) {
if (cell(i).empty() && i < optionals_) {
os << "{" << defaults[i] << "}";
} else if (cell(i).size() == 1 && cell(i)[0].nucleus()->asCharInset()) {
if (first)
os << " ";
os << cell(i);
} else
os << "{" << cell(i) << "}";
first = false;
}
if (first)
os.pendingSpace(true);
} else {
os << "\\" << name() << " ";
os.pendingSpace(true);
}
}
void MathMacro::maple(MapleStream & os) const
{
lyx::maple(expanded_.cell(0), os);
}
void MathMacro::mathmlize(MathStream & os) const
{
lyx::mathmlize(expanded_.cell(0), os);
}
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();
}
} // namespace lyx