X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2FMathMacro.cpp;h=f4150f30f84250533ff313105aa5ebfda07605cb;hb=ff85a4902ec4fff1b3bf0dbc9c9c53b3e87563cf;hp=37c393e01014de2f76573dce245b8158efccd9eb;hpb=501763cafaed5d6c7c0228c2919bc3e3c297774a;p=lyx.git diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp index 37c393e010..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. @@ -13,9 +13,13 @@ #include #include "MathMacro.h" -#include "MathSupport.h" + +#include "InsetMathChar.h" +#include "MathCompletionList.h" #include "MathExtern.h" +#include "MathFactory.h" #include "MathStream.h" +#include "MathSupport.h" #include "Buffer.h" #include "BufferView.h" @@ -24,17 +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 { @@ -53,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)) { @@ -111,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) {} @@ -122,17 +137,26 @@ 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) return asString(cell(0)); - else - return name_; + + return name_; } @@ -153,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 @@ -192,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: - BOOST_ASSERT(macro_ != 0); + LBUFERR(macro_); Dimension fontDim; FontInfo labelFont = sane_font; math_font_max_dim(labelFont, fontDim.asc, fontDim.des); @@ -229,15 +256,20 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const dim.wid += 2; metricsMarkers2(dim); } else { - BOOST_ASSERT(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 @@ -272,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; @@ -283,8 +315,27 @@ void MathMacro::updateMacro(MacroContext const & mc) } -void MathMacro::updateRepresentation(Cursor const * bvCur) +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; @@ -292,35 +343,33 @@ void MathMacro::updateRepresentation(Cursor const * bvCur) // 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_) { - needsUpdate_ = false; - - // get default values of macro - vector const & defaults = macro_->defaults(); - - // create MathMacroArgumentValue objects pointing to the cells of the macro - vector values(nargs()); - for (size_t i = 0; i < nargs(); ++i) { - ArgumentProxy * proxy; - if (i < defaults.size()) - proxy = new ArgumentProxy(*this, i, defaults[i]); - else - 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 - docstring const & display = macro_->display(); - asArray(display.empty() ? macro_->definition() : display, definition_); + needsUpdate_ = false; + + // get default values of macro + vector const & defaults = macro_->defaults(); + + // create MathMacroArgumentValue objects pointing to the cells of the macro + vector values(nargs()); + for (size_t i = 0; i < nargs(); ++i) { + ArgumentProxy * proxy; + if (i < defaults.size()) + proxy = new ArgumentProxy(*this, i, defaults[i]); + else + proxy = new ArgumentProxy(*this, i); + values[i].insert(0, MathAtom(proxy)); } + // expanding macro with the values + macro_->expand(values, expanded_.cell(0)); + 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_); } @@ -333,23 +382,19 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const int expy = y; if (displayMode_ == DISPLAY_INIT || displayMode_ == DISPLAY_INTERACTIVE_INIT) { - PainterInfo pi2(pi.base.bv, pi.pain); - pi2.base.font.setColor(macro_ ? Color_latex : Color_error); - //pi2.base.style = LM_ST_TEXT; - pi2.pain.text(x, y, from_ascii("\\") + name(), pi2.base.font); + FontSetChanger dummy(pi.base, "lyxtex"); + pi.pain.text(x, y, from_ascii("\\") + name(), pi.base.font); } else if (displayMode_ == DISPLAY_UNFOLDED) { - PainterInfo pi2(pi.base.bv, pi.pain); - pi2.base.font.setColor(macro_ ? Color_latex : Color_error); - //pi2.base.style = LM_ST_TEXT; - pi2.pain.text(x, y, from_ascii("\\"), pi2.base.font); - x += mathed_string_width(pi2.base.font, from_ascii("\\")) + 1; - cell(0).draw(pi2, x, y); - drawMarkers(pi2, expx, expy); + FontSetChanger dummy(pi.base, "lyxtex"); + 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); } else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST && 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 @@ -366,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 @@ -377,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; @@ -437,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); } @@ -486,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? @@ -513,14 +555,20 @@ 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) { + definition_.validate(features); + InsetMathNest::validate(features); + } } void MathMacro::edit(Cursor & cur, bool front, EntryDirection entry_from) { - cur.updateFlags(Update::SinglePar); + cur.screenUpdateFlags(Update::SinglePar); InsetMathNest::edit(cur, front, entry_from); } @@ -529,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; @@ -538,7 +586,7 @@ Inset * MathMacro::editXY(Cursor & cur, int x, int y) void MathMacro::removeArgument(Inset::pos_type pos) { if (displayMode_ == DISPLAY_NORMAL) { - BOOST_ASSERT(size_t(pos) < cells_.size()); + LASSERT(size_t(pos) < cells_.size(), return); cells_.erase(cells_.begin() + pos); if (size_t(pos) < attachedArgsNum_) --attachedArgsNum_; @@ -553,7 +601,7 @@ void MathMacro::removeArgument(Inset::pos_type pos) { void MathMacro::insertArgument(Inset::pos_type pos) { if (displayMode_ == DISPLAY_NORMAL) { - BOOST_ASSERT(size_t(pos) <= cells_.size()); + LASSERT(size_t(pos) <= cells_.size(), return); cells_.insert(cells_.begin() + pos, MathData()); if (size_t(pos) < attachedArgsNum_) ++attachedArgsNum_; @@ -567,7 +615,7 @@ void MathMacro::insertArgument(Inset::pos_type pos) { void MathMacro::detachArguments(vector & args, bool strip) { - BOOST_ASSERT(displayMode_ == DISPLAY_NORMAL); + LASSERT(displayMode_ == DISPLAY_NORMAL, return); args = cells_; // strip off empty cells, but not more than arity-attachedArgsNum_ @@ -588,7 +636,7 @@ void MathMacro::detachArguments(vector & args, bool strip) void MathMacro::attachArguments(vector const & args, size_t arity, int optionals) { - BOOST_ASSERT(displayMode_ == DISPLAY_NORMAL); + LASSERT(displayMode_ == DISPLAY_NORMAL, return); cells_ = args; attachedArgsNum_ = args.size(); cells_.resize(arity); @@ -601,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); } @@ -624,7 +692,7 @@ void MathMacro::fold(Cursor & cur) { if (!nextFoldMode_) { nextFoldMode_ = true; - cur.updateFlags(Update::SinglePar); + cur.screenUpdateFlags(Update::SinglePar); } } @@ -633,7 +701,7 @@ void MathMacro::unfold(Cursor & cur) { if (nextFoldMode_) { nextFoldMode_ = false; - cur.updateFlags(Update::SinglePar); + cur.screenUpdateFlags(Update::SinglePar); } } @@ -646,18 +714,22 @@ bool MathMacro::folded() const void MathMacro::write(WriteStream & os) const { + MathEnsurer ensurer(os, macro_ != 0, true); + // non-normal mode if (displayMode_ != DISPLAY_NORMAL) { - os << "\\" << name() << " "; - os.pendingSpace(true); + os << "\\" << name(); + if (name().size() != 1 || isAlphaASCII(name()[0])) + os.pendingSpace(true); return; } // normal mode - BOOST_ASSERT(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(); @@ -681,10 +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() + && isASCII(cell(i)[0].nucleus()->asCharInset()->getChar())) { if (first) os << " "; os << cell(i); @@ -707,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; } @@ -719,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()); } @@ -770,7 +858,7 @@ bool MathMacro::automaticPopupCompletion() const } -Inset::CompletionList const * +CompletionList const * MathMacro::createCompletionList(Cursor const & cur) const { if (displayMode() != DISPLAY_UNFOLDED) @@ -805,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;