X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2FMathMacro.cpp;h=f4150f30f84250533ff313105aa5ebfda07605cb;hb=ff85a4902ec4fff1b3bf0dbc9c9c53b3e87563cf;hp=e61a09f89d52b756cd3b6bca11221e4f7a4efc4d;hpb=c874e9503093e1f3456ac4e28ce55ade89eb3873;p=lyx.git diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp index e61a09f89d..f4150f30f8 100644 --- a/src/mathed/MathMacro.cpp +++ b/src/mathed/MathMacro.cpp @@ -4,7 +4,7 @@ * Licence details can be found in the file COPYING. * * \author Alejandro Aguilar Sierra - * \author André Pönitz + * \author André Pönitz * \author Stefan Schimanski * * Full author contact details are available in file CREDITS. @@ -17,6 +17,7 @@ #include "InsetMathChar.h" #include "MathCompletionList.h" #include "MathExtern.h" +#include "MathFactory.h" #include "MathStream.h" #include "MathSupport.h" @@ -27,19 +28,21 @@ #include "FuncStatus.h" #include "FuncRequest.h" #include "LaTeXFeatures.h" -#include "LyXFunc.h" +#include "LyX.h" #include "LyXRC.h" -#include "Undo.h" #include "frontends/Painter.h" #include "support/debug.h" +#include "support/gettext.h" #include "support/lassert.h" +#include "support/lstrings.h" #include "support/textutils.h" #include #include +using namespace lyx::support; using namespace std; namespace lyx { @@ -58,17 +61,23 @@ public: asArray(def, def_); } /// + InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; } + /// void metrics(MetricsInfo & mi, Dimension & dim) const { mathMacro_.macro()->unlock(); - if (!mathMacro_.editMetrics(mi.base.bv) + mathMacro_.cell(idx_).metrics(mi, dim); + + if (!mathMacro_.editMetrics(mi.base.bv) && mathMacro_.cell(idx_).empty()) def_.metrics(mi, dim); - else { - CoordCache & coords = mi.base.bv->coordCache(); - dim = coords.arrays().dim(&mathMacro_.cell(idx_)); - } + mathMacro_.macro()->lock(); } + // FIXME Other external things need similar treatment. + /// + void mathmlize(MathStream & ms) const { ms << mathMacro_.cell(idx_); } + /// + void htmlize(HtmlStream & ms) const { ms << mathMacro_.cell(idx_); } /// void draw(PainterInfo & pi, int x, int y) const { if (mathMacro_.editMetrics(pi.base.bv)) { @@ -116,10 +125,11 @@ private: }; -MathMacro::MathMacro(docstring const & name) - : InsetMathNest(0), name_(name), displayMode_(DISPLAY_INIT), - attachedArgsNum_(0), optionals_(0), nextFoldMode_(true), - macro_(0), needsUpdate_(false), appetite_(9) +MathMacro::MathMacro(Buffer * buf, docstring const & name) + : InsetMathNest(buf, 0), name_(name), displayMode_(DISPLAY_INIT), + expanded_(buf), attachedArgsNum_(0), optionals_(0), nextFoldMode_(true), + macroBackup_(buf), macro_(0), needsUpdate_(false), + isUpdating_(false), appetite_(9) {} @@ -127,14 +137,23 @@ Inset * MathMacro::clone() const { MathMacro * copy = new MathMacro(*this); copy->needsUpdate_ = true; - copy->expanded_.cell(0).clear(); + //copy->expanded_.cell(0).clear(); return copy; } +void MathMacro::normalize(NormalStream & os) const +{ + os << "[macro " << name(); + for (size_t i = 0; i < nargs(); ++i) + os << ' ' << cell(i); + os << ']'; +} + + docstring MathMacro::name() const { - if (displayMode_ == DISPLAY_UNFOLDED && name_.size() != 1) + if (displayMode_ == DISPLAY_UNFOLDED) return asString(cell(0)); return name_; @@ -158,9 +177,12 @@ bool MathMacro::editMode(BufferView const * bv) const { // look if there is no other macro in edit mode above ++i; for (; i != cur.depth(); ++i) { - MathMacro const * macro = dynamic_cast(&cur[i].inset()); - if (macro && macro->displayMode() == DISPLAY_NORMAL) - return false; + InsetMath * im = cur[i].asInsetMath(); + if (im) { + MathMacro const * macro = im->asMacro(); + if (macro && macro->displayMode() == DISPLAY_NORMAL) + return false; + } } // ok, none found, I am the highest one @@ -197,7 +219,7 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const && editing_[mi.base.bv]) { // Macro will be edited in a old-style list mode here: - LASSERT(macro_ != 0, /**/); + LBUFERR(macro_); Dimension fontDim; FontInfo labelFont = sane_font; math_font_max_dim(labelFont, fontDim.asc, fontDim.des); @@ -234,15 +256,20 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const dim.wid += 2; metricsMarkers2(dim); } else { - LASSERT(macro_ != 0, /**/); + LBUFERR(macro_); - // metrics are computed here for the cells, - // in the proxy we will then use the dim from the cache - InsetMathNest::metrics(mi); - - // calculate metrics finally + // calculate metrics, hoping that all cells are seen macro_->lock(); expanded_.cell(0).metrics(mi, dim); + + // otherwise do a manual metrics call + CoordCache & coords = mi.base.bv->coordCache(); + for (idx_type i = 0; i < nargs(); ++i) { + if (!coords.getArrays().hasDim(&cell(i))) { + Dimension tdim; + cell(i).metrics(mi, tdim); + } + } macro_->unlock(); // calculate dimension with label while editing @@ -277,7 +304,7 @@ int MathMacro::kerning(BufferView const * bv) const { void MathMacro::updateMacro(MacroContext const & mc) { if (validName()) { - macro_ = mc.get(name()); + macro_ = mc.get(name()); if (macro_ && macroBackup_ != *macro_) { macroBackup_ = *macro_; needsUpdate_ = true; @@ -288,8 +315,27 @@ void MathMacro::updateMacro(MacroContext const & mc) } -void MathMacro::updateRepresentation() +class MathMacro::UpdateLocker { +public: + explicit UpdateLocker(MathMacro & mm) : mac(mm) + { + mac.isUpdating_ = true; + } + ~UpdateLocker() { mac.isUpdating_ = false; } +private: + MathMacro & mac; +}; + + +void MathMacro::updateRepresentation(Cursor * cur, MacroContext const & mc, + UpdateType utype) +{ + if (isUpdating_) + return; + + UpdateLocker locker(*this); + // known macro? if (macro_ == 0) return; @@ -297,14 +343,11 @@ void MathMacro::updateRepresentation() // update requires requires_ = macro_->requires(); - // non-normal mode? We are done! - if (displayMode_ != DISPLAY_NORMAL) + if (!needsUpdate_ + // non-normal mode? We are done! + || (displayMode_ != DISPLAY_NORMAL)) return; - // macro changed? - if (!needsUpdate_) - return; - needsUpdate_ = false; // get default values of macro @@ -320,10 +363,11 @@ void MathMacro::updateRepresentation() proxy = new ArgumentProxy(*this, i); values[i].insert(0, MathAtom(proxy)); } - // expanding macro with the values macro_->expand(values, expanded_.cell(0)); - // get definition for list edit mode + if (utype == OutputUpdate && !expanded_.cell(0).empty()) + expanded_.cell(0).updateMacros(cur, mc, utype); + // get definition for list edit mode docstring const & display = macro_->display(); asArray(display.empty() ? macro_->definition() : display, definition_); } @@ -350,7 +394,7 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const && editing_[pi.base.bv]) { // Macro will be edited in a old-style list mode here: - CoordCache & coords = pi.base.bv->coordCache(); + CoordCache const & coords = pi.base.bv->coordCache(); FontInfo const & labelFont = sane_font; // markers and box needs two pixels @@ -367,8 +411,7 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const // draw definition definition_.draw(pi, x, y); - Dimension defDim - = coords.arrays().dim(&definition_); + Dimension const & defDim = coords.getArrays().dim(&definition_); y += max(fontDim.des, defDim.des); // draw parameters @@ -378,8 +421,7 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const for (idx_type i = 0; i < nargs(); ++i) { // position of label - Dimension cdim - = coords.arrays().dim(&cell(i)); + Dimension const & cdim = coords.getArrays().dim(&cell(i)); x = expx + 2; y += max(fontDim.asc, cdim.asc) + 1; @@ -438,14 +480,14 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const // edit mode changed? if (editing_[pi.base.bv] != editMode(pi.base.bv)) - pi.base.bv->cursor().updateFlags(Update::SinglePar); + pi.base.bv->cursor().screenUpdateFlags(Update::SinglePar); } void MathMacro::drawSelection(PainterInfo & pi, int x, int y) const { // We may have 0 arguments, but InsetMathNest requires at least one. - if (cells_.size() > 0) + if (!cells_.empty()) InsetMathNest::drawSelection(pi, x, y); } @@ -487,8 +529,7 @@ bool MathMacro::validName() const { docstring n = name(); - // empty name? - if (n.size() == 0) + if (n.empty()) return false; // converting back and force doesn't swallow anything? @@ -514,8 +555,8 @@ void MathMacro::validate(LaTeXFeatures & features) const if (!requires_.empty()) features.require(requires_); - if (name() == "binom" || name() == "mathcircumflex") - features.require(to_utf8(name())); + if (name() == "binom") + features.require("binom"); // validate the cells and the definition if (displayMode() == DISPLAY_NORMAL) { @@ -527,7 +568,7 @@ void MathMacro::validate(LaTeXFeatures & features) const void MathMacro::edit(Cursor & cur, bool front, EntryDirection entry_from) { - cur.updateFlags(Update::SinglePar); + cur.screenUpdateFlags(Update::SinglePar); InsetMathNest::edit(cur, front, entry_from); } @@ -536,7 +577,7 @@ Inset * MathMacro::editXY(Cursor & cur, int x, int y) { // We may have 0 arguments, but InsetMathNest requires at least one. if (nargs() > 0) { - cur.updateFlags(Update::SinglePar); + cur.screenUpdateFlags(Update::SinglePar); return InsetMathNest::editXY(cur, x, y); } else return this; @@ -545,7 +586,7 @@ Inset * MathMacro::editXY(Cursor & cur, int x, int y) void MathMacro::removeArgument(Inset::pos_type pos) { if (displayMode_ == DISPLAY_NORMAL) { - LASSERT(size_t(pos) < cells_.size(), /**/); + LASSERT(size_t(pos) < cells_.size(), return); cells_.erase(cells_.begin() + pos); if (size_t(pos) < attachedArgsNum_) --attachedArgsNum_; @@ -560,7 +601,7 @@ void MathMacro::removeArgument(Inset::pos_type pos) { void MathMacro::insertArgument(Inset::pos_type pos) { if (displayMode_ == DISPLAY_NORMAL) { - LASSERT(size_t(pos) <= cells_.size(), /**/); + LASSERT(size_t(pos) <= cells_.size(), return); cells_.insert(cells_.begin() + pos, MathData()); if (size_t(pos) < attachedArgsNum_) ++attachedArgsNum_; @@ -574,7 +615,7 @@ void MathMacro::insertArgument(Inset::pos_type pos) { void MathMacro::detachArguments(vector & args, bool strip) { - LASSERT(displayMode_ == DISPLAY_NORMAL, /**/); + LASSERT(displayMode_ == DISPLAY_NORMAL, return); args = cells_; // strip off empty cells, but not more than arity-attachedArgsNum_ @@ -595,7 +636,7 @@ void MathMacro::detachArguments(vector & args, bool strip) void MathMacro::attachArguments(vector const & args, size_t arity, int optionals) { - LASSERT(displayMode_ == DISPLAY_NORMAL, /**/); + LASSERT(displayMode_ == DISPLAY_NORMAL, return); cells_ = args; attachedArgsNum_ = args.size(); cells_.resize(arity); @@ -608,21 +649,41 @@ void MathMacro::attachArguments(vector const & args, size_t arity, int bool MathMacro::idxFirst(Cursor & cur) const { - cur.updateFlags(Update::SinglePar); + cur.screenUpdateFlags(Update::SinglePar); return InsetMathNest::idxFirst(cur); } bool MathMacro::idxLast(Cursor & cur) const { - cur.updateFlags(Update::SinglePar); + cur.screenUpdateFlags(Update::SinglePar); return InsetMathNest::idxLast(cur); } bool MathMacro::notifyCursorLeaves(Cursor const & old, Cursor & cur) { - cur.updateFlags(Update::Force); + if (displayMode_ == DISPLAY_UNFOLDED) { + docstring const & unfolded_name = name(); + if (unfolded_name != name_) { + // The macro name was changed + Cursor inset_cursor = old; + int macroSlice = inset_cursor.find(this); + // returning true means the cursor is "now" invalid, + // which it was. + LASSERT(macroSlice != -1, return true); + inset_cursor.cutOff(macroSlice); + inset_cursor.recordUndoInset(); + inset_cursor.pop(); + inset_cursor.cell().erase(inset_cursor.pos()); + inset_cursor.cell().insert(inset_cursor.pos(), + createInsetMath(unfolded_name, cur.buffer())); + cur.resetAnchor(); + cur.screenUpdateFlags(cur.result().screenUpdate() | Update::SinglePar); + return true; + } + } + cur.screenUpdateFlags(Update::Force); return InsetMathNest::notifyCursorLeaves(old, cur); } @@ -631,7 +692,7 @@ void MathMacro::fold(Cursor & cur) { if (!nextFoldMode_) { nextFoldMode_ = true; - cur.updateFlags(Update::SinglePar); + cur.screenUpdateFlags(Update::SinglePar); } } @@ -640,7 +701,7 @@ void MathMacro::unfold(Cursor & cur) { if (nextFoldMode_) { nextFoldMode_ = false; - cur.updateFlags(Update::SinglePar); + cur.screenUpdateFlags(Update::SinglePar); } } @@ -664,10 +725,11 @@ void MathMacro::write(WriteStream & os) const } // normal mode - LASSERT(macro_, /**/); + // we should be ok to continue even if this fails. + LATTEST(macro_); - // optional arguments make macros fragile - if (optionals_ > 0 && os.fragile()) + // Always protect macros in a fragile environment + if (os.fragile()) os << "\\protect"; os << "\\" << name(); @@ -691,11 +753,11 @@ void MathMacro::write(WriteStream & os) const // skip the tailing empty optionals i = optionals_; - // Print remaining macros + // Print remaining arguments for (; i < cells_.size(); ++i) { if (cell(i).size() == 1 && cell(i)[0].nucleus()->asCharInset() - && cell(i)[0].nucleus()->asCharInset()->getChar() < 0x80) { + && isASCII(cell(i)[0].nucleus()->asCharInset()->getChar())) { if (first) os << " "; os << cell(i); @@ -718,7 +780,23 @@ void MathMacro::maple(MapleStream & os) const void MathMacro::mathmlize(MathStream & os) const { - lyx::mathmlize(expanded_.cell(0), os); + MathData const & data = expanded_.cell(0); + if (data.empty()) { + // this means that we do not recognize the macro + throw MathExportException(); + } + os << data; +} + + +void MathMacro::htmlize(HtmlStream & os) const +{ + MathData const & data = expanded_.cell(0); + if (data.empty()) { + // this means that we do not recognize the macro + throw MathExportException(); + } + os << data; } @@ -730,14 +808,13 @@ void MathMacro::octave(OctaveStream & os) const void MathMacro::infoize(odocstream & os) const { - os << "Macro: " << name(); + os << bformat(_("Macro: %1$s"), name()); } void MathMacro::infoize2(odocstream & os) const { - os << "Macro: " << name(); - + os << bformat(_("Macro: %1$s"), name()); } @@ -816,13 +893,13 @@ bool MathMacro::insertCompletion(Cursor & cur, docstring const & s, docstring newName = name() + s; asArray(newName, cell(0)); cur.bv().cursor().pos() = name().size(); - cur.updateFlags(Update::SinglePar); + cur.screenUpdateFlags(Update::SinglePar); // finish macro if (finished) { cur.bv().cursor().pop(); ++cur.bv().cursor().pos(); - cur.updateFlags(Update::SinglePar); + cur.screenUpdateFlags(Update::SinglePar); } return true;