Implement properly \limits and \nolimits

These are now properties of insets that can be operators :
InsetMathSymbols, InsetMathDecoration (for over/underbrace) and
InsetMathMacro (for its contents).

Each of these has a limit_ member that allows to remember a limit
forcing and a member defaultLimits() that indicates what to do in the
absence of such forcing. Moreover the write() method calls
writeLimits().

This allows to simplify the definitions of integrals in lib/symbols by
defining the integrals as macros of their "op" version, as it is done in
the style files.

Also, many hardcoded assumptions can now be removed.

The handling of LFUN_MATH_LIMITS is now done in InsetNest, which tries
successively to apply the limit change to (1) the character after
cursor, (2) the character before cursor and (3) the character at the
end of the inset (useful for script insets?)

The new code allows to define
  \newcommand\int{\intop\limits}
but not
  \newcommand\makelimits#1{#1\limits}

It is also possible to type explicitly \limits or \nolimits to modify
a symbol.
This commit is contained in:
Jean-Marc Lasgouttes 2020-07-19 19:56:07 +02:00
parent 1dbc50a30d
commit f96b99dcb3
15 changed files with 262 additions and 245 deletions

View File

@ -1029,70 +1029,60 @@ tbond cmsy 180 186 mathord x x
# If the wasysym integrals are really wanted then one has to load the package # If the wasysym integrals are really wanted then one has to load the package
# manually and disable automatic loading of amsmath and esint. # manually and disable automatic loading of amsmath and esint.
iffont esint iffont esint
int esint 001|002 0 mathop ∫ ∫ esint|amsmath
intop esint 001|002 0 mathop ∫ ∫ esint intop esint 001|002 0 mathop ∫ ∫ esint
iint esint 003|004 0 mathop ∬ ∬ esint|amsmath
iintop esint 003|004 0 mathop ∬ ∬ esint iintop esint 003|004 0 mathop ∬ ∬ esint
iiint esint 005|006 0 mathop ∭ ∭ esint|amsmath
iiintop esint 005|006 0 mathop ∭ ∭ esint iiintop esint 005|006 0 mathop ∭ ∭ esint
iiiint esint 007|008 0 mathop ⨌ ⨌ esint|amsmath
iiiintop esint 007|008 0 mathop ⨌ ⨌ esint iiiintop esint 007|008 0 mathop ⨌ ⨌ esint
#9 codepoint forbidden in qt4, 10,12,13 in qt5 #9 codepoint forbidden in qt4, 10,12,13 in qt5
oint esint 043|044 0 mathop ∮ ∮ esint
ointop esint 043|044 0 mathop ∮ ∮ esint ointop esint 043|044 0 mathop ∮ ∮ esint
oiint esint 045|046 0 mathop ∯ ∯ esint
oiintop esint 045|046 0 mathop ∯ ∯ esint oiintop esint 045|046 0 mathop ∯ ∯ esint
sqint esint 015|016 0 mathop ⨖ ⨖ esint
sqintop esint 015|016 0 mathop ⨖ ⨖ esint sqintop esint 015|016 0 mathop ⨖ ⨖ esint
sqiint esint 017|018 0 mathop x esint
sqiintop esint 017|017 0 mathop x esint sqiintop esint 017|017 0 mathop x esint
dotsint esint 041|042 0 mathop ∫⋯∫ ∫⋯∫ esint
dotsintop esint 041|042 0 mathop ∫⋯∫ ∫⋯∫ esint dotsintop esint 041|042 0 mathop ∫⋯∫ ∫⋯∫ esint
ointctrclockwise esint 023|024 0 mathop ∳ ∳ esint
ointctrclockwiseop esint 023|024 0 mathop ∳ ∳ esint ointctrclockwiseop esint 023|024 0 mathop ∳ ∳ esint
ointclockwise esint 025|026 0 mathop ∲ ∲ esint
ointclockwiseop esint 025|026 0 mathop ∲ ∲ esint ointclockwiseop esint 025|026 0 mathop ∲ ∲ esint
else else
int cmex 82|90 242 mathop ∫ ∫ esint|amsmath
intop cmex 82|90 242 mathop ∫ ∫ esint intop cmex 82|90 242 mathop ∫ ∫ esint
iint wasy 115|120 0 mathop ∬ ∬ esint|amsmath
iintop wasy 115|120 0 mathop &Int ∬ esint iintop wasy 115|120 0 mathop &Int ∬ esint
iiint wasy 116|121 0 mathop ∭ ∭ esint|amsmath
iiintop wasy 116|121 0 mathop ∭ ∭ esint iiintop wasy 116|121 0 mathop ∭ ∭ esint
\def\iiiint{\int\kern-6mu\int\kern-6mu\int\kern-6mu\int} esint|amsmath
\def\iiiintop{\int\kern-6mu\int\kern-6mu\int\kern-6mu\int} esint \def\iiiintop{\int\kern-6mu\int\kern-6mu\int\kern-6mu\int} esint
\def\dotsint{\int\kern-3mu\cdots\kern-3mu\int} esint
\def\dotsintop{\int\kern-3mu\cdots\kern-3mu\int} esint
oint cmex 72|73 0 mathop ∮ ∮ esint
ointop cmex 72|73 0 mathop ∮ ∮ esint ointop cmex 72|73 0 mathop ∮ ∮ esint
oiint wasy 118|123 0 mathop ∯ ∯ esint
oiintop wasy 118|123 0 mathop ∯ ∯ esint oiintop wasy 118|123 0 mathop ∯ ∯ esint
\def\sqint{\square\kern-17mu\int\kern6mu} esint
\def\sqintop{\square\kern-17mu\int\kern6mu} esint \def\sqintop{\square\kern-17mu\int\kern6mu} esint
\def\sqiint{\square\kern-20mu\iint\kern3mu} esint
\def\sqiintop{\square\kern-20mu\iint\kern3mu} esint \def\sqiintop{\square\kern-20mu\iint\kern3mu} esint
\def\ointctrclockwise{\circlearrowleft\kern-21mu\int\kern6mu} esint \def\dotsintop{\int\kern-3mu\cdots\kern-3mu\int} esint
\def\ointctrclockwiseop{\circlearrowleft\kern-21mu\int\kern6mu} esint \def\ointctrclockwiseop{\circlearrowleft\kern-21mu\int\kern6mu} esint
\def\ointclockwise{\circlearrowright\kern-21mu\int\kern6mu} esint
\def\ointclockwiseop{\circlearrowright\kern-21mu\int\kern6mu} esint \def\ointclockwiseop{\circlearrowright\kern-21mu\int\kern6mu} esint
endif endif
\def\int{\intop\nolimits} mathop ∫ ∫ esint|amsmath
\def\iint{\iintop\nolimits} mathop ∬ ∬ esint|amsmath
\def\iiint{\iiintop\nolimits} mathop ∭ ∭ esint|amsmath
\def\iiiint{\iiiintop\nolimits} mathop ⨌ ⨌ esint|amsmath
\def\oint{\ointop\nolimits} mathop ∮ ∮ esint
\def\oiint{\oiintop\nolimits} mathop ∯ ∯ esint
\def\sqint{\sqintop\nolimits} mathop ⨖ ⨖ esint
\def\sqiint{\sqiintop\nolimits} mathop x esint
\def\dotsint{\dotsintop\nolimits} mathop ∫⋯∫ ∫⋯∫
\def\ointctrclockwise{\ointctrclockwise\nolimits} mathop ∲ ∲ esint
\def\ointclockwise{\ointclockwise\nolimits} mathop ∲ ∲ esint
varointclockwise esint 027|028 0 mathop ∲ ∲ esint
varointclockwiseop esint 027|028 0 mathop ∲ ∲ esint varointclockwiseop esint 027|028 0 mathop ∲ ∲ esint
varointctrclockwise esint 029|030 0 mathop ∳ ∳ esint
varointctrclockwiseop esint 029|030 0 mathop ∳ ∳ esint varointctrclockwiseop esint 029|030 0 mathop ∳ ∳ esint
fint esint 031|032 0 mathop ⨏ ⨏ esint
fintop esint 031|032 0 mathop ⨏ ⨏ esint fintop esint 031|032 0 mathop ⨏ ⨏ esint
varoiint esint 033|034 0 mathop ∯ ∯ esint
varoiintop esint 033|034 0 mathop ∯ ∯ esint varoiintop esint 033|034 0 mathop ∯ ∯ esint
landupint esint 035|036 0 mathop x x esint
landupintop esint 035|036 0 mathop x x esint landupintop esint 035|036 0 mathop x x esint
landdownint esint 037|038 0 mathop x x esint
landdownintop esint 037|038 0 mathop x x esint landdownintop esint 037|038 0 mathop x x esint
\def\varointclockwise{\varointclockwiseop\limits} mathop ∲ ∲ esint
\def\varointctrclockwise{\varointctrclockwiseop\limits} mathop ∳ ∳ esint
\def\fint{\fintop\limits} mathop ⨏ ⨏ esint
\def\varoiint{\varoiintop\limits} mathop ∯ ∯ esint
\def\landupint{\landupintop\limits} mathop x x esint
\def\landdownint{\landdownintop\limits} mathop x x esint
# From the amsmath package: # From the amsmath package:
\def\idotsint{\int\kern-3mu\cdots\kern-3mu\int} amsmath \def\idotsint{\int\kern-3mu\cdots\kern-3mu\int\limits} amsmath
log lyxblacktext 0 0 func x x log lyxblacktext 0 0 func x x

View File

@ -52,12 +52,6 @@ MathData const & InsetMath::cell(idx_type) const
} }
MathClass InsetMath::mathClass() const
{
return MC_ORD;
}
InsetMath::marker_type InsetMath::marker(BufferView const *) const InsetMath::marker_type InsetMath::marker(BufferView const *) const
{ {
return nargs() > 0 ? MARKER : NO_MARKER; return nargs() > 0 ? MARKER : NO_MARKER;
@ -74,6 +68,19 @@ bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
} }
/// write LaTeX and LyX code
void InsetMath::writeLimits(WriteStream & os) const
{
if (limits() == LIMITS) {
os << "\\limits";
os.pendingSpace(true);
} else if (limits() == NO_LIMITS) {
os << "\\nolimits ";
os.pendingSpace(true);
}
}
void InsetMath::dump() const void InsetMath::dump() const
{ {
lyxerr << "---------------------------------------------" << endl; lyxerr << "---------------------------------------------" << endl;

View File

@ -39,6 +39,17 @@ enum HullType {
HullType hullType(docstring const & name); HullType hullType(docstring const & name);
docstring hullName(HullType type); docstring hullName(HullType type);
enum Limits {
// what is obtained with \c \\nolimits
NO_LIMITS = -1,
// the default
AUTO_LIMITS = 0,
// what is obtained with \c \\limits
LIMITS = 1
};
/** /**
Abstract base class for all math objects. A math insets is for use of the Abstract base class for all math objects. A math insets is for use of the
@ -173,7 +184,7 @@ public:
virtual InsetMathSpecialChar const * asSpecialCharInset() const { return nullptr; } virtual InsetMathSpecialChar const * asSpecialCharInset() const { return nullptr; }
/// The class of the math object (used primarily for spacing) /// The class of the math object (used primarily for spacing)
virtual MathClass mathClass() const; virtual MathClass mathClass() const { return MC_ORD; }
/// Add this inset to a math row. Return true if contents got added /// Add this inset to a math row. Return true if contents got added
virtual bool addToMathRow(MathRow &, MetricsInfo & mi) const; virtual bool addToMathRow(MathRow &, MetricsInfo & mi) const;
/// Hook that is run before metrics computation starts /// Hook that is run before metrics computation starts
@ -185,15 +196,22 @@ public:
/// Hook that is run after drawing /// Hook that is run after drawing
virtual void afterDraw(PainterInfo const &) const {} virtual void afterDraw(PainterInfo const &) const {}
/// identifies things that can get scripts
virtual bool isScriptable() const { return false; }
/// will this get written as a single block in {..} /// will this get written as a single block in {..}
virtual bool extraBraces() const { return false; } virtual bool extraBraces() const { return false; }
/// return the content as char if the inset is able to do so /// return the content as char if the inset is able to do so
virtual char_type getChar() const { return 0; } virtual char_type getChar() const { return 0; }
/// identifies things that can get \limits or \nolimits
virtual bool takesLimits() const { return false; } /// Whether the inset allows \(no)limits
bool allowsLimitsChange() const { return mathClass() == MC_OP; }
/// The default limits value
virtual Limits defaultLimits() const { return NO_LIMITS; }
/// whether the inset has limit-like sub/superscript
virtual Limits limits() const { return AUTO_LIMITS; }
/// sets types of sub/superscripts
virtual void limits(Limits) {}
/// write limits status for LaTeX and LyX code
void writeLimits(WriteStream & os) const;
/// replace things by other things /// replace things by other things
virtual void replace(ReplaceData &) {} virtual void replace(ReplaceData &) {}

View File

@ -38,7 +38,7 @@ namespace lyx {
InsetMathDecoration::InsetMathDecoration(Buffer * buf, latexkeys const * key) InsetMathDecoration::InsetMathDecoration(Buffer * buf, latexkeys const * key)
: InsetMathNest(buf, 1), key_(key), dh_(0), dy_(0), dw_(0) : InsetMathNest(buf, 1), key_(key)
{ {
// lyxerr << " creating deco " << key->name << endl; // lyxerr << " creating deco " << key->name << endl;
} }
@ -64,12 +64,6 @@ MathClass InsetMathDecoration::mathClass() const
} }
bool InsetMathDecoration::isScriptable() const
{
return mathClass() == MC_OP;
}
bool InsetMathDecoration::protect() const bool InsetMathDecoration::protect() const
{ {
return return
@ -151,6 +145,7 @@ void InsetMathDecoration::write(WriteStream & os) const
os << '\\' << key_->name << '{'; os << '\\' << key_->name << '{';
ModeSpecifier specifier(os, currentMode()); ModeSpecifier specifier(os, currentMode());
os << cell(0) << '}'; os << cell(0) << '}';
writeLimits(os);
} }

View File

@ -39,8 +39,12 @@ public:
void infoize(odocstream & os) const; void infoize(odocstream & os) const;
/// ///
MathClass mathClass() const; MathClass mathClass() const;
/// /// The default limits value
bool isScriptable() const; Limits defaultLimits() const { return allowsLimitsChange() ? LIMITS : NO_LIMITS; }
/// whether the inset has limit-like sub/superscript
Limits limits() const { return limits_; }
/// sets types of sub/superscripts
void limits(Limits lim) { limits_ = lim; }
/// ///
void validate(LaTeXFeatures & features) const; void validate(LaTeXFeatures & features) const;
/// ///
@ -60,12 +64,15 @@ private:
/// ///
latexkeys const * key_; latexkeys const * key_;
///
Limits limits_ = AUTO_LIMITS;
// FIXME: this should depend on BufferView
/// height cache of deco /// height cache of deco
mutable int dh_; mutable int dh_ = 0;
/// vertical offset cache of deco /// vertical offset cache of deco
mutable int dy_; mutable int dy_ = 0;
/// width for non-wide deco /// width for non-wide deco
mutable int dw_; mutable int dw_ = 0;
}; };
} // namespace lyx } // namespace lyx

View File

@ -371,6 +371,38 @@ bool InsetMathMacro::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
return has_contents; 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() 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() : in->limits();
LATTEST(lim != AUTO_LIMITS);
return lim;
}
void InsetMathMacro::beforeMetrics() const void InsetMathMacro::beforeMetrics() const
{ {
d->macro_->lock(); d->macro_->lock();
@ -1155,6 +1187,9 @@ void InsetMathMacro::write(WriteStream & os) const
// add space if there was no argument // add space if there was no argument
if (first) if (first)
os.pendingSpace(true); os.pendingSpace(true);
// write \(no)limits modifiers if relevant
writeLimits(os);
} }

View File

@ -41,6 +41,16 @@ public:
/// If the macro is in normal edit mode, dissolve its contents in /// If the macro is in normal edit mode, dissolve its contents in
/// the row. Otherwise, just insert the inset. /// the row. Otherwise, just insert the inset.
bool addToMathRow(MathRow &, MetricsInfo & mi) const; bool addToMathRow(MathRow &, MetricsInfo & mi) const;
/// Whether the inset allows \(no)limits
bool allowsLimitsChange() const;
/// The default limits value
Limits defaultLimits() const;
/// whether the inset has limit-like sub/superscript
Limits limits() const { return limits_; }
/// sets types of sub/superscripts
void limits(Limits lim) { limits_ = lim; }
/// ///
void beforeMetrics() const; void beforeMetrics() const;
/// ///
@ -184,6 +194,9 @@ private:
/// ///
bool editMode(BufferView const * bv) const; bool editMode(BufferView const * bv) const;
///
Limits limits_ = AUTO_LIMITS;
/// ///
class Private; class Private;
/// ///

View File

@ -518,7 +518,6 @@ void InsetMathNest::handleFont2(Cursor & cur, docstring const & arg)
// FIXME: support other font changes here as well? // FIXME: support other font changes here as well?
} }
void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd) void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
{ {
//LYXERR0("InsetMathNest: request: " << cmd); //LYXERR0("InsetMathNest: request: " << cmd);
@ -1277,6 +1276,32 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
} }
break; break;
case LFUN_MATH_LIMITS: {
InsetMath * in = 0;
if (cur.pos() < cur.lastpos() && cur.nextMath().allowsLimitsChange())
in = &cur.nextMath();
else if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange())
in = &cur.prevMath();
else if (cur.lastpos() > 0 && cur.cell().back()->allowsLimitsChange())
in = cur.cell().back().nucleus();
// only when nucleus allows this
if (!in)
return;
cur.recordUndoInset();
if (!cmd.argument().empty()) {
if (cmd.argument() == "limits")
in->limits(LIMITS);
else if (cmd.argument() == "nolimits")
in->limits(NO_LIMITS);
else
in->limits(AUTO_LIMITS);
} else if (in->limits() == AUTO_LIMITS)
in->limits(in->defaultLimits() == LIMITS ? NO_LIMITS : LIMITS);
else
in->limits(AUTO_LIMITS);
return;
}
default: default:
InsetMath::doDispatch(cur, cmd); InsetMath::doDispatch(cur, cmd);
break; break;
@ -1457,6 +1482,29 @@ bool InsetMathNest::getStatus(Cursor & cur, FuncRequest const & cmd,
break; break;
} }
case LFUN_MATH_LIMITS: {
InsetMath * in = 0;
if (cur.pos() < cur.lastpos() && cur.nextMath().allowsLimitsChange())
in = &cur.nextMath();
else if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange())
in = &cur.prevMath();
else if (cur.lastpos() > 0 && cur.cell().back()->allowsLimitsChange())
in = cur.cell().back().nucleus();
if (in) {
if (!cmd.argument().empty()) {
if (cmd.argument() == "limits")
flag.setOnOff(in->limits() == LIMITS);
else if (cmd.argument() == "nolimits")
flag.setOnOff(in->limits() == NO_LIMITS);
else
flag.setOnOff(in->limits() == AUTO_LIMITS);
}
flag.setEnabled(true);
} else
flag.setEnabled(false);
return true;
}
default: default:
ret = false; ret = false;
break; break;
@ -1858,6 +1906,15 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
bool InsetMathNest::interpretString(Cursor & cur, docstring const & str) bool InsetMathNest::interpretString(Cursor & cur, docstring const & str)
{ {
if (str == "\\limits" || str == "\\nolimits") {
if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange()) {
cur.prevMath().limits(str == "\\limits" ? LIMITS : NO_LIMITS);
return true;
} else {
cur.message(bformat(_("Cannot apply %1$s here."), str));
return false;
}
}
// Create a InsetMathBig from cur.cell()[cur.pos() - 1] and t if // Create a InsetMathBig from cur.cell()[cur.pos() - 1] and t if
// possible // possible
if (!cur.empty() && cur.pos() > 0 && if (!cur.empty() && cur.pos() > 0 &&

View File

@ -13,16 +13,12 @@
#include "InsetMathScript.h" #include "InsetMathScript.h"
#include "InsetMathBrace.h" #include "InsetMathBrace.h"
#include "InsetMathSymbol.h"
#include "MathData.h" #include "MathData.h"
#include "MathStream.h" #include "MathStream.h"
#include "MathSupport.h" #include "MathSupport.h"
#include "BufferView.h" #include "BufferView.h"
#include "Cursor.h" #include "Cursor.h"
#include "DispatchResult.h"
#include "FuncRequest.h"
#include "FuncStatus.h"
#include "LaTeXFeatures.h" #include "LaTeXFeatures.h"
#include "MetricsInfo.h" #include "MetricsInfo.h"
@ -37,17 +33,17 @@ namespace lyx {
InsetMathScript::InsetMathScript(Buffer * buf) InsetMathScript::InsetMathScript(Buffer * buf)
: InsetMathNest(buf, 1), cell_1_is_up_(false), limits_(0) : InsetMathNest(buf, 1), cell_1_is_up_(false)
{} {}
InsetMathScript::InsetMathScript(Buffer * buf, bool up) InsetMathScript::InsetMathScript(Buffer * buf, bool up)
: InsetMathNest(buf, 2), cell_1_is_up_(up), limits_(0) : InsetMathNest(buf, 2), cell_1_is_up_(up)
{} {}
InsetMathScript::InsetMathScript(Buffer * buf, MathAtom const & at, bool up) InsetMathScript::InsetMathScript(Buffer * buf, MathAtom const & at, bool up)
: InsetMathNest(buf, 2), cell_1_is_up_(up), limits_(0) : InsetMathNest(buf, 2), cell_1_is_up_(up)
{ {
LATTEST(nargs() >= 1); LATTEST(nargs() >= 1);
cell(0).push_back(at); cell(0).push_back(at);
@ -179,7 +175,7 @@ int InsetMathScript::dy0(BufferView const & bv) const
if (!hasDown()) if (!hasDown())
return nd; return nd;
int des = down().dimension(bv).ascent(); int des = down().dimension(bv).ascent();
if (hasLimits()) if (has_limits_)
des += nd + 2; des += nd + 2;
else { else {
int na = nasc(bv); int na = nasc(bv);
@ -195,7 +191,7 @@ int InsetMathScript::dy1(BufferView const & bv) const
if (!hasUp()) if (!hasUp())
return na; return na;
int asc = up().dimension(bv).descent(); int asc = up().dimension(bv).descent();
if (hasLimits()) if (has_limits_)
asc += na + 2; asc += na + 2;
else { else {
int nd = ndes(bv); int nd = ndes(bv);
@ -210,7 +206,7 @@ int InsetMathScript::dx0(BufferView const & bv) const
{ {
LASSERT(hasDown(), return 0); LASSERT(hasDown(), return 0);
Dimension const dim = dimension(bv); Dimension const dim = dimension(bv);
return hasLimits() ? (dim.wid - down().dimension(bv).width()) / 2 return has_limits_ ? (dim.wid - down().dimension(bv).width()) / 2
: nwid(bv) + min(nker(&bv), 0); : nwid(bv) + min(nker(&bv), 0);
} }
@ -219,7 +215,7 @@ int InsetMathScript::dx1(BufferView const & bv) const
{ {
LASSERT(hasUp(), return 0); LASSERT(hasUp(), return 0);
Dimension const dim = dimension(bv); Dimension const dim = dimension(bv);
return hasLimits() ? (dim.wid - up().dimension(bv).width()) / 2 return has_limits_ ? (dim.wid - up().dimension(bv).width()) / 2
: nwid(bv) + max(nker(&bv), 0); : nwid(bv) + max(nker(&bv), 0);
} }
@ -227,7 +223,7 @@ int InsetMathScript::dx1(BufferView const & bv) const
int InsetMathScript::dxx(BufferView const & bv) const int InsetMathScript::dxx(BufferView const & bv) const
{ {
Dimension const dim = dimension(bv); Dimension const dim = dimension(bv);
return hasLimits() ? (dim.wid - nwid(bv)) / 2 : 0; return has_limits_ ? (dim.wid - nwid(bv)) / 2 : 0;
} }
@ -275,16 +271,21 @@ MathClass InsetMathScript::mathClass() const
void InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const void InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const
{ {
Changer dummy2 = mi.base.changeEnsureMath();
Dimension dim0; Dimension dim0;
Dimension dim1; Dimension dim1;
Dimension dim2; Dimension dim2;
cell(0).metrics(mi, dim0); {
Changer dummy = mi.base.changeScript(); // Keeps the changers local
if (nargs() > 1) Changer dummy2 = mi.base.changeEnsureMath();
cell(1).metrics(mi, dim1, !hasLimits()); cell(0).metrics(mi, dim0);
if (nargs() > 2) Changer dummy = mi.base.changeScript();
cell(2).metrics(mi, dim2, !hasLimits()); if (nargs() > 1)
cell(1).metrics(mi, dim1, !has_limits_);
if (nargs() > 2)
cell(2).metrics(mi, dim2, !has_limits_);
}
// we store this, because it is much easier
has_limits_ = hasLimits(mi.base.font);
dim.wid = 0; dim.wid = 0;
BufferView & bv = *mi.base.bv; BufferView & bv = *mi.base.bv;
@ -296,7 +297,7 @@ void InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const
if (hasDown()) if (hasDown())
dimdown = down().dimension(bv); dimdown = down().dimension(bv);
if (hasLimits()) { if (has_limits_) {
dim.wid = nwid(bv); dim.wid = nwid(bv);
if (hasUp()) if (hasUp())
dim.wid = max(dim.wid, dimup.width()); dim.wid = max(dim.wid, dimup.width());
@ -365,47 +366,17 @@ void InsetMathScript::drawT(TextPainter & pain, int x, int y) const
} }
// FIXME: See InsetMathSymbol::takesLimits, which seems to attempt the bool InsetMathScript::hasLimits(FontInfo const & font) const
// same in a hardcoded way. takeLimits use is currently commented out in
// InsetMathScript::metrics. It seems that the mathop test is general
// enough, but only time will tell.
bool InsetMathScript::allowsLimits() const
{ {
if (font.style() != DISPLAY_STYLE)
return false;
if (nuc().empty()) if (nuc().empty())
return false; return false;
// Only makes sense for insets of mathop class
if (nuc().back()->mathClass() != MC_OP)
return false;
return true;
}
Limits const lim = nuc().back()->limits() == AUTO_LIMITS
bool InsetMathScript::hasLimits() const ? nuc().back()->defaultLimits() : nuc().back()->limits();
{ LASSERT(lim != AUTO_LIMITS, return false);
// obvious cases return lim == LIMITS;
if (limits_ == 1)
return true;
if (limits_ == -1)
return false;
// we can only display limits if the nucleus wants some
if (!allowsLimits())
return false;
// FIXME: this is some hardcoding done in InsetMathSymbol::metrics.
if (!nuc().back()->isScriptable())
return false;
if (nuc().back()->asSymbolInset()) {
// \intop is an alias for \int\limits, \ointop == \oint\limits
if (nuc().back()->asSymbolInset()->name().find(from_ascii("intop")) != string::npos)
return true;
// per default \int has limits beside the \int even in displayed formulas
if (nuc().back()->asSymbolInset()->name().find(from_ascii("int")) != string::npos)
return false;
}
// assume "real" limits for everything else
return true;
} }
@ -481,7 +452,8 @@ bool InsetMathScript::idxUpDown(Cursor & cur, bool up) const
return false; return false;
// go up/down only if in the last position // go up/down only if in the last position
// or in the first position of something with displayed limits // or in the first position of something with displayed limits
if (cur.pos() == cur.lastpos() || (cur.pos() == 0 && hasLimits())) { if (cur.pos() == cur.lastpos()
|| (cur.pos() == 0 && has_limits_)) {
cur.idx() = idxOfScript(up); cur.idx() = idxOfScript(up);
cur.pos() = 0; cur.pos() = 0;
return true; return true;
@ -519,20 +491,12 @@ void InsetMathScript::write(WriteStream & os) const
{ {
MathEnsurer ensurer(os); MathEnsurer ensurer(os);
if (!nuc().empty()) { if (!nuc().empty())
os << nuc(); os << nuc();
//if (nuc().back()->takesLimits()) { else if (os.firstitem())
if (limits_ == -1) LYXERR(Debug::MATHED, "suppressing {} when writing");
os << "\\nolimits "; else
if (limits_ == 1) os << "{}";
os << "\\limits ";
//}
} else {
if (os.firstitem())
LYXERR(Debug::MATHED, "suppressing {} when writing");
else
os << "{}";
}
if (hasDown() /*&& !down().empty()*/) if (hasDown() /*&& !down().empty()*/)
os << "_{" << down() << '}'; os << "_{" << down() << '}';
@ -616,7 +580,8 @@ void InsetMathScript::mathmlize(MathStream & ms) const
{ {
bool d = hasDown() && !down().empty(); bool d = hasDown() && !down().empty();
bool u = hasUp() && !up().empty(); bool u = hasUp() && !up().empty();
bool l = hasLimits(); // FIXME: the MathStream should be able to give us this information
bool l = has_limits_;
if (u && d) if (u && d)
ms << MTag(l ? "munderover" : "msubsup"); ms << MTag(l ? "munderover" : "msubsup");
@ -680,8 +645,7 @@ void InsetMathScript::infoize(odocstream & os) const
void InsetMathScript::infoize2(odocstream & os) const void InsetMathScript::infoize2(odocstream & os) const
{ {
if (limits_) os << from_ascii(has_limits_ == 1 ? ", Displayed limits" : ", Inlined limits");
os << from_ascii(limits_ == 1 ? ", Displayed limits" : ", Inlined limits");
} }
@ -745,57 +709,6 @@ bool InsetMathScript::notifyCursorLeaves(Cursor const & old, Cursor & cur)
} }
void InsetMathScript::doDispatch(Cursor & cur, FuncRequest & cmd)
{
//LYXERR("InsetMathScript: request: " << cmd);
if (cmd.action() == LFUN_MATH_LIMITS) {
// only when nucleus allows this
if (!allowsLimits())
return;
cur.recordUndoInset();
if (!cmd.argument().empty()) {
if (cmd.argument() == "limits")
limits_ = 1;
else if (cmd.argument() == "nolimits")
limits_ = -1;
else
limits_ = 0;
} else if (limits_ == 0)
limits_ = hasLimits() ? -1 : 1;
else
limits_ = 0;
return;
}
InsetMathNest::doDispatch(cur, cmd);
}
bool InsetMathScript::getStatus(Cursor & cur, FuncRequest const & cmd,
FuncStatus & flag) const
{
if (cmd.action() == LFUN_MATH_LIMITS) {
// only when nucleus allows this
if (allowsLimits()) {
if (!cmd.argument().empty()) {
if (cmd.argument() == "limits")
flag.setOnOff(limits_ == 1);
else if (cmd.argument() == "nolimits")
flag.setOnOff(limits_ == -1);
else
flag.setOnOff(limits_ == 0);
}
flag.setEnabled(true);
} else
flag.setEnabled(false);
return true;
}
return InsetMathNest::getStatus(cur, cmd, flag);
}
// the idea for dual scripts came from the eLyXer code // the idea for dual scripts came from the eLyXer code
void InsetMathScript::validate(LaTeXFeatures & features) const void InsetMathScript::validate(LaTeXFeatures & features) const
{ {

View File

@ -72,10 +72,6 @@ public:
/// ///
InsetMathScript * asScriptInset(); InsetMathScript * asScriptInset();
/// set limits
void limits(int lim) { limits_ = lim; }
/// get limits
int limits() const { return limits_; }
/// returns subscript. Always run 'hasDown' or 'has(false)' before! /// returns subscript. Always run 'hasDown' or 'has(false)' before!
MathData const & down() const; MathData const & down() const;
/// returns subscript. Always run 'hasDown' or 'has(false)' before! /// returns subscript. Always run 'hasDown' or 'has(false)' before!
@ -108,11 +104,6 @@ public:
InsetCode lyxCode() const { return MATH_SCRIPT_CODE; } InsetCode lyxCode() const { return MATH_SCRIPT_CODE; }
/// ///
void validate(LaTeXFeatures &features) const; void validate(LaTeXFeatures &features) const;
protected:
virtual void doDispatch(Cursor & cur, FuncRequest & cmd);
/// do we want to handle this event?
bool getStatus(Cursor & cur, FuncRequest const & cmd,
FuncStatus & status) const;
private: private:
virtual Inset * clone() const; virtual Inset * clone() const;
/// returns x offset for main part /// returns x offset for main part
@ -138,17 +129,15 @@ private:
/// shifts the superscript to the right, and a negative value shifts the /// shifts the superscript to the right, and a negative value shifts the
/// subscript to the left. /// subscript to the left.
int nker(BufferView const * bv) const; int nker(BufferView const * bv) const;
/// can one change how scripts are drawn? /// do we we have to draw the scripts above/below nucleus?
bool allowsLimits() const; bool hasLimits(FontInfo const &) const;
/// where do we have to draw the scripts?
bool hasLimits() const;
/// clean up empty cells and return true if a cell has been deleted. /// clean up empty cells and return true if a cell has been deleted.
bool notifyCursorLeaves(Cursor const & old, Cursor & cur); bool notifyCursorLeaves(Cursor const & old, Cursor & cur);
/// possible subscript (index 0) and superscript (index 1) /// possible subscript (index 0) and superscript (index 1)
bool cell_1_is_up_; bool cell_1_is_up_;
/// 1 - "limits", -1 - "nolimits", 0 - "default" /// remember whether we are in display mode (used by mathml output)
int limits_; mutable bool has_limits_ = false;
}; };

View File

@ -27,22 +27,22 @@
#include "support/textutils.h" #include "support/textutils.h"
#include "support/unique_ptr.h" #include "support/unique_ptr.h"
using namespace std;
namespace lyx { namespace lyx {
InsetMathSymbol::InsetMathSymbol(latexkeys const * l) InsetMathSymbol::InsetMathSymbol(latexkeys const * l)
: sym_(l), h_(0), kerning_(0), scriptable_(false) : sym_(l)
{} {}
InsetMathSymbol::InsetMathSymbol(char const * name) InsetMathSymbol::InsetMathSymbol(char const * name)
: sym_(in_word_set(from_ascii(name))), h_(0), : sym_(in_word_set(from_ascii(name)))
kerning_(0), scriptable_(false)
{} {}
InsetMathSymbol::InsetMathSymbol(docstring const & name) InsetMathSymbol::InsetMathSymbol(docstring const & name)
: sym_(in_word_set(name)), h_(0), kerning_(0), scriptable_(false) : sym_(in_word_set(name))
{} {}
@ -58,6 +58,14 @@ docstring InsetMathSymbol::name() const
} }
/// The default limits value
Limits InsetMathSymbol::defaultLimits() const
{
return (allowsLimitsChange() && sym_->extra != "func")
? LIMITS : NO_LIMITS;
}
void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const
{ {
mathedSymbolDim(mi.base, dim, sym_); mathedSymbolDim(mi.base, dim, sym_);
@ -82,16 +90,6 @@ void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const
dim.asc += h_; dim.asc += h_;
dim.des -= h_; dim.des -= h_;
} }
// set scriptable_
//FIXME: get rid of that? Only "funclim" probably, along with
// class==MC_OP. The issue is to implement \limits properly.
scriptable_ = false;
if (mi.base.font.style() == DISPLAY_STYLE)
if (sym_->inset == "cmex" || sym_->inset == "esint" ||
sym_->extra == "funclim" ||
(sym_->inset == "stmry" && sym_->extra == "mathop"))
scriptable_ = true;
} }
@ -122,23 +120,6 @@ MathClass InsetMathSymbol::mathClass() const
} }
bool InsetMathSymbol::isScriptable() const
{
return scriptable_;
}
bool InsetMathSymbol::takesLimits() const
{
return
sym_->inset == "cmex" ||
sym_->inset == "lyxboldsymb" ||
sym_->inset == "esint" ||
sym_->extra == "funclim" ||
(sym_->inset == "stmry" && sym_->extra == "mathop");
}
void InsetMathSymbol::normalize(NormalStream & os) const void InsetMathSymbol::normalize(NormalStream & os) const
{ {
os << "[symbol " << name() << ']'; os << "[symbol " << name() << ']';
@ -242,6 +223,7 @@ void InsetMathSymbol::write(WriteStream & os) const
return; return;
os.pendingSpace(true); os.pendingSpace(true);
writeLimits(os);
} }

View File

@ -19,9 +19,7 @@ namespace lyx {
class latexkeys; class latexkeys;
/** "normal" symbols that don't take limits and don't grow in displayed // \xxx symbols that may take limits or grow in displayed formulæ.
* formulae.
*/
class InsetMathSymbol : public InsetMath { class InsetMathSymbol : public InsetMath {
public: public:
/// ///
@ -43,10 +41,12 @@ public:
MathClass mathClass() const; MathClass mathClass() const;
/// ///
bool isOrdAlpha() const; bool isOrdAlpha() const;
/// do we take scripts? /// The default limits value
bool isScriptable() const; Limits defaultLimits() const;
/// do we take \limits or \nolimits? /// whether the inset has limit-like sub/superscript
bool takesLimits() const; Limits limits() const { return limits_; }
/// sets types of sub/superscripts
void limits(Limits lim) { limits_ = lim; }
/// identifies SymbolInset as such /// identifies SymbolInset as such
InsetMathSymbol const * asSymbolInset() const { return this; } InsetMathSymbol const * asSymbolInset() const { return this; }
/// the LaTeX name of the symbol (without the backslash) /// the LaTeX name of the symbol (without the backslash)
@ -83,11 +83,14 @@ private:
/// ///
latexkeys const * sym_; latexkeys const * sym_;
/// ///
mutable int h_; Limits limits_ = AUTO_LIMITS;
/// cached superscript kerning
mutable int kerning_; // FIXME: these depend on BufferView
/// ///
mutable bool scriptable_; mutable int h_ = 0;
/// cached superscript kerning
mutable int kerning_ = 0;
}; };
} // namespace lyx } // namespace lyx

View File

@ -966,6 +966,18 @@ MathClass MathData::mathClass() const
} }
MathClass MathData::lastMathClass() const
{
MathClass res = MC_ORD;
for (MathAtom const & at : *this) {
MathClass mc = at->mathClass();
if (mc != MC_UNKNOWN)
return res = mc;
}
return res;
}
ostream & operator<<(ostream & os, MathData const & ar) ostream & operator<<(ostream & os, MathData const & ar)
{ {
odocstringstream oss; odocstringstream oss;

View File

@ -140,8 +140,10 @@ public:
void metricsT(TextMetricsInfo const & mi, Dimension & dim) const; void metricsT(TextMetricsInfo const & mi, Dimension & dim) const;
/// redraw cell using cache metrics information /// redraw cell using cache metrics information
void drawT(TextPainter & pi, int x, int y) const; void drawT(TextPainter & pi, int x, int y) const;
/// approximate the math class of the data /// approximate mathclass of the data
MathClass mathClass() const; MathClass mathClass() const;
/// math class of last interesting element
MathClass lastMathClass() const;
/// access to cached x coordinate of last drawing /// access to cached x coordinate of last drawing
int xo(BufferView const & bv) const; int xo(BufferView const & bv) const;

View File

@ -797,7 +797,6 @@ void Parser::parse2(MathAtom & at, const unsigned flags, const mode_type mode,
bool Parser::parse1(InsetMathGrid & grid, unsigned flags, bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
const mode_type mode, const bool numbered) const mode_type mode, const bool numbered)
{ {
int limits = 0;
InsetMathGrid::row_type cellrow = 0; InsetMathGrid::row_type cellrow = 0;
InsetMathGrid::col_type cellcol = 0; InsetMathGrid::col_type cellcol = 0;
MathData * cell = &grid.cell(grid.index(cellrow, cellcol)); MathData * cell = &grid.cell(grid.index(cellrow, cellcol));
@ -1006,10 +1005,6 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
p->nuc().erase(0); p->nuc().erase(0);
parse(p->cell(p->idxOfScript(up)), FLAG_ITEM, mode); parse(p->cell(p->idxOfScript(up)), FLAG_ITEM, mode);
if (limits) {
p->limits(limits);
limits = 0;
}
} }
else if (t.character() == ']' && (flags & FLAG_BRACK_LAST)) { else if (t.character() == ']' && (flags & FLAG_BRACK_LAST)) {
@ -1408,9 +1403,8 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
} }
else if (t.cs() == "limits" || t.cs() == "nolimits") { else if (t.cs() == "limits" || t.cs() == "nolimits") {
CatCode const cat = nextToken().cat(); if (!cell->empty())
if (cat == catSuper || cat == catSub) cell->back()->limits(t.cs() == "limits" ? LIMITS : NO_LIMITS);
limits = t.cs() == "limits" ? 1 : -1;
else { else {
MathAtom at = createInsetMath(t.cs(), buf); MathAtom at = createInsetMath(t.cs(), buf);
cell->push_back(at); cell->push_back(at);