2003-08-19 13:00:56 +00:00
|
|
|
/**
|
2007-04-25 16:11:45 +00:00
|
|
|
* \file MathMacro.cpp
|
2003-08-19 13:00:56 +00:00
|
|
|
* This file is part of LyX, the document processor.
|
|
|
|
* Licence details can be found in the file COPYING.
|
1999-09-27 18:44:28 +00:00
|
|
|
*
|
2003-08-19 13:00:56 +00:00
|
|
|
* \author Alejandro Aguilar Sierra
|
2008-11-14 15:58:50 +00:00
|
|
|
* \author André Pönitz
|
2007-11-01 11:13:07 +00:00
|
|
|
* \author Stefan Schimanski
|
1999-09-27 18:44:28 +00:00
|
|
|
*
|
2003-08-19 13:00:56 +00:00
|
|
|
* Full author contact details are available in file CREDITS.
|
1999-09-27 18:44:28 +00:00
|
|
|
*/
|
|
|
|
|
2003-08-02 11:30:30 +00:00
|
|
|
#include <config.h>
|
1999-09-27 18:44:28 +00:00
|
|
|
|
2007-04-25 16:11:45 +00:00
|
|
|
#include "MathMacro.h"
|
2008-03-15 00:22:54 +00:00
|
|
|
|
2008-10-07 16:24:07 +00:00
|
|
|
#include "InsetMathChar.h"
|
2008-03-15 00:22:54 +00:00
|
|
|
#include "MathCompletionList.h"
|
2006-09-17 09:14:18 +00:00
|
|
|
#include "MathExtern.h"
|
2009-07-09 15:21:21 +00:00
|
|
|
#include "MathFactory.h"
|
2006-10-22 10:15:23 +00:00
|
|
|
#include "MathStream.h"
|
2008-03-15 00:22:54 +00:00
|
|
|
#include "MathSupport.h"
|
2004-04-13 06:27:29 +00:00
|
|
|
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "Buffer.h"
|
2007-11-01 11:13:07 +00:00
|
|
|
#include "BufferView.h"
|
2008-01-14 21:53:49 +00:00
|
|
|
#include "CoordCache.h"
|
2007-04-26 14:56:30 +00:00
|
|
|
#include "Cursor.h"
|
2007-11-01 11:13:07 +00:00
|
|
|
#include "FuncStatus.h"
|
|
|
|
#include "FuncRequest.h"
|
2008-02-21 19:42:34 +00:00
|
|
|
#include "LaTeXFeatures.h"
|
2010-02-09 16:11:13 +00:00
|
|
|
#include "LyX.h"
|
2008-02-21 19:42:34 +00:00
|
|
|
#include "LyXRC.h"
|
2016-06-04 08:41:13 +00:00
|
|
|
#include "MetricsInfo.h"
|
2007-11-01 11:13:07 +00:00
|
|
|
|
2005-07-17 10:31:44 +00:00
|
|
|
#include "frontends/Painter.h"
|
1999-09-27 18:44:28 +00:00
|
|
|
|
2008-02-18 07:14:42 +00:00
|
|
|
#include "support/debug.h"
|
2013-04-25 21:27:10 +00:00
|
|
|
#include "support/gettext.h"
|
2008-06-19 09:16:05 +00:00
|
|
|
#include "support/lassert.h"
|
2014-11-14 17:18:30 +00:00
|
|
|
#include "support/lstrings.h"
|
2016-11-28 12:13:36 +00:00
|
|
|
#include "support/RefChanger.h"
|
2008-06-19 09:16:05 +00:00
|
|
|
#include "support/textutils.h"
|
2008-02-18 07:14:42 +00:00
|
|
|
|
2008-05-06 10:36:32 +00:00
|
|
|
#include <ostream>
|
2007-11-01 11:13:07 +00:00
|
|
|
#include <vector>
|
2006-10-21 00:16:43 +00:00
|
|
|
|
2014-11-14 17:18:30 +00:00
|
|
|
using namespace lyx::support;
|
2007-12-12 10:16:00 +00:00
|
|
|
using namespace std;
|
2006-08-13 22:54:59 +00:00
|
|
|
|
2007-12-12 10:16:00 +00:00
|
|
|
namespace lyx {
|
2002-02-16 15:59:55 +00:00
|
|
|
|
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
/// A proxy for the macro values
|
|
|
|
class ArgumentProxy : public InsetMath {
|
2007-04-17 16:52:43 +00:00
|
|
|
public:
|
|
|
|
///
|
2015-04-02 19:35:05 +00:00
|
|
|
ArgumentProxy(MathMacro * mathMacro, size_t idx)
|
2007-05-24 16:29:40 +00:00
|
|
|
: mathMacro_(mathMacro), idx_(idx) {}
|
2007-04-17 16:52:43 +00:00
|
|
|
///
|
2015-04-02 19:35:05 +00:00
|
|
|
ArgumentProxy(MathMacro * mathMacro, size_t idx, docstring const & def)
|
2014-12-29 20:13:42 +00:00
|
|
|
: mathMacro_(mathMacro), idx_(idx)
|
2007-11-01 11:13:07 +00:00
|
|
|
{
|
|
|
|
asArray(def, def_);
|
|
|
|
}
|
|
|
|
///
|
2015-04-02 19:35:05 +00:00
|
|
|
void setOwner(MathMacro * mathMacro) { mathMacro_ = mathMacro; }
|
|
|
|
///
|
2015-10-03 15:41:25 +00:00
|
|
|
MathMacro const * owner() { return mathMacro_; }
|
|
|
|
///
|
2009-11-15 12:30:26 +00:00
|
|
|
InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
|
2016-11-28 12:13:36 +00:00
|
|
|
/// The math data to use for display
|
|
|
|
MathData const & displayCell(BufferView const * bv) const
|
|
|
|
{
|
|
|
|
// handle default macro arguments
|
|
|
|
bool use_def_arg = !mathMacro_->editMetrics(bv)
|
|
|
|
&& mathMacro_->cell(idx_).empty();
|
|
|
|
return use_def_arg ? def_ : mathMacro_->cell(idx_);
|
|
|
|
}
|
2009-11-15 12:30:26 +00:00
|
|
|
///
|
2016-10-04 22:25:38 +00:00
|
|
|
bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const
|
2016-11-16 14:07:00 +00:00
|
|
|
{
|
2016-10-04 22:25:38 +00:00
|
|
|
// macro arguments are in macros
|
2016-11-28 12:13:36 +00:00
|
|
|
LATTEST(mathMacro_->nesting() > 0);
|
|
|
|
/// The macro nesting can change display of insets. Change it locally.
|
|
|
|
Changer chg = make_change(mi.base.macro_nesting, mathMacro_->nesting());
|
2016-10-04 22:25:38 +00:00
|
|
|
|
2016-11-28 12:13:36 +00:00
|
|
|
MathRow::Element e_beg(MathRow::BEG_ARG);
|
2016-10-04 22:25:38 +00:00
|
|
|
e_beg.macro = mathMacro_;
|
|
|
|
e_beg.ar = &mathMacro_->cell(idx_);
|
|
|
|
mrow.push_back(e_beg);
|
2016-11-16 14:07:00 +00:00
|
|
|
|
|
|
|
mathMacro_->macro()->unlock();
|
2016-11-28 12:13:36 +00:00
|
|
|
bool has_contents = displayCell(mi.base.bv).addToMathRow(mrow, mi);
|
2016-11-16 14:07:00 +00:00
|
|
|
mathMacro_->macro()->lock();
|
|
|
|
|
2016-10-04 22:25:38 +00:00
|
|
|
// if there was no contents, and the contents is editable,
|
|
|
|
// then we insert a box instead.
|
2016-11-28 12:13:36 +00:00
|
|
|
if (!has_contents && mathMacro_->nesting() == 1) {
|
|
|
|
MathRow::Element e(MathRow::BOX);
|
2016-10-04 22:25:38 +00:00
|
|
|
e.color = Color_mathline;
|
|
|
|
mrow.push_back(e);
|
|
|
|
has_contents = true;
|
|
|
|
}
|
2016-11-16 14:07:00 +00:00
|
|
|
|
2016-11-28 12:13:36 +00:00
|
|
|
MathRow::Element e_end(MathRow::END_ARG);
|
2016-10-04 22:25:38 +00:00
|
|
|
e_end.macro = mathMacro_;
|
|
|
|
e_end.ar = &mathMacro_->cell(idx_);
|
|
|
|
|
|
|
|
mrow.push_back(e_end);
|
|
|
|
|
|
|
|
return has_contents;
|
2016-11-16 14:07:00 +00:00
|
|
|
}
|
|
|
|
///
|
2016-11-28 12:13:36 +00:00
|
|
|
void metrics(MetricsInfo &, Dimension &) const {
|
|
|
|
// This should never be invoked, since ArgumentProxy insets are linearized
|
|
|
|
LATTEST(false);
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
2015-03-29 11:53:01 +00:00
|
|
|
// write(), normalize(), infoize() and infoize2() are not needed since
|
|
|
|
// MathMacro uses the definition and not the expanded cells.
|
|
|
|
///
|
2015-04-02 19:35:05 +00:00
|
|
|
void maple(MapleStream & ms) const { ms << mathMacro_->cell(idx_); }
|
2015-03-29 11:53:01 +00:00
|
|
|
///
|
2015-04-02 19:35:05 +00:00
|
|
|
void maxima(MaximaStream & ms) const { ms << mathMacro_->cell(idx_); }
|
2015-03-29 11:53:01 +00:00
|
|
|
///
|
2015-04-02 19:35:05 +00:00
|
|
|
void mathematica(MathematicaStream & ms) const { ms << mathMacro_->cell(idx_); }
|
2009-11-15 12:30:26 +00:00
|
|
|
///
|
2015-04-02 19:35:05 +00:00
|
|
|
void mathmlize(MathStream & ms) const { ms << mathMacro_->cell(idx_); }
|
2007-11-01 11:13:07 +00:00
|
|
|
///
|
2015-04-02 19:35:05 +00:00
|
|
|
void htmlize(HtmlStream & ms) const { ms << mathMacro_->cell(idx_); }
|
2010-03-30 00:18:24 +00:00
|
|
|
///
|
2015-04-02 19:35:05 +00:00
|
|
|
void octave(OctaveStream & os) const { os << mathMacro_->cell(idx_); }
|
2015-03-29 11:53:01 +00:00
|
|
|
///
|
2016-11-28 12:13:36 +00:00
|
|
|
void draw(PainterInfo &, int, int) const {
|
|
|
|
// This should never be invoked, since ArgumentProxy insets are linearized
|
|
|
|
LATTEST(false);
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
2007-04-17 16:52:43 +00:00
|
|
|
///
|
2007-11-01 11:13:07 +00:00
|
|
|
size_t idx() const { return idx_; }
|
2007-06-15 18:26:35 +00:00
|
|
|
///
|
2007-12-24 10:54:39 +00:00
|
|
|
int kerning(BufferView const * bv) const
|
2014-12-29 20:13:42 +00:00
|
|
|
{
|
2016-11-28 12:13:36 +00:00
|
|
|
return displayCell(bv).kerning(bv);
|
2007-12-24 10:54:39 +00:00
|
|
|
}
|
2007-05-28 22:27:45 +00:00
|
|
|
|
2007-04-17 16:52:43 +00:00
|
|
|
private:
|
2007-11-01 11:13:07 +00:00
|
|
|
///
|
2014-12-29 20:13:42 +00:00
|
|
|
Inset * clone() const
|
2007-11-01 11:13:07 +00:00
|
|
|
{
|
|
|
|
return new ArgumentProxy(*this);
|
|
|
|
}
|
|
|
|
///
|
2015-04-02 19:35:05 +00:00
|
|
|
MathMacro * mathMacro_;
|
2007-11-01 11:13:07 +00:00
|
|
|
///
|
2007-05-24 16:29:40 +00:00
|
|
|
size_t idx_;
|
2007-11-01 11:13:07 +00:00
|
|
|
///
|
|
|
|
MathData def_;
|
2007-04-17 16:52:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-04-02 19:35:05 +00:00
|
|
|
/// Private implementation of MathMacro
|
2015-04-02 19:20:32 +00:00
|
|
|
class MathMacro::Private {
|
|
|
|
public:
|
|
|
|
Private(Buffer * buf, docstring const & name)
|
|
|
|
: name_(name), displayMode_(DISPLAY_INIT),
|
|
|
|
expanded_(buf), definition_(buf), attachedArgsNum_(0),
|
|
|
|
optionals_(0), nextFoldMode_(true), macroBackup_(buf),
|
|
|
|
macro_(0), needsUpdate_(false), isUpdating_(false),
|
|
|
|
appetite_(9)
|
|
|
|
{
|
|
|
|
}
|
2015-04-02 19:35:05 +00:00
|
|
|
/// Update the pointers to our owner of all expanded macros.
|
|
|
|
/// This needs to be called every time a copy of the owner is created
|
|
|
|
/// (bug 9418).
|
|
|
|
void updateChildren(MathMacro * owner);
|
2015-10-03 15:41:25 +00:00
|
|
|
/// Recursively update the pointers of all expanded macros
|
|
|
|
/// appearing in the arguments of the current macro
|
|
|
|
void updateNestedChildren(MathMacro * owner, InsetMathNest * ni);
|
2015-04-02 19:20:32 +00:00
|
|
|
/// name of macro
|
|
|
|
docstring name_;
|
|
|
|
/// current display mode
|
|
|
|
DisplayMode displayMode_;
|
|
|
|
/// expanded macro with ArgumentProxies
|
|
|
|
MathData expanded_;
|
|
|
|
/// macro definition with #1,#2,.. insets
|
|
|
|
MathData definition_;
|
|
|
|
/// number of arguments that were really attached
|
|
|
|
size_t attachedArgsNum_;
|
|
|
|
/// optional argument attached? (only in DISPLAY_NORMAL mode)
|
|
|
|
size_t optionals_;
|
|
|
|
/// fold mode to be set in next metrics call?
|
|
|
|
bool nextFoldMode_;
|
|
|
|
/// if macro_ == true, then here is a copy of the macro
|
|
|
|
/// don't use it for locking
|
|
|
|
MacroData macroBackup_;
|
|
|
|
/// if macroNotFound_ == false, then here is a reference to the macro
|
|
|
|
/// this might invalidate after metrics was called
|
|
|
|
MacroData const * macro_;
|
|
|
|
///
|
|
|
|
mutable std::map<BufferView const *, bool> editing_;
|
|
|
|
///
|
|
|
|
std::string requires_;
|
|
|
|
/// update macro representation
|
|
|
|
bool needsUpdate_;
|
|
|
|
///
|
|
|
|
bool isUpdating_;
|
|
|
|
/// maximal number of arguments the macro is greedy for
|
|
|
|
size_t appetite_;
|
2016-11-28 12:13:36 +00:00
|
|
|
/// Level of nesting in macros (including this one)
|
|
|
|
int nesting_;
|
2015-04-02 19:20:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-04-02 19:35:05 +00:00
|
|
|
void MathMacro::Private::updateChildren(MathMacro * owner)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < expanded_.size(); ++i) {
|
|
|
|
ArgumentProxy * p = dynamic_cast<ArgumentProxy *>(expanded_[i].nucleus());
|
|
|
|
if (p)
|
|
|
|
p->setOwner(owner);
|
2015-10-03 15:41:25 +00:00
|
|
|
|
|
|
|
InsetMathNest * ni = expanded_[i].nucleus()->asNestInset();
|
|
|
|
if (ni)
|
|
|
|
updateNestedChildren(owner, ni);
|
2015-04-02 19:35:05 +00:00
|
|
|
}
|
2015-06-30 17:27:38 +00:00
|
|
|
|
2015-10-03 15:41:25 +00:00
|
|
|
if (macro_) {
|
|
|
|
// The macro_ pointer is updated when MathData::metrics() is
|
|
|
|
// called. However, when instant preview is on or the macro is
|
|
|
|
// not on screen, MathData::metrics() is not called and we may
|
|
|
|
// have a dangling pointer. As a safety measure, when a macro
|
|
|
|
// is copied, always let macro_ point to the backup copy of the
|
|
|
|
// MacroData structure. This backup is updated every time the
|
|
|
|
// macro is changed, so it will not become stale.
|
2015-07-05 18:44:54 +00:00
|
|
|
macro_ = ¯oBackup_;
|
2015-06-30 17:27:38 +00:00
|
|
|
}
|
2015-04-02 19:35:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-03 15:41:25 +00:00
|
|
|
void MathMacro::Private::updateNestedChildren(MathMacro * owner, InsetMathNest * ni)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < ni->nargs(); ++i) {
|
|
|
|
MathData & ar = ni->cell(i);
|
|
|
|
for (size_t j = 0; j < ar.size(); ++j) {
|
|
|
|
ArgumentProxy * ap = dynamic_cast
|
|
|
|
<ArgumentProxy *>(ar[j].nucleus());
|
|
|
|
if (ap) {
|
|
|
|
MathMacro::Private * md = ap->owner()->d;
|
|
|
|
if (md->macro_)
|
|
|
|
md->macro_ = &md->macroBackup_;
|
|
|
|
ap->setOwner(owner);
|
|
|
|
}
|
|
|
|
InsetMathNest * imn = ar[j].nucleus()->asNestInset();
|
|
|
|
if (imn)
|
|
|
|
updateNestedChildren(owner, imn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-08 11:45:46 +00:00
|
|
|
MathMacro::MathMacro(Buffer * buf, docstring const & name)
|
2015-04-02 19:20:32 +00:00
|
|
|
: InsetMathNest(buf, 0), d(new Private(buf, name))
|
2001-07-12 11:55:57 +00:00
|
|
|
{}
|
|
|
|
|
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
MathMacro::MathMacro(MathMacro const & that)
|
|
|
|
: InsetMathNest(that), d(new Private(*that.d))
|
|
|
|
{
|
2015-10-03 15:41:25 +00:00
|
|
|
setBuffer(*that.buffer_);
|
2015-04-02 19:35:05 +00:00
|
|
|
d->updateChildren(this);
|
2015-04-02 19:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MathMacro & MathMacro::operator=(MathMacro const & that)
|
|
|
|
{
|
|
|
|
if (&that == this)
|
|
|
|
return *this;
|
|
|
|
InsetMathNest::operator=(that);
|
|
|
|
*d = *that.d;
|
2015-04-02 19:35:05 +00:00
|
|
|
d->updateChildren(this);
|
2015-04-02 19:20:32 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MathMacro::~MathMacro()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-04 22:25:38 +00:00
|
|
|
bool MathMacro::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
|
2016-11-16 14:07:00 +00:00
|
|
|
{
|
|
|
|
// 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);
|
|
|
|
|
2016-11-28 12:13:36 +00:00
|
|
|
/// The macro nesting can change display of insets. Change it locally.
|
|
|
|
Changer chg = make_change(mi.base.macro_nesting, d->nesting_);
|
|
|
|
|
2016-10-04 22:25:38 +00:00
|
|
|
if (displayMode() != MathMacro::DISPLAY_NORMAL
|
|
|
|
|| d->editing_[mi.base.bv])
|
|
|
|
return InsetMath::addToMathRow(mrow, mi);
|
2016-11-16 14:07:00 +00:00
|
|
|
|
2016-11-28 12:13:36 +00:00
|
|
|
MathRow::Element e_beg(MathRow::BEG_MACRO);
|
2016-10-04 22:25:38 +00:00
|
|
|
e_beg.macro = this;
|
|
|
|
mrow.push_back(e_beg);
|
2016-11-16 14:07:00 +00:00
|
|
|
|
2016-10-04 22:25:38 +00:00
|
|
|
d->macro_->lock();
|
|
|
|
bool has_contents = d->expanded_.addToMathRow(mrow, mi);
|
|
|
|
d->macro_->unlock();
|
|
|
|
|
|
|
|
// if there was no contents and the array is editable, then we
|
|
|
|
// insert a grey box instead.
|
2016-11-14 17:01:56 +00:00
|
|
|
if (!has_contents && mi.base.macro_nesting == 1) {
|
2016-11-28 12:13:36 +00:00
|
|
|
MathRow::Element e(MathRow::BOX);
|
2016-10-04 22:25:38 +00:00
|
|
|
e.color = Color_mathmacroblend;
|
|
|
|
mrow.push_back(e);
|
|
|
|
has_contents = true;
|
2016-11-16 14:07:00 +00:00
|
|
|
}
|
2016-10-04 22:25:38 +00:00
|
|
|
|
2016-11-28 12:13:36 +00:00
|
|
|
MathRow::Element e_end(MathRow::END_MACRO);
|
2016-10-04 22:25:38 +00:00
|
|
|
e_end.macro = this;
|
|
|
|
mrow.push_back(e_end);
|
|
|
|
|
|
|
|
return has_contents;
|
2016-11-16 14:07:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-30 18:03:17 +00:00
|
|
|
Inset * MathMacro::clone() const
|
1999-09-27 18:44:28 +00:00
|
|
|
{
|
2007-11-01 11:13:07 +00:00
|
|
|
MathMacro * copy = new MathMacro(*this);
|
2015-04-02 19:20:32 +00:00
|
|
|
copy->d->needsUpdate_ = true;
|
|
|
|
//copy->d->expanded_.clear();
|
2007-11-01 11:13:07 +00:00
|
|
|
return copy;
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
2001-08-09 09:19:18 +00:00
|
|
|
|
2011-04-01 20:03:15 +00:00
|
|
|
void MathMacro::normalize(NormalStream & os) const
|
|
|
|
{
|
|
|
|
os << "[macro " << name();
|
|
|
|
for (size_t i = 0; i < nargs(); ++i)
|
|
|
|
os << ' ' << cell(i);
|
|
|
|
os << ']';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
MathMacro::DisplayMode MathMacro::displayMode() const
|
|
|
|
{
|
|
|
|
return d->displayMode_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool MathMacro::extraBraces() const
|
|
|
|
{
|
|
|
|
return d->displayMode_ == DISPLAY_NORMAL && arity() > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-22 10:15:23 +00:00
|
|
|
docstring MathMacro::name() const
|
2001-08-08 17:26:30 +00:00
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->displayMode_ == DISPLAY_UNFOLDED)
|
2007-11-01 11:13:07 +00:00
|
|
|
return asString(cell(0));
|
2008-06-19 09:16:05 +00:00
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
return d->name_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
docstring MathMacro::macroName() const
|
|
|
|
{
|
|
|
|
return d->name_;
|
2001-08-08 17:26:30 +00:00
|
|
|
}
|
1999-09-27 18:44:28 +00:00
|
|
|
|
2001-08-09 09:19:18 +00:00
|
|
|
|
2016-11-28 12:13:36 +00:00
|
|
|
int MathMacro::nesting() const
|
|
|
|
{
|
|
|
|
return d->nesting_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-17 16:23:27 +00:00
|
|
|
void MathMacro::cursorPos(BufferView const & bv,
|
|
|
|
CursorSlice const & sl, bool boundary, int & x, int & y) const
|
2005-10-05 21:19:32 +00:00
|
|
|
{
|
2006-09-16 18:11:38 +00:00
|
|
|
// We may have 0 arguments, but InsetMathNest requires at least one.
|
2005-10-05 21:19:32 +00:00
|
|
|
if (nargs() > 0)
|
2006-10-17 16:23:27 +00:00
|
|
|
InsetMathNest::cursorPos(bv, sl, boundary, x, y);
|
2005-10-05 21:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-24 10:54:39 +00:00
|
|
|
bool MathMacro::editMode(BufferView const * bv) const {
|
2007-11-01 11:13:07 +00:00
|
|
|
// find this in cursor trace
|
2007-12-24 10:54:39 +00:00
|
|
|
Cursor const & cur = bv->cursor();
|
2007-11-01 11:13:07 +00:00
|
|
|
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) {
|
2010-10-27 07:04:58 +00:00
|
|
|
InsetMath * im = cur[i].asInsetMath();
|
|
|
|
if (im) {
|
|
|
|
MathMacro const * macro = im->asMacro();
|
|
|
|
if (macro && macro->displayMode() == DISPLAY_NORMAL)
|
|
|
|
return false;
|
|
|
|
}
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ok, none found, I am the highest one
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-11-16 14:07:00 +00:00
|
|
|
MacroData const * MathMacro::macro() const
|
2015-04-02 19:20:32 +00:00
|
|
|
{
|
|
|
|
return d->macro_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-24 10:54:39 +00:00
|
|
|
bool MathMacro::editMetrics(BufferView const * bv) const
|
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
return d->editing_[bv];
|
2007-12-24 10:54:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-21 20:39:47 +00:00
|
|
|
void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
|
2002-03-25 12:11:25 +00:00
|
|
|
{
|
2016-11-28 12:13:36 +00:00
|
|
|
/// The macro nesting can change display of insets. Change it locally.
|
|
|
|
Changer chg = make_change(mi.base.macro_nesting, d->nesting_);
|
2016-10-04 22:25:38 +00:00
|
|
|
|
2007-12-24 10:54:39 +00:00
|
|
|
// set edit mode for which we will have calculated metrics. But only
|
2015-04-02 19:20:32 +00:00
|
|
|
d->editing_[mi.base.bv] = editMode(mi.base.bv);
|
2007-12-22 14:39:47 +00:00
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
// calculate new metrics according to display mode
|
2015-04-02 19:35:05 +00:00
|
|
|
if (d->displayMode_ == DISPLAY_INIT || d->displayMode_ == DISPLAY_INTERACTIVE_INIT) {
|
2016-11-23 22:05:01 +00:00
|
|
|
Changer dummy = mi.base.changeFontSet("lyxtex");
|
2007-11-01 11:13:07 +00:00
|
|
|
mathed_string_dim(mi.base.font, from_ascii("\\") + name(), dim);
|
2015-04-02 19:20:32 +00:00
|
|
|
} else if (d->displayMode_ == DISPLAY_UNFOLDED) {
|
2016-11-23 22:05:01 +00:00
|
|
|
Changer dummy = mi.base.changeFontSet("lyxtex");
|
2007-11-01 11:13:07 +00:00
|
|
|
cell(0).metrics(mi, dim);
|
|
|
|
Dimension bsdim;
|
|
|
|
mathed_string_dim(mi.base.font, from_ascii("\\"), bsdim);
|
|
|
|
dim.wid += bsdim.width() + 1;
|
2007-12-12 19:28:07 +00:00
|
|
|
dim.asc = max(bsdim.ascent(), dim.ascent());
|
|
|
|
dim.des = max(bsdim.descent(), dim.descent());
|
2016-11-14 17:01:56 +00:00
|
|
|
metricsMarkers(mi, dim);
|
2014-12-29 20:13:42 +00:00
|
|
|
} else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST
|
2015-04-02 19:20:32 +00:00
|
|
|
&& d->editing_[mi.base.bv]) {
|
2008-01-25 22:02:38 +00:00
|
|
|
// Macro will be edited in a old-style list mode here:
|
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
LBUFERR(d->macro_);
|
2008-01-25 22:02:38 +00:00
|
|
|
Dimension fontDim;
|
|
|
|
FontInfo labelFont = sane_font;
|
|
|
|
math_font_max_dim(labelFont, fontDim.asc, fontDim.des);
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
// 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;
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
Dimension defDim;
|
2015-04-02 19:20:32 +00:00
|
|
|
d->definition_.metrics(mi, defDim);
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
// add them up
|
|
|
|
dim.wid = nameDim.wid + defDim.wid;
|
|
|
|
dim.asc = max(nameDim.asc, defDim.asc);
|
|
|
|
dim.des = max(nameDim.des, defDim.des);
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
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);
|
|
|
|
}
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
// make space for box and markers, 2 pixels
|
|
|
|
dim.asc += 1;
|
|
|
|
dim.des += 1;
|
|
|
|
dim.wid += 2;
|
2016-11-14 17:01:56 +00:00
|
|
|
metricsMarkers2(mi, dim);
|
2004-04-13 13:54:58 +00:00
|
|
|
} else {
|
2015-04-02 19:20:32 +00:00
|
|
|
LBUFERR(d->macro_);
|
2007-05-28 22:27:45 +00:00
|
|
|
|
2016-11-23 22:05:01 +00:00
|
|
|
Changer dummy = (currentMode() == TEXT_MODE)
|
|
|
|
? mi.base.font.changeShape(UP_SHAPE)
|
|
|
|
: Changer();
|
|
|
|
|
2008-10-16 21:27:43 +00:00
|
|
|
// calculate metrics, hoping that all cells are seen
|
2015-04-02 19:20:32 +00:00
|
|
|
d->macro_->lock();
|
|
|
|
d->expanded_.metrics(mi, dim);
|
2008-10-16 21:27:43 +00:00
|
|
|
|
|
|
|
// otherwise do a manual metrics call
|
|
|
|
CoordCache & coords = mi.base.bv->coordCache();
|
|
|
|
for (idx_type i = 0; i < nargs(); ++i) {
|
2011-04-15 10:54:52 +00:00
|
|
|
if (!coords.getArrays().hasDim(&cell(i))) {
|
2008-10-16 21:27:43 +00:00
|
|
|
Dimension tdim;
|
|
|
|
cell(i).metrics(mi, tdim);
|
|
|
|
}
|
|
|
|
}
|
2015-04-02 19:20:32 +00:00
|
|
|
d->macro_->unlock();
|
2007-05-28 22:27:45 +00:00
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
// calculate dimension with label while editing
|
2014-12-29 20:13:42 +00:00
|
|
|
if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_INLINE_BOX
|
2015-04-02 19:20:32 +00:00
|
|
|
&& d->editing_[mi.base.bv]) {
|
2007-10-28 18:51:54 +00:00
|
|
|
FontInfo font = mi.base.font;
|
Remove a conversion to_utf8() inside FontSetChanger
This requires to change many docstrings into std::strings. The logic behind that
is that they represent a fixed set of math fonts, and therefore “string” means
here “poor man's enum” rather than text (this is consistent with MetricsBase).
Profiling of scrolling inside a document over macro-instensive areas:
Before the patch:
44,1% BufferView::updateMetrics()
-> 34,8% InsetMathHull::metrics()
-> 9,8% FontSetChanger::FontSetChanger()
28,4% BufferView::draw()
After the patch:
35,3% BufferView::updateMetrics()
-> 27,2% InsetMathHull::metrics
-> 0,4% FontSetChanger::FontSetChanger()
47,5% BufferView::draw()
FontSetChanger::FontSetChanger() is made 41x less expensive (with reference
BV::draw()) just by removing this conversion. The remaining 0,4% could be
squished by replacing the strings with a proper enum, but this is premature. Of
course, this only treats the symptoms: there is no good reason that this
function is called 45500 times over the time of 40 repaints.
2016-06-07 21:58:55 +00:00
|
|
|
augmentFont(font, "lyxtex");
|
2007-11-01 11:13:07 +00:00
|
|
|
Dimension namedim;
|
|
|
|
mathed_string_dim(font, name(), namedim);
|
|
|
|
#if 0
|
|
|
|
dim.wid += 2 + namedim.wid + 2 + 2;
|
2007-12-12 19:28:07 +00:00
|
|
|
dim.asc = max(dim.asc, namedim.asc) + 2;
|
|
|
|
dim.des = max(dim.des, namedim.des) + 2;
|
2007-11-01 11:13:07 +00:00
|
|
|
#endif
|
2007-12-12 19:28:07 +00:00
|
|
|
dim.wid = max(1 + namedim.wid + 1, 2 + dim.wid + 2);
|
2007-11-01 11:13:07 +00:00
|
|
|
dim.asc += 1 + namedim.height() + 1;
|
|
|
|
dim.des += 2;
|
2007-04-17 16:52:43 +00:00
|
|
|
}
|
2004-04-13 13:54:58 +00:00
|
|
|
}
|
1999-09-27 18:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-22 14:39:22 +00:00
|
|
|
int MathMacro::kerning(BufferView const * bv) const {
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->displayMode_ == DISPLAY_NORMAL && !d->editing_[bv])
|
|
|
|
return d->expanded_.kerning(bv);
|
2007-11-01 11:13:07 +00:00
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-29 20:13:42 +00:00
|
|
|
void MathMacro::updateMacro(MacroContext const & mc)
|
2000-02-10 17:53:36 +00:00
|
|
|
{
|
2007-12-21 20:42:46 +00:00
|
|
|
if (validName()) {
|
2015-04-02 19:20:32 +00:00
|
|
|
d->macro_ = mc.get(name());
|
|
|
|
if (d->macro_ && d->macroBackup_ != *d->macro_) {
|
|
|
|
d->macroBackup_ = *d->macro_;
|
|
|
|
d->needsUpdate_ = true;
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
2004-04-13 13:54:58 +00:00
|
|
|
} else {
|
2015-04-02 19:20:32 +00:00
|
|
|
d->macro_ = 0;
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
|
|
|
}
|
2007-05-28 22:27:45 +00:00
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
|
2014-03-05 23:45:42 +00:00
|
|
|
class MathMacro::UpdateLocker
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit UpdateLocker(MathMacro & mm) : mac(mm)
|
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
mac.d->isUpdating_ = true;
|
2014-03-05 23:45:42 +00:00
|
|
|
}
|
2015-04-02 19:20:32 +00:00
|
|
|
~UpdateLocker() { mac.d->isUpdating_ = false; }
|
2014-03-05 23:45:42 +00:00
|
|
|
private:
|
|
|
|
MathMacro & mac;
|
|
|
|
};
|
2014-11-17 10:52:14 +00:00
|
|
|
/** Avoid wrong usage of UpdateLocker.
|
|
|
|
To avoid wrong usage:
|
|
|
|
UpdateLocker(...); // wrong
|
|
|
|
UpdateLocker locker(...); // right
|
|
|
|
*/
|
|
|
|
#define UpdateLocker(x) unnamed_UpdateLocker;
|
|
|
|
// Tip gotten from Bobby Schmidt's column in C/C++ Users Journal
|
2014-03-05 23:45:42 +00:00
|
|
|
|
|
|
|
|
2011-05-07 11:57:08 +00:00
|
|
|
void MathMacro::updateRepresentation(Cursor * cur, MacroContext const & mc,
|
2016-11-28 12:13:36 +00:00
|
|
|
UpdateType utype, int nesting)
|
2007-11-01 11:13:07 +00:00
|
|
|
{
|
2014-11-14 20:30:42 +00:00
|
|
|
// block recursive calls (bug 8999)
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->isUpdating_)
|
2014-03-05 23:45:42 +00:00
|
|
|
return;
|
|
|
|
|
2014-07-05 17:12:09 +00:00
|
|
|
UpdateLocker locker(*this);
|
2014-03-05 23:45:42 +00:00
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
// known macro?
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->macro_ == 0)
|
2007-12-21 20:42:46 +00:00
|
|
|
return;
|
|
|
|
|
2016-11-28 12:13:36 +00:00
|
|
|
// remember nesting level of this macro
|
|
|
|
d->nesting_ = nesting;
|
|
|
|
|
2007-12-21 20:42:46 +00:00
|
|
|
// update requires
|
2015-04-02 19:20:32 +00:00
|
|
|
d->requires_ = d->macro_->requires();
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
if (!d->needsUpdate_
|
2010-01-11 18:25:26 +00:00
|
|
|
// non-normal mode? We are done!
|
2015-04-02 19:20:32 +00:00
|
|
|
|| (d->displayMode_ != DISPLAY_NORMAL))
|
2007-12-21 20:42:46 +00:00
|
|
|
return;
|
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
d->needsUpdate_ = false;
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-03-04 14:57:46 +00:00
|
|
|
// get default values of macro
|
2015-04-02 19:20:32 +00:00
|
|
|
vector<docstring> const & defaults = d->macro_->defaults();
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-03-04 14:57:46 +00:00
|
|
|
// create MathMacroArgumentValue objects pointing to the cells of the macro
|
|
|
|
vector<MathData> values(nargs());
|
|
|
|
for (size_t i = 0; i < nargs(); ++i) {
|
|
|
|
ArgumentProxy * proxy;
|
2014-12-29 20:13:42 +00:00
|
|
|
if (i < defaults.size())
|
2015-04-02 19:35:05 +00:00
|
|
|
proxy = new ArgumentProxy(this, i, defaults[i]);
|
2008-03-04 14:57:46 +00:00
|
|
|
else
|
2015-04-02 19:35:05 +00:00
|
|
|
proxy = new ArgumentProxy(this, i);
|
2008-03-04 14:57:46 +00:00
|
|
|
values[i].insert(0, MathAtom(proxy));
|
2008-01-14 21:53:49 +00:00
|
|
|
}
|
2008-03-04 14:57:46 +00:00
|
|
|
// expanding macro with the values
|
2014-11-14 20:30:42 +00:00
|
|
|
// Only update the argument macros if anything was expanded, otherwise
|
|
|
|
// we would get an endless loop (bug 9140). UpdateLocker does not work
|
|
|
|
// in this case, since MacroData::expand() creates new MathMacro
|
|
|
|
// objects, so this would be a different recursion path than the one
|
|
|
|
// protected by UpdateLocker.
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->macro_->expand(values, d->expanded_)) {
|
|
|
|
if (utype == OutputUpdate && !d->expanded_.empty())
|
2016-11-28 12:13:36 +00:00
|
|
|
d->expanded_.updateMacros(cur, mc, utype, nesting);
|
2014-11-14 20:30:42 +00:00
|
|
|
}
|
2009-11-15 12:30:26 +00:00
|
|
|
// get definition for list edit mode
|
2015-04-02 19:20:32 +00:00
|
|
|
docstring const & display = d->macro_->display();
|
2016-10-31 14:23:20 +00:00
|
|
|
asArray(display.empty() ? d->macro_->definition() : display,
|
|
|
|
d->definition_, Parse::QUIET);
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->displayMode_ == DISPLAY_INIT || d->displayMode_ == DISPLAY_INTERACTIVE_INIT) {
|
2016-05-23 21:30:23 +00:00
|
|
|
Changer dummy = pi.base.changeFontSet("lyxtex");
|
2008-07-05 23:29:48 +00:00
|
|
|
pi.pain.text(x, y, from_ascii("\\") + name(), pi.base.font);
|
2015-04-02 19:20:32 +00:00
|
|
|
} else if (d->displayMode_ == DISPLAY_UNFOLDED) {
|
2016-05-23 21:30:23 +00:00
|
|
|
Changer dummy = pi.base.changeFontSet("lyxtex");
|
2008-07-05 23:29:48 +00:00
|
|
|
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);
|
2008-01-25 22:02:38 +00:00
|
|
|
} else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST
|
2015-04-02 19:20:32 +00:00
|
|
|
&& d->editing_[pi.base.bv]) {
|
2008-01-25 22:02:38 +00:00
|
|
|
// Macro will be edited in a old-style list mode here:
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-10-29 08:03:26 +00:00
|
|
|
CoordCache const & coords = pi.base.bv->coordCache();
|
2008-01-25 22:02:38 +00:00
|
|
|
FontInfo const & labelFont = sane_font;
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
// markers and box needs two pixels
|
|
|
|
x += 2;
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
// get maximal font height
|
|
|
|
Dimension fontDim;
|
|
|
|
math_font_max_dim(pi.base.font, fontDim.asc, fontDim.des);
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
// 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
|
2015-04-02 19:20:32 +00:00
|
|
|
d->definition_.draw(pi, x, y);
|
|
|
|
Dimension const & defDim = coords.getArrays().dim(&d->definition_);
|
2008-01-25 22:02:38 +00:00
|
|
|
y += max(fontDim.des, defDim.des);
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
// draw parameters
|
|
|
|
docstring str = from_ascii("#9");
|
|
|
|
int strw1 = mathed_string_width(labelFont, from_ascii("#9"));
|
|
|
|
int strw2 = mathed_string_width(labelFont, from_ascii(": "));
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
for (idx_type i = 0; i < nargs(); ++i) {
|
|
|
|
// position of label
|
2008-10-29 08:03:26 +00:00
|
|
|
Dimension const & cdim = coords.getArrays().dim(&cell(i));
|
2008-01-25 22:02:38 +00:00
|
|
|
x = expx + 2;
|
|
|
|
y += max(fontDim.asc, cdim.asc) + 1;
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
// 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;
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
// draw paramter
|
|
|
|
cell(i).draw(pi, x, y);
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
// next line
|
|
|
|
y += max(fontDim.des, cdim.des);
|
|
|
|
}
|
2014-12-29 20:13:42 +00:00
|
|
|
|
|
|
|
pi.pain.rectangle(expx + 1, expy - dim.asc + 1, dim.wid - 3,
|
2008-01-25 22:02:38 +00:00
|
|
|
dim.height() - 2, Color_mathmacroframe);
|
|
|
|
drawMarkers2(pi, expx, expy);
|
2007-11-01 11:13:07 +00:00
|
|
|
} else {
|
2008-01-25 22:02:38 +00:00
|
|
|
bool drawBox = lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_INLINE_BOX;
|
2016-11-23 22:05:01 +00:00
|
|
|
Changer dummy = (currentMode() == TEXT_MODE)
|
|
|
|
? pi.base.font.changeShape(UP_SHAPE)
|
|
|
|
: Changer();
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
// warm up cells
|
2007-05-24 16:29:40 +00:00
|
|
|
for (size_t i = 0; i < nargs(); ++i)
|
|
|
|
cell(i).setXY(*pi.base.bv, x, y);
|
2007-05-28 22:27:45 +00:00
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
if (drawBox && d->editing_[pi.base.bv]) {
|
2007-11-01 11:13:07 +00:00
|
|
|
// draw header and rectangle around
|
2007-10-28 18:51:54 +00:00
|
|
|
FontInfo font = pi.base.font;
|
Remove a conversion to_utf8() inside FontSetChanger
This requires to change many docstrings into std::strings. The logic behind that
is that they represent a fixed set of math fonts, and therefore “string” means
here “poor man's enum” rather than text (this is consistent with MetricsBase).
Profiling of scrolling inside a document over macro-instensive areas:
Before the patch:
44,1% BufferView::updateMetrics()
-> 34,8% InsetMathHull::metrics()
-> 9,8% FontSetChanger::FontSetChanger()
28,4% BufferView::draw()
After the patch:
35,3% BufferView::updateMetrics()
-> 27,2% InsetMathHull::metrics
-> 0,4% FontSetChanger::FontSetChanger()
47,5% BufferView::draw()
FontSetChanger::FontSetChanger() is made 41x less expensive (with reference
BV::draw()) just by removing this conversion. The remaining 0,4% could be
squished by replacing the strings with a proper enum, but this is premature. Of
course, this only treats the symptoms: there is no good reason that this
function is called 45500 times over the time of 40 repaints.
2016-06-07 21:58:55 +00:00
|
|
|
augmentFont(font, "lyxtex");
|
2007-11-01 11:13:07 +00:00
|
|
|
font.setSize(FONT_SIZE_TINY);
|
|
|
|
font.setColor(Color_mathmacrolabel);
|
|
|
|
Dimension namedim;
|
|
|
|
mathed_string_dim(font, name(), namedim);
|
2008-01-22 21:23:41 +00:00
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
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);
|
2015-04-02 19:20:32 +00:00
|
|
|
expx += (dim.wid - d->expanded_.dimension(*pi.base.bv).width()) / 2;
|
2008-01-22 21:23:41 +00:00
|
|
|
}
|
2007-11-01 11:13:07 +00:00
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->editing_[pi.base.bv]) {
|
2007-11-01 11:13:07 +00:00
|
|
|
pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
|
2015-04-02 19:20:32 +00:00
|
|
|
d->expanded_.draw(pi, expx, expy);
|
2007-11-01 11:13:07 +00:00
|
|
|
pi.pain.leaveMonochromeMode();
|
2008-01-22 21:23:41 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
if (drawBox)
|
2014-12-29 20:13:42 +00:00
|
|
|
pi.pain.rectangle(x, y - dim.asc, dim.wid,
|
2008-01-22 21:23:41 +00:00
|
|
|
dim.height(), Color_mathmacroframe);
|
2007-11-01 11:13:07 +00:00
|
|
|
} else
|
2015-04-02 19:20:32 +00:00
|
|
|
d->expanded_.draw(pi, expx, expy);
|
2007-11-01 11:13:07 +00:00
|
|
|
|
2008-01-25 22:02:38 +00:00
|
|
|
if (!drawBox)
|
2008-01-22 21:23:41 +00:00
|
|
|
drawMarkers(pi, x, y);
|
2016-11-14 17:01:56 +00:00
|
|
|
|
2004-04-13 13:54:58 +00:00
|
|
|
}
|
2007-11-01 11:13:07 +00:00
|
|
|
|
2008-01-14 21:53:49 +00:00
|
|
|
// edit mode changed?
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->editing_[pi.base.bv] != editMode(pi.base.bv))
|
2010-07-08 20:04:35 +00:00
|
|
|
pi.base.bv->cursor().screenUpdateFlags(Update::SinglePar);
|
2001-04-24 16:13:38 +00:00
|
|
|
}
|
1999-09-27 18:44:28 +00:00
|
|
|
|
2001-04-25 15:43:57 +00:00
|
|
|
|
2005-10-05 21:19:32 +00:00
|
|
|
void MathMacro::drawSelection(PainterInfo & pi, int x, int y) const
|
|
|
|
{
|
2006-09-16 18:11:38 +00:00
|
|
|
// We may have 0 arguments, but InsetMathNest requires at least one.
|
2012-10-21 19:14:16 +00:00
|
|
|
if (!cells_.empty())
|
2006-09-16 18:11:38 +00:00
|
|
|
InsetMathNest::drawSelection(pi, x, y);
|
2005-10-05 21:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-04 14:49:03 +00:00
|
|
|
void MathMacro::setDisplayMode(MathMacro::DisplayMode mode, int appetite)
|
2007-11-01 11:13:07 +00:00
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->displayMode_ != mode) {
|
2007-11-01 11:13:07 +00:00
|
|
|
// transfer name if changing from or to DISPLAY_UNFOLDED
|
|
|
|
if (mode == DISPLAY_UNFOLDED) {
|
|
|
|
cells_.resize(1);
|
2015-04-02 19:20:32 +00:00
|
|
|
asArray(d->name_, cell(0));
|
|
|
|
} else if (d->displayMode_ == DISPLAY_UNFOLDED) {
|
|
|
|
d->name_ = asString(cell(0));
|
2007-11-01 11:13:07 +00:00
|
|
|
cells_.resize(0);
|
|
|
|
}
|
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
d->displayMode_ = mode;
|
|
|
|
d->needsUpdate_ = true;
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-03-04 14:49:03 +00:00
|
|
|
// the interactive init mode is non-greedy by default
|
|
|
|
if (appetite == -1)
|
2015-04-02 19:20:32 +00:00
|
|
|
d->appetite_ = (mode == DISPLAY_INTERACTIVE_INIT) ? 0 : 9;
|
2008-03-04 14:49:03 +00:00
|
|
|
else
|
2015-04-02 19:20:32 +00:00
|
|
|
d->appetite_ = size_t(appetite);
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-21 20:42:46 +00:00
|
|
|
MathMacro::DisplayMode MathMacro::computeDisplayMode() const
|
2007-11-01 11:13:07 +00:00
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->nextFoldMode_ == true && d->macro_ && !d->macro_->locked())
|
2007-11-01 11:13:07 +00:00
|
|
|
return DISPLAY_NORMAL;
|
|
|
|
else
|
|
|
|
return DISPLAY_UNFOLDED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool MathMacro::validName() const
|
|
|
|
{
|
|
|
|
docstring n = name();
|
|
|
|
|
2012-10-21 19:14:16 +00:00
|
|
|
if (n.empty())
|
2007-11-01 11:13:07 +00:00
|
|
|
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) {
|
2008-02-27 10:43:29 +00:00
|
|
|
if (!(n[i] >= 'a' && n[i] <= 'z')
|
|
|
|
&& !(n[i] >= 'A' && n[i] <= 'Z')
|
2014-12-29 20:13:42 +00:00
|
|
|
&& n[i] != '*')
|
2007-11-01 11:13:07 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
size_t MathMacro::arity() const
|
2015-05-17 15:27:12 +00:00
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->displayMode_ == DISPLAY_NORMAL )
|
|
|
|
return cells_.size();
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t MathMacro::optionals() const
|
|
|
|
{
|
|
|
|
return d->optionals_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MathMacro::setOptionals(int n)
|
|
|
|
{
|
|
|
|
if (n <= int(nargs()))
|
|
|
|
d->optionals_ = n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t MathMacro::appetite() const
|
|
|
|
{
|
|
|
|
return d->appetite_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-24 23:11:16 +00:00
|
|
|
InsetMath::mode_type MathMacro::currentMode() const
|
|
|
|
{
|
|
|
|
// User defined macros are always assumed to be mathmode macros.
|
|
|
|
// Only the global macros defined in lib/symbols may be textmode.
|
|
|
|
|
|
|
|
MacroData const * data = MacroTable::globalMacros().get(name());
|
|
|
|
bool textmode = data && data->symbol() && data->symbol()->extra == "textmode";
|
|
|
|
return textmode ? TEXT_MODE : MATH_MODE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-07-26 06:56:43 +00:00
|
|
|
void MathMacro::validate(LaTeXFeatures & features) const
|
2001-07-13 14:54:56 +00:00
|
|
|
{
|
2015-06-24 22:49:15 +00:00
|
|
|
// Immediately after a document is loaded, in some cases the MacroData
|
|
|
|
// of the global macros defined in the lib/symbols file may still not
|
|
|
|
// be known to the macro machinery because it will be set only after
|
|
|
|
// the first call to updateMacros(). This is not a problem unless
|
|
|
|
// instant preview is on for math, in which case we will be missing
|
|
|
|
// the corresponding requirements.
|
|
|
|
// In this case, we get the required info from the global macro table.
|
2015-04-02 19:20:32 +00:00
|
|
|
if (!d->requires_.empty())
|
|
|
|
features.require(d->requires_);
|
2015-06-24 22:49:15 +00:00
|
|
|
else if (!d->macro_) {
|
|
|
|
// Update requires for known global macros.
|
|
|
|
MacroData const * data = MacroTable::globalMacros().get(name());
|
|
|
|
if (data && !data->requires().empty())
|
|
|
|
features.require(data->requires());
|
|
|
|
}
|
2007-05-04 15:30:27 +00:00
|
|
|
|
2008-10-23 00:48:06 +00:00
|
|
|
if (name() == "binom")
|
|
|
|
features.require("binom");
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-03-26 12:55:36 +00:00
|
|
|
// validate the cells and the definition
|
|
|
|
if (displayMode() == DISPLAY_NORMAL) {
|
2015-04-02 19:20:32 +00:00
|
|
|
d->definition_.validate(features);
|
2008-03-26 12:55:36 +00:00
|
|
|
InsetMathNest::validate(features);
|
|
|
|
}
|
2001-07-13 14:54:56 +00:00
|
|
|
}
|
2001-11-16 08:26:41 +00:00
|
|
|
|
|
|
|
|
2008-02-11 08:20:13 +00:00
|
|
|
void MathMacro::edit(Cursor & cur, bool front, EntryDirection entry_from)
|
2007-11-01 11:13:07 +00:00
|
|
|
{
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
2008-02-10 19:52:45 +00:00
|
|
|
InsetMathNest::edit(cur, front, entry_from);
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-29 13:39:47 +00:00
|
|
|
Inset * MathMacro::editXY(Cursor & cur, int x, int y)
|
2005-10-05 21:19:32 +00:00
|
|
|
{
|
2006-09-16 18:11:38 +00:00
|
|
|
// We may have 0 arguments, but InsetMathNest requires at least one.
|
2007-04-17 16:49:17 +00:00
|
|
|
if (nargs() > 0) {
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
2014-12-29 20:13:42 +00:00
|
|
|
return InsetMathNest::editXY(cur, x, y);
|
2007-11-01 11:13:07 +00:00
|
|
|
} else
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-01 15:36:27 +00:00
|
|
|
void MathMacro::removeArgument(Inset::pos_type pos) {
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->displayMode_ == DISPLAY_NORMAL) {
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(size_t(pos) < cells_.size(), return);
|
2007-11-01 11:13:07 +00:00
|
|
|
cells_.erase(cells_.begin() + pos);
|
2015-04-02 19:20:32 +00:00
|
|
|
if (size_t(pos) < d->attachedArgsNum_)
|
|
|
|
--d->attachedArgsNum_;
|
|
|
|
if (size_t(pos) < d->optionals_) {
|
|
|
|
--d->optionals_;
|
2007-04-17 16:49:17 +00:00
|
|
|
}
|
2007-11-01 11:13:07 +00:00
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
d->needsUpdate_ = true;
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-01 15:36:27 +00:00
|
|
|
void MathMacro::insertArgument(Inset::pos_type pos) {
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->displayMode_ == DISPLAY_NORMAL) {
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(size_t(pos) <= cells_.size(), return);
|
2007-11-01 11:13:07 +00:00
|
|
|
cells_.insert(cells_.begin() + pos, MathData());
|
2015-04-02 19:20:32 +00:00
|
|
|
if (size_t(pos) < d->attachedArgsNum_)
|
|
|
|
++d->attachedArgsNum_;
|
|
|
|
if (size_t(pos) < d->optionals_)
|
|
|
|
++d->optionals_;
|
2007-11-01 11:13:07 +00:00
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
d->needsUpdate_ = true;
|
2007-04-17 16:49:17 +00:00
|
|
|
}
|
2005-10-05 21:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-12 19:28:07 +00:00
|
|
|
void MathMacro::detachArguments(vector<MathData> & args, bool strip)
|
2007-11-01 11:13:07 +00:00
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
LASSERT(d->displayMode_ == DISPLAY_NORMAL, return);
|
2007-11-01 11:13:07 +00:00
|
|
|
args = cells_;
|
|
|
|
|
|
|
|
// strip off empty cells, but not more than arity-attachedArgsNum_
|
|
|
|
if (strip) {
|
|
|
|
size_t i;
|
2015-04-02 19:20:32 +00:00
|
|
|
for (i = cells_.size(); i > d->attachedArgsNum_; --i)
|
2007-11-01 11:13:07 +00:00
|
|
|
if (!cell(i - 1).empty()) break;
|
|
|
|
args.resize(i);
|
|
|
|
}
|
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
d->attachedArgsNum_ = 0;
|
|
|
|
d->expanded_ = MathData();
|
2007-11-01 11:13:07 +00:00
|
|
|
cells_.resize(0);
|
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
d->needsUpdate_ = true;
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-12 19:28:07 +00:00
|
|
|
void MathMacro::attachArguments(vector<MathData> const & args, size_t arity, int optionals)
|
2007-11-01 11:13:07 +00:00
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
LASSERT(d->displayMode_ == DISPLAY_NORMAL, return);
|
2007-11-01 11:13:07 +00:00
|
|
|
cells_ = args;
|
2015-04-02 19:20:32 +00:00
|
|
|
d->attachedArgsNum_ = args.size();
|
2007-11-01 11:13:07 +00:00
|
|
|
cells_.resize(arity);
|
2015-04-02 19:20:32 +00:00
|
|
|
d->expanded_ = MathData();
|
|
|
|
d->optionals_ = optionals;
|
2007-11-01 11:13:07 +00:00
|
|
|
|
2015-04-02 19:20:32 +00:00
|
|
|
d->needsUpdate_ = true;
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-29 20:13:42 +00:00
|
|
|
bool MathMacro::idxFirst(Cursor & cur) const
|
2007-04-16 14:42:53 +00:00
|
|
|
{
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
2007-04-16 14:42:53 +00:00
|
|
|
return InsetMathNest::idxFirst(cur);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-29 20:13:42 +00:00
|
|
|
bool MathMacro::idxLast(Cursor & cur) const
|
2007-04-16 14:42:53 +00:00
|
|
|
{
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
2007-04-16 14:42:53 +00:00
|
|
|
return InsetMathNest::idxLast(cur);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-22 20:45:18 +00:00
|
|
|
bool MathMacro::notifyCursorLeaves(Cursor const & old, Cursor & cur)
|
2007-06-14 20:57:56 +00:00
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->displayMode_ == DISPLAY_UNFOLDED) {
|
2009-07-09 15:21:21 +00:00
|
|
|
docstring const & unfolded_name = name();
|
2015-04-02 19:20:32 +00:00
|
|
|
if (unfolded_name != d->name_) {
|
2009-07-09 15:21:21 +00:00
|
|
|
// The macro name was changed
|
2009-11-16 13:31:01 +00:00
|
|
|
Cursor inset_cursor = old;
|
|
|
|
int macroSlice = inset_cursor.find(this);
|
2013-04-25 21:27:10 +00:00
|
|
|
// returning true means the cursor is "now" invalid,
|
|
|
|
// which it was.
|
|
|
|
LASSERT(macroSlice != -1, return true);
|
2009-11-16 13:31:01 +00:00
|
|
|
inset_cursor.cutOff(macroSlice);
|
|
|
|
inset_cursor.recordUndoInset();
|
|
|
|
inset_cursor.pop();
|
|
|
|
inset_cursor.cell().erase(inset_cursor.pos());
|
|
|
|
inset_cursor.cell().insert(inset_cursor.pos(),
|
2009-11-15 23:54:45 +00:00
|
|
|
createInsetMath(unfolded_name, cur.buffer()));
|
2010-10-28 11:13:47 +00:00
|
|
|
cur.resetAnchor();
|
2010-10-13 17:28:55 +00:00
|
|
|
cur.screenUpdateFlags(cur.result().screenUpdate() | Update::SinglePar);
|
2009-07-09 15:21:21 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::Force);
|
2008-02-22 20:45:18 +00:00
|
|
|
return InsetMathNest::notifyCursorLeaves(old, cur);
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MathMacro::fold(Cursor & cur)
|
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
if (!d->nextFoldMode_) {
|
|
|
|
d->nextFoldMode_ = true;
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
2007-06-14 20:57:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
void MathMacro::unfold(Cursor & cur)
|
2007-04-16 14:42:53 +00:00
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->nextFoldMode_) {
|
|
|
|
d->nextFoldMode_ = false;
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
2007-04-16 14:42:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
bool MathMacro::folded() const
|
2001-11-16 08:26:41 +00:00
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
return d->nextFoldMode_;
|
2001-11-16 08:26:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
void MathMacro::write(WriteStream & os) const
|
2001-11-16 08:29:11 +00:00
|
|
|
{
|
2016-09-25 18:31:13 +00:00
|
|
|
MacroData const * data = MacroTable::globalMacros().get(name());
|
|
|
|
bool textmode_macro = data && data->symbol()
|
|
|
|
&& data->symbol()->extra == "textmode";
|
|
|
|
bool needs_mathmode = data && (!data->symbol()
|
|
|
|
|| data->symbol()->extra != "textmode");
|
2016-09-10 16:32:44 +00:00
|
|
|
|
|
|
|
MathEnsurer ensurer(os, needs_mathmode, true, textmode_macro);
|
2008-06-16 01:21:17 +00:00
|
|
|
|
2007-12-21 20:42:46 +00:00
|
|
|
// non-normal mode
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->displayMode_ != DISPLAY_NORMAL) {
|
2008-06-16 01:21:17 +00:00
|
|
|
os << "\\" << name();
|
2008-06-19 09:16:05 +00:00
|
|
|
if (name().size() != 1 || isAlphaASCII(name()[0]))
|
|
|
|
os.pendingSpace(true);
|
2007-12-21 20:42:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// normal mode
|
2013-04-25 21:27:10 +00:00
|
|
|
// we should be ok to continue even if this fails.
|
2015-04-02 19:20:32 +00:00
|
|
|
LATTEST(d->macro_);
|
2007-12-21 20:42:46 +00:00
|
|
|
|
Fix display and output of math macros with optional arguments
This is a long standing issue, present since the new math macros
inception in version 1.6. It manifests as a display issue when a
macro with optional arguments appears in the optional argument of
another macro. In this case the display is messed up and it is
difficult, if not impossible, changing the arguments as they do not
appear on screen as related to a specific macro instance. It also
manifests as latex errors when compiling, even if the latex output
is formally correct, due to limitations of the xargs package used
to output the macros. Most probably, both aspects have the same
root cause, as simply enclosing in braces the macro and its
parameters solves both issues. However, when reloading a document,
lyx strips the outer braces enclosing a macro argument, thus
frustrating this possible workaround.
This commit solves the display issue by correctly accounting for
macros with optional arguments nested in the argument of another
macro, and circumvents the xargs package limitations causing errors
by enclosing in braces the macros with optional arguments appearing
in the argument of an outer macro when they are output. This means
that when loading an old document with such macros and saving it
again, the macro representation is updated and will have these
additional braces. However, as such braces are stripped by lyx on
loading, there is no risk that they accumulate.
See also this thread:
http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg197828.html
2016-12-01 17:02:47 +00:00
|
|
|
// We may already be in the argument of a macro
|
|
|
|
bool const inside_macro = os.insideMacro();
|
|
|
|
os.insideMacro(true);
|
|
|
|
|
|
|
|
// Enclose in braces to avoid latex errors with xargs if we have
|
|
|
|
// optional arguments and are in the optional argument of a macro
|
|
|
|
if (d->optionals_ && inside_macro)
|
|
|
|
os << '{';
|
|
|
|
|
2014-02-22 15:30:55 +00:00
|
|
|
// Always protect macros in a fragile environment
|
|
|
|
if (os.fragile())
|
2007-12-21 20:43:50 +00:00
|
|
|
os << "\\protect";
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2007-12-21 20:42:46 +00:00
|
|
|
os << "\\" << name();
|
|
|
|
bool first = true;
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2007-12-23 16:31:37 +00:00
|
|
|
// Optional arguments:
|
|
|
|
// First find last non-empty optional argument
|
|
|
|
idx_type emptyOptFrom = 0;
|
|
|
|
idx_type i = 0;
|
2015-04-02 19:20:32 +00:00
|
|
|
for (; i < cells_.size() && i < d->optionals_; ++i) {
|
2007-12-23 16:31:37 +00:00
|
|
|
if (!cell(i).empty())
|
|
|
|
emptyOptFrom = i + 1;
|
|
|
|
}
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2007-12-23 16:31:37 +00:00
|
|
|
// print out optionals
|
|
|
|
for (i=0; i < cells_.size() && i < emptyOptFrom; ++i) {
|
|
|
|
first = false;
|
|
|
|
os << "[" << cell(i) << "]";
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2007-12-23 16:31:37 +00:00
|
|
|
// skip the tailing empty optionals
|
2015-04-02 19:20:32 +00:00
|
|
|
i = d->optionals_;
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2009-11-15 12:30:26 +00:00
|
|
|
// Print remaining arguments
|
2007-12-21 20:42:46 +00:00
|
|
|
for (; i < cells_.size(); ++i) {
|
2014-12-29 20:13:42 +00:00
|
|
|
if (cell(i).size() == 1
|
2008-10-07 16:24:07 +00:00
|
|
|
&& cell(i)[0].nucleus()->asCharInset()
|
2012-12-30 10:58:21 +00:00
|
|
|
&& isASCII(cell(i)[0].nucleus()->asCharInset()->getChar())) {
|
2007-12-21 20:42:46 +00:00
|
|
|
if (first)
|
|
|
|
os << " ";
|
|
|
|
os << cell(i);
|
|
|
|
} else
|
|
|
|
os << "{" << cell(i) << "}";
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
|
Fix display and output of math macros with optional arguments
This is a long standing issue, present since the new math macros
inception in version 1.6. It manifests as a display issue when a
macro with optional arguments appears in the optional argument of
another macro. In this case the display is messed up and it is
difficult, if not impossible, changing the arguments as they do not
appear on screen as related to a specific macro instance. It also
manifests as latex errors when compiling, even if the latex output
is formally correct, due to limitations of the xargs package used
to output the macros. Most probably, both aspects have the same
root cause, as simply enclosing in braces the macro and its
parameters solves both issues. However, when reloading a document,
lyx strips the outer braces enclosing a macro argument, thus
frustrating this possible workaround.
This commit solves the display issue by correctly accounting for
macros with optional arguments nested in the argument of another
macro, and circumvents the xargs package limitations causing errors
by enclosing in braces the macros with optional arguments appearing
in the argument of an outer macro when they are output. This means
that when loading an old document with such macros and saving it
again, the macro representation is updated and will have these
additional braces. However, as such braces are stripped by lyx on
loading, there is no risk that they accumulate.
See also this thread:
http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg197828.html
2016-12-01 17:02:47 +00:00
|
|
|
// Close the opened brace or add space if there was no argument
|
|
|
|
if (d->optionals_ && inside_macro)
|
|
|
|
os << '}';
|
|
|
|
else if (first)
|
2007-12-21 20:42:46 +00:00
|
|
|
os.pendingSpace(true);
|
Fix display and output of math macros with optional arguments
This is a long standing issue, present since the new math macros
inception in version 1.6. It manifests as a display issue when a
macro with optional arguments appears in the optional argument of
another macro. In this case the display is messed up and it is
difficult, if not impossible, changing the arguments as they do not
appear on screen as related to a specific macro instance. It also
manifests as latex errors when compiling, even if the latex output
is formally correct, due to limitations of the xargs package used
to output the macros. Most probably, both aspects have the same
root cause, as simply enclosing in braces the macro and its
parameters solves both issues. However, when reloading a document,
lyx strips the outer braces enclosing a macro argument, thus
frustrating this possible workaround.
This commit solves the display issue by correctly accounting for
macros with optional arguments nested in the argument of another
macro, and circumvents the xargs package limitations causing errors
by enclosing in braces the macros with optional arguments appearing
in the argument of an outer macro when they are output. This means
that when loading an old document with such macros and saving it
again, the macro representation is updated and will have these
additional braces. However, as such braces are stripped by lyx on
loading, there is no risk that they accumulate.
See also this thread:
http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg197828.html
2016-12-01 17:02:47 +00:00
|
|
|
|
|
|
|
os.insideMacro(inside_macro);
|
2001-11-16 08:29:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
void MathMacro::maple(MapleStream & os) const
|
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
lyx::maple(d->expanded_, os);
|
2007-11-01 11:13:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-29 11:53:01 +00:00
|
|
|
void MathMacro::maxima(MaximaStream & os) const
|
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
lyx::maxima(d->expanded_, os);
|
2015-03-29 11:53:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MathMacro::mathematica(MathematicaStream & os) const
|
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
lyx::mathematica(d->expanded_, os);
|
2015-03-29 11:53:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-31 15:46:39 +00:00
|
|
|
void MathMacro::mathmlize(MathStream & os) const
|
2001-11-16 08:29:11 +00:00
|
|
|
{
|
2015-02-16 21:33:48 +00:00
|
|
|
// macro_ is 0 if this is an unknown macro
|
2015-04-02 19:20:32 +00:00
|
|
|
LATTEST(d->macro_ || d->displayMode_ != DISPLAY_NORMAL);
|
|
|
|
if (d->macro_) {
|
|
|
|
docstring const xmlname = d->macro_->xmlname();
|
2014-12-29 20:13:42 +00:00
|
|
|
if (!xmlname.empty()) {
|
2015-04-02 19:20:32 +00:00
|
|
|
char const * type = d->macro_->MathMLtype();
|
2016-05-22 15:32:04 +00:00
|
|
|
os << '<' << type << "> " << xmlname << " </"
|
2014-12-29 20:13:42 +00:00
|
|
|
<< type << '>';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->expanded_.empty()) {
|
2011-04-01 20:15:43 +00:00
|
|
|
// this means that we do not recognize the macro
|
|
|
|
throw MathExportException();
|
|
|
|
}
|
2015-04-02 19:20:32 +00:00
|
|
|
os << d->expanded_;
|
2001-11-16 08:26:41 +00:00
|
|
|
}
|
2001-12-11 11:33:43 +00:00
|
|
|
|
|
|
|
|
2010-03-30 00:18:24 +00:00
|
|
|
void MathMacro::htmlize(HtmlStream & os) const
|
|
|
|
{
|
2015-02-16 21:33:48 +00:00
|
|
|
// macro_ is 0 if this is an unknown macro
|
2015-04-02 19:20:32 +00:00
|
|
|
LATTEST(d->macro_ || d->displayMode_ != DISPLAY_NORMAL);
|
|
|
|
if (d->macro_) {
|
|
|
|
docstring const xmlname = d->macro_->xmlname();
|
2014-12-29 20:13:42 +00:00
|
|
|
if (!xmlname.empty()) {
|
|
|
|
os << ' ' << xmlname << ' ';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2015-04-02 19:20:32 +00:00
|
|
|
if (d->expanded_.empty()) {
|
2011-04-01 20:15:43 +00:00
|
|
|
// this means that we do not recognize the macro
|
|
|
|
throw MathExportException();
|
|
|
|
}
|
2015-04-02 19:20:32 +00:00
|
|
|
os << d->expanded_;
|
2010-03-30 00:18:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-01 11:13:07 +00:00
|
|
|
void MathMacro::octave(OctaveStream & os) const
|
2007-04-17 16:49:17 +00:00
|
|
|
{
|
2015-04-02 19:20:32 +00:00
|
|
|
lyx::octave(d->expanded_, os);
|
2007-04-17 16:49:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-22 10:15:23 +00:00
|
|
|
void MathMacro::infoize(odocstream & os) const
|
2003-01-07 11:24:43 +00:00
|
|
|
{
|
2014-11-14 17:18:30 +00:00
|
|
|
os << bformat(_("Macro: %1$s"), name());
|
2003-01-07 11:24:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-22 10:15:23 +00:00
|
|
|
void MathMacro::infoize2(odocstream & os) const
|
2003-01-07 11:24:43 +00:00
|
|
|
{
|
2014-11-14 17:18:30 +00:00
|
|
|
os << bformat(_("Macro: %1$s"), name());
|
2003-01-07 11:24:43 +00:00
|
|
|
}
|
2006-10-21 00:16:43 +00:00
|
|
|
|
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
bool MathMacro::completionSupported(Cursor const & cur) const
|
|
|
|
{
|
2008-02-21 19:45:36 +00:00
|
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
|
|
return InsetMathNest::completionSupported(cur);
|
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
return lyxrc.completion_popup_math
|
|
|
|
&& displayMode() == DISPLAY_UNFOLDED
|
|
|
|
&& cur.bv().cursor().pos() == int(name().size());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool MathMacro::inlineCompletionSupported(Cursor const & cur) const
|
|
|
|
{
|
2008-02-21 19:45:36 +00:00
|
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
|
|
return InsetMathNest::inlineCompletionSupported(cur);
|
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
return lyxrc.completion_inline_math
|
|
|
|
&& displayMode() == DISPLAY_UNFOLDED
|
|
|
|
&& cur.bv().cursor().pos() == int(name().size());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool MathMacro::automaticInlineCompletion() const
|
|
|
|
{
|
2008-02-21 19:45:36 +00:00
|
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
|
|
return InsetMathNest::automaticInlineCompletion();
|
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
return lyxrc.completion_inline_math;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool MathMacro::automaticPopupCompletion() const
|
|
|
|
{
|
2008-02-21 19:45:36 +00:00
|
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
|
|
return InsetMathNest::automaticPopupCompletion();
|
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
return lyxrc.completion_popup_math;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-29 20:13:42 +00:00
|
|
|
CompletionList const *
|
2008-02-22 21:11:19 +00:00
|
|
|
MathMacro::createCompletionList(Cursor const & cur) const
|
2008-02-21 19:42:34 +00:00
|
|
|
{
|
2008-02-21 19:45:36 +00:00
|
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
2008-02-22 21:11:19 +00:00
|
|
|
return InsetMathNest::createCompletionList(cur);
|
2008-02-21 19:45:36 +00:00
|
|
|
|
2008-02-21 23:36:02 +00:00
|
|
|
return new MathCompletionList(cur.bv().cursor());
|
2008-02-21 19:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
docstring MathMacro::completionPrefix(Cursor const & cur) const
|
|
|
|
{
|
2008-02-21 19:45:36 +00:00
|
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
|
|
return InsetMathNest::completionPrefix(cur);
|
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
if (!completionSupported(cur))
|
|
|
|
return docstring();
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
return "\\" + name();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool MathMacro::insertCompletion(Cursor & cur, docstring const & s,
|
|
|
|
bool finished)
|
|
|
|
{
|
2008-02-21 19:45:36 +00:00
|
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
|
|
return InsetMathNest::insertCompletion(cur, s, finished);
|
|
|
|
|
|
|
|
if (!completionSupported(cur))
|
2008-02-21 19:42:34 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// append completion
|
|
|
|
docstring newName = name() + s;
|
|
|
|
asArray(newName, cell(0));
|
|
|
|
cur.bv().cursor().pos() = name().size();
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
// finish macro
|
|
|
|
if (finished) {
|
|
|
|
cur.bv().cursor().pop();
|
|
|
|
++cur.bv().cursor().pos();
|
2010-07-08 20:04:35 +00:00
|
|
|
cur.screenUpdateFlags(Update::SinglePar);
|
2008-02-21 19:42:34 +00:00
|
|
|
}
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MathMacro::completionPosAndDim(Cursor const & cur, int & x, int & y,
|
|
|
|
Dimension & dim) const
|
|
|
|
{
|
2008-02-21 19:45:36 +00:00
|
|
|
if (displayMode() != DISPLAY_UNFOLDED)
|
|
|
|
InsetMathNest::completionPosAndDim(cur, x, y, dim);
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
// get inset dimensions
|
|
|
|
dim = cur.bv().coordCache().insets().dim(this);
|
2008-02-21 19:45:05 +00:00
|
|
|
// 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;
|
2014-12-29 20:13:42 +00:00
|
|
|
|
2008-02-21 19:45:05 +00:00
|
|
|
// and position
|
2008-02-21 19:42:34 +00:00
|
|
|
Point xy
|
|
|
|
= cur.bv().coordCache().insets().xy(this);
|
|
|
|
x = xy.x_;
|
|
|
|
y = xy.y_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
} // namespace lyx
|