#include "MathCompletionList.h"
#include "MathExtern.h"
#include "MathFactory.h"
+#include "MathRow.h"
#include "MathStream.h"
#include "MathSupport.h"
#include "support/gettext.h"
#include "support/lassert.h"
#include "support/lstrings.h"
-#include "support/RefChanger.h"
+#include "support/Changer.h"
#include "support/textutils.h"
#include <ostream>
asArray(def, def_);
}
///
- void setBuffer(Buffer & buffer)
+ void setBuffer(Buffer & buffer) override
{
Inset::setBuffer(buffer);
def_.setBuffer(buffer);
///
InsetMathMacro const * owner() { return mathMacro_; }
///
- marker_type marker(BufferView const *) const { return NO_MARKER; }
+ marker_type marker(BufferView const *) const override { return marker_type::NO_MARKER; }
///
- InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
+ InsetCode lyxCode() const override { return ARGUMENT_PROXY_CODE; }
/// The math data to use for display
MathData const & displayCell(BufferView const * bv) const
{
return use_def_arg ? def_ : mathMacro_->cell(idx_);
}
///
- bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const
+ bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const override
{
// macro arguments are in macros
LATTEST(mathMacro_->nesting() > 0);
/// The macro nesting can change display of insets. Change it locally.
- Changer chg = make_change(mi.base.macro_nesting,
+ Changer chg = changeVar(mi.base.macro_nesting,
mathMacro_->nesting() == 1 ? 0 : mathMacro_->nesting());
MathRow::Element e_beg(mi, MathRow::BEGIN);
return has_contents;
}
///
- void beforeMetrics() const
+ void beforeMetrics() const override
{
mathMacro_->macro()->unlock();
}
///
- void afterMetrics() const
+ void afterMetrics() const override
{
mathMacro_->macro()->lock();
}
///
- void beforeDraw(PainterInfo const & pi) const
+ void beforeDraw(PainterInfo const & pi) const override
{
// if the macro is being edited, then the painter is in
// monochrome mode.
pi.pain.leaveMonochromeMode();
}
///
- void afterDraw(PainterInfo const & pi) const
+ void afterDraw(PainterInfo const & pi) const override
{
if (mathMacro_->editMetrics(pi.base.bv))
- pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
+ pi.pain.enterMonochromeMode(Color_mathmacroblend);
}
///
- void metrics(MetricsInfo &, Dimension &) const {
+ void metrics(MetricsInfo &, Dimension &) const override {
// This should never be invoked, since InsetArgumentProxy insets are linearized
LATTEST(false);
}
///
- void draw(PainterInfo &, int, int) const {
+ void draw(PainterInfo &, int, int) const override {
// This should never be invoked, since InsetArgumentProxy insets are linearized
LATTEST(false);
}
///
- int kerning(BufferView const * bv) const
+ int kerning(BufferView const * bv) const override
{
return displayCell(bv).kerning(bv);
}
// write(), normalize(), infoize() and infoize2() are not needed since
// InsetMathMacro uses the definition and not the expanded cells.
///
- void maple(MapleStream & ms) const { ms << mathMacro_->cell(idx_); }
+ void maple(MapleStream & ms) const override { ms << mathMacro_->cell(idx_); }
///
- void maxima(MaximaStream & ms) const { ms << mathMacro_->cell(idx_); }
+ void maxima(MaximaStream & ms) const override { ms << mathMacro_->cell(idx_); }
///
- void mathematica(MathematicaStream & ms) const { ms << mathMacro_->cell(idx_); }
+ void mathematica(MathematicaStream & ms) const override { ms << mathMacro_->cell(idx_); }
///
- void mathmlize(MathStream & ms) const { ms << mathMacro_->cell(idx_); }
+ void mathmlize(MathMLStream & ms) const override { ms << mathMacro_->cell(idx_); }
///
- void htmlize(HtmlStream & ms) const { ms << mathMacro_->cell(idx_); }
+ void htmlize(HtmlStream & ms) const override { ms << mathMacro_->cell(idx_); }
///
- void octave(OctaveStream & os) const { os << mathMacro_->cell(idx_); }
+ void octave(OctaveStream & os) const override { os << mathMacro_->cell(idx_); }
///
- MathClass mathClass() const
+ MathClass mathClass() const override
{
return MC_UNKNOWN;
// This can be refined once the pointer issues are fixed. I did not
private:
///
- Inset * clone() const
+ Inset * clone() const override
{
return new InsetArgumentProxy(*this);
}
: 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), nesting_(0)
+ macro_(nullptr), needsUpdate_(false), isUpdating_(false),
+ appetite_(9), nesting_(0), limits_(AUTO_LIMITS)
{
}
/// Update the pointers to our owner of all expanded macros.
///
mutable std::map<BufferView const *, bool> editing_;
///
- std::string requires_;
+ std::string required_;
/// update macro representation
bool needsUpdate_;
///
size_t appetite_;
/// Level of nesting in macros (including this one)
int nesting_;
+ ///
+ Limits limits_;
};
InsetMathMacro::InsetMathMacro(InsetMathMacro const & that)
: InsetMathNest(that), d(new Private(*that.d))
{
- setBuffer(*that.buffer_);
+ // FIXME This should not really be necessary, but when we are
+ // initializing the table of global macros, we create macros
+ // with no associated Buffer.
+ if (that.buffer_)
+ setBuffer(*that.buffer_);
d->updateChildren(this);
}
return InsetMath::addToMathRow(mrow, mi);
/// The macro nesting can change display of insets. Change it locally.
- Changer chg = make_change(mi.base.macro_nesting, d->nesting_);
+ Changer chg = changeVar(mi.base.macro_nesting, d->nesting_);
MathRow::Element e_beg(mi, MathRow::BEGIN);
e_beg.inset = this;
- e_beg.marker = (d->nesting_ == 1) ? marker(mi.base.bv) : NO_MARKER;
+ e_beg.marker = (d->nesting_ == 1) ? marker(mi.base.bv) : marker_type::NO_MARKER;
mrow.push_back(e_beg);
d->macro_->lock();
MathRow::Element e_end(mi, MathRow::END);
e_end.inset = this;
- e_end.marker = (d->nesting_ == 1) ? marker(mi.base.bv) : NO_MARKER;
+ e_end.marker = (d->nesting_ == 1) ? marker(mi.base.bv) : marker_type::NO_MARKER;
mrow.push_back(e_end);
return has_contents;
}
+
+/// Whether the inset allows \(no)limits
+bool InsetMathMacro::allowsLimitsChange() const
+{
+ // similar to the code in mathClass(), except that we search for
+ // the right-side class.
+ MathClass mc = MC_UNKNOWN;
+ if (MacroData const * m = macroBackup()) {
+ // If it is a global macro and is defined explicitly
+ if (m->symbol())
+ mc = string_to_class(m->symbol()->extra);
+ }
+ // Otherwise guess from the expanded macro
+ if (mc == MC_UNKNOWN)
+ mc = d->expanded_.lastMathClass();
+
+ return mc == MC_OP;
+}
+
+
+Limits InsetMathMacro::defaultLimits(bool display) const
+{
+ if (d->expanded_.empty())
+ return NO_LIMITS;
+ // Guess from the expanded macro
+ InsetMath const * in = d->expanded_.back().nucleus();
+ Limits const lim = in->limits() == AUTO_LIMITS
+ ? in->defaultLimits(display) : in->limits();
+ LATTEST(lim != AUTO_LIMITS);
+ return lim;
+}
+
+
+Limits InsetMathMacro::limits() const
+{
+ return d->limits_;
+}
+
+
+void InsetMathMacro::limits(Limits lim)
+{
+ d->limits_ = lim;
+}
+
+
void InsetMathMacro::beforeMetrics() const
{
d->macro_->lock();
void InsetMathMacro::beforeDraw(PainterInfo const & pi) const
{
if (d->editing_[pi.base.bv])
- pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
+ pi.pain.enterMonochromeMode(Color_mathmacroblend);
}
bool InsetMathMacro::editMode(BufferView const * bv) const {
// find this in cursor trace
- Cursor const & cur = bv->cursor();
+ DocIterator const & cur =
+ // Do not move the reference while selecting with the mouse to avoid
+ // flicker due to changing metrics
+ bv->mouseSelecting() ? bv->cursor().realAnchor() : 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
}
-InsetMath::marker_type InsetMathMacro::marker(BufferView const * bv) const
+marker_type InsetMathMacro::marker(BufferView const * bv) const
{
if (nargs() == 0)
- return NO_MARKER;
+ return marker_type::NO_MARKER;
switch (d->displayMode_) {
case DISPLAY_INIT:
case DISPLAY_INTERACTIVE_INIT:
- return NO_MARKER;
+ return marker_type::NO_MARKER;
case DISPLAY_UNFOLDED:
- return MARKER;
+ return marker_type::MARKER;
case DISPLAY_NORMAL:
switch (lyxrc.macro_edit_style) {
case LyXRC::MACRO_EDIT_INLINE:
- return MARKER2;
+ return marker_type::MARKER2;
case LyXRC::MACRO_EDIT_INLINE_BOX:
- return d->editing_[bv] ? BOX_MARKER : MARKER2;
+ return d->editing_[bv] ? marker_type::BOX_MARKER : marker_type::MARKER2;
case LyXRC::MACRO_EDIT_LIST:
- return MARKER2;
+ return marker_type::MARKER2;
}
}
// please gcc 4.6
- return NO_MARKER;
+ return marker_type::NO_MARKER;
}
void InsetMathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
{
/// The macro nesting can change display of insets. Change it locally.
- Changer chg = make_change(mi.base.macro_nesting, d->nesting_);
+ Changer chg = changeVar(mi.base.macro_nesting, d->nesting_);
// set edit mode for which we will have calculated metrics. But only
d->editing_[mi.base.bv] = editMode(mi.base.bv);
d->needsUpdate_ = true;
}
} else {
- d->macro_ = 0;
+ d->macro_ = nullptr;
}
}
UpdateLocker locker(*this);
// known macro?
- if (d->macro_ == 0)
+ if (d->macro_ == nullptr)
return;
// remember nesting level of this macro
d->nesting_ = nesting;
// update requires
- d->requires_ = d->macro_->requires();
+ d->required_ = d->macro_->required();
if (!d->needsUpdate_
// non-normal mode? We are done!
pi.pain.text(x, y, from_ascii(":"), labelFont);
x += strw2;
- // draw paramter
+ // draw parameter
cell(i).draw(pi, x, y);
// next line
InsetMathMacro::DisplayMode InsetMathMacro::computeDisplayMode() const
{
- if (d->nextFoldMode_ == true && d->macro_ && !d->macro_->locked())
+ if (d->nextFoldMode_ && d->macro_ && !d->macro_->locked())
return DISPLAY_NORMAL;
else
return DISPLAY_UNFOLDED;
// valid characters?
if (n.size() > 1) {
- for (size_t i = 0; i<n.size(); ++i) {
- if (!(n[i] >= 'a' && n[i] <= 'z')
- && !(n[i] >= 'A' && n[i] <= 'Z')
- && n[i] != '*')
+ for (char_type c : n) {
+ if (!(c >= 'a' && c <= 'z')
+ && !(c >= 'A' && c <= 'Z')
+ && c != '*')
return false;
}
}
// 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.
- if (!d->requires_.empty())
- features.require(d->requires_);
+ if (!d->required_.empty())
+ features.require(d->required_);
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());
+ if (data && !data->required().empty())
+ features.require(data->required());
}
- if (name() == "binom")
- features.require("binom");
-
- // validate the cells and the definition
- if (displayMode() == DISPLAY_NORMAL) {
- d->definition_.validate(features);
- InsetMathNest::validate(features);
+ // Validate the cells and the definition.
+ // However, don't validate the definition if the macro is
+ // from the symbols file and has not been redefined, because
+ // in this case the definition is only used for screen display.
+ MathWordList const & words = mathedWordList();
+ MathWordList::const_iterator it = words.find(name());
+ MacroNameSet macros;
+ buffer().listMacroNames(macros);
+ if (it == words.end() || it->second.inset != "macro"
+ || macros.find(name()) != macros.end()) {
+ if (displayMode() == DISPLAY_NORMAL) {
+ d->definition_.validate(features);
+ } else if (displayMode() == DISPLAY_INIT) {
+ MathData ar(const_cast<Buffer *>(&buffer()));
+ MacroData const * data = buffer().getMacro(name());
+ if (data) {
+ asArray(data->definition(), ar);
+ ar.validate(features);
+ }
+ }
}
+ InsetMathNest::validate(features);
}
}
-void InsetMathMacro::removeArgument(Inset::pos_type pos) {
+void InsetMathMacro::removeArgument(pos_type pos) {
if (d->displayMode_ == DISPLAY_NORMAL) {
LASSERT(size_t(pos) < cells_.size(), return);
cells_.erase(cells_.begin() + pos);
}
-void InsetMathMacro::insertArgument(Inset::pos_type pos) {
+void InsetMathMacro::insertArgument(pos_type pos) {
if (d->displayMode_ == DISPLAY_NORMAL) {
LASSERT(size_t(pos) <= cells_.size(), return);
cells_.insert(cells_.begin() + pos, MathData());
}
-void InsetMathMacro::write(WriteStream & os) const
+void InsetMathMacro::write(TeXMathStream & os) const
{
mode_type mode = currentMode();
MathEnsurer ensurer(os, mode == MATH_MODE, true, mode == TEXT_MODE);
// contains macros with optionals.
bool braced = false;
size_type last = cell(i).size() - 1;
- if (cell(i).size() && cell(i)[last]->asUnknownInset()) {
+ if (!cell(i).empty() && cell(i)[last]->asUnknownInset()) {
latexkeys const * l = in_word_set(cell(i)[last]->name());
braced = (l && l->inset == "big");
- } else if (cell(i).size() && cell(i)[0]->asScriptInset()) {
+ } else if (!cell(i).empty() && cell(i)[0]->asScriptInset()) {
braced = cell(i)[0]->asScriptInset()->nuc().empty();
} else {
for (size_type j = 0; j < cell(i).size(); ++j) {
}
// add space if there was no argument
- if (first)
+ // or add braces if we have optionals but none are present and [ follows
+ if (first) {
os.pendingSpace(true);
+ os.useBraces(d->optionals_ > 0);
+ }
+
+ // write \(no)limits modifiers if relevant
+ writeLimits(os);
}
}
-void InsetMathMacro::mathmlize(MathStream & os) const
+void InsetMathMacro::mathmlize(MathMLStream & ms) const
{
// macro_ is 0 if this is an unknown macro
LATTEST(d->macro_ || d->displayMode_ != DISPLAY_NORMAL);
if (d->macro_) {
- docstring const xmlname = d->macro_->xmlname();
+ docstring const xmlname = (ms.xmlMode()) ? d->macro_->xmlname() : d->macro_->htmlname();
if (!xmlname.empty()) {
char const * type = d->macro_->MathMLtype();
- os << '<' << type << "> " << xmlname << " </"
- << type << '>';
+ ms << "<" << from_ascii(ms.namespacedTag(type)) << ">"
+ << xmlname
+ << "</" << from_ascii(ms.namespacedTag(type)) << ">";
return;
}
}
// this means that we do not recognize the macro
throw MathExportException();
}
- os << d->expanded_;
+ ms << d->expanded_;
}
// macro_ is 0 if this is an unknown macro
LATTEST(d->macro_ || d->displayMode_ != DISPLAY_NORMAL);
if (d->macro_) {
- docstring const xmlname = d->macro_->xmlname();
+ docstring const xmlname = d->macro_->htmlname();
if (!xmlname.empty()) {
os << ' ' << xmlname << ' ';
return;
bool InsetMathMacro::completionSupported(Cursor const & cur) const
{
+ if (cur.buffer()->isReadonly())
+ return false;
+
if (displayMode() != DISPLAY_UNFOLDED)
return InsetMathNest::completionSupported(cur);
bool InsetMathMacro::inlineCompletionSupported(Cursor const & cur) const
{
+ if (cur.buffer()->isReadonly())
+ return false;
+
if (displayMode() != DISPLAY_UNFOLDED)
return InsetMathNest::inlineCompletionSupported(cur);
if (displayMode() != DISPLAY_UNFOLDED)
return InsetMathNest::completionPrefix(cur);
- if (!completionSupported(cur))
- return docstring();
-
return "\\" + name();
}
-bool InsetMathMacro::insertCompletion(Cursor & cur, docstring const & s,
- bool finished)
+bool InsetMathMacro::insertCompletion(Cursor & cur, docstring const & s, bool finished)
{
+ if (cur.buffer()->isReadonly())
+ return false;
+
if (displayMode() != DISPLAY_UNFOLDED)
return InsetMathNest::insertCompletion(cur, s, finished);
if (!completionSupported(cur))
return false;
+ // Contrary to Text, the whole inset should be recorded (#12581).
+ cur.recordUndoInset();
+
// append completion
docstring newName = name() + s;
asArray(newName, cell(0));