From 6aa5467320e8d7e6245678d47ec0b6eb853a36df Mon Sep 17 00:00:00 2001 From: Stefan Schimanski Date: Thu, 1 Nov 2007 14:40:15 +0000 Subject: [PATCH] * added non-greedy init mode for macros. If you enter a macro with the cursor, you don't want that it eats the insets on the right. * split up the MathData::updateMacro method into the optional and normal parameter part git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@21334 a592a061-630c-0410-9148-cb99ea01b6c8 --- src/Cursor.cpp | 11 +- src/mathed/MathData.cpp | 213 ++++++++++++++++++++++----------------- src/mathed/MathData.h | 11 +- src/mathed/MathMacro.cpp | 4 +- src/mathed/MathMacro.h | 2 + 5 files changed, 143 insertions(+), 98 deletions(-) diff --git a/src/Cursor.cpp b/src/Cursor.cpp index 54a3b8bb7e..3fa8a4af07 100644 --- a/src/Cursor.cpp +++ b/src/Cursor.cpp @@ -38,10 +38,11 @@ #include "insets/InsetTabular.h" #include "insets/InsetText.h" -#include "mathed/MathData.h" #include "mathed/InsetMath.h" #include "mathed/InsetMathScript.h" #include "mathed/MacroTable.h" +#include "mathed/MathData.h" +#include "mathed/MathMacro.h" #include #include @@ -943,7 +944,13 @@ bool Cursor::macroModeClose() InsetMathNest * const in = inset().asInsetMath()->asNestInset(); if (in && in->interpretString(*this, s)) return true; - plainInsert(createInsetMath(name)); + MathAtom atom = createInsetMath(name); + if (atom.nucleus()->asMacro()) { + // make non-greedy, i.e. don't eat parameters from the right + MathMacro * macro = atom.nucleus()->asMacro(); + macro->setDisplayMode(MathMacro::DISPLAY_NONGREEDY_INIT); + } + plainInsert(atom); return true; } diff --git a/src/mathed/MathData.cpp b/src/mathed/MathData.cpp index f44f287095..b417e4b30a 100644 --- a/src/mathed/MathData.cpp +++ b/src/mathed/MathData.cpp @@ -378,12 +378,18 @@ void MathData::updateMacros(MetricsInfo & mi) || macroInset->optionals() != macroOptionals)) { // is it a virgin macro which was never attached to parameters? bool fromInitToNormalMode - = oldDisplayMode == MathMacro::DISPLAY_INIT + = (oldDisplayMode == MathMacro::DISPLAY_INIT + || oldDisplayMode == MathMacro::DISPLAY_NONGREEDY_INIT) && newDisplayMode == MathMacro::DISPLAY_NORMAL; + bool greedy = (oldDisplayMode != MathMacro::DISPLAY_NONGREEDY_INIT); // attach parameters attachMacroParameters(cur, i, macroNumArgs, macroOptionals, - fromInitToNormalMode); + fromInitToNormalMode, greedy); + + // FIXME: proper anchor handling, this removes the selection + cur.updateInsets(&cur.bottom().inset()); + cur.clearSelection(); } // give macro the chance to adapt to new situation @@ -512,15 +518,15 @@ void MathData::detachMacroParameters(Cursor & cur, const size_type macroPos) void MathData::attachMacroParameters(Cursor & cur, const size_type macroPos, const size_type macroNumArgs, - const int macroOptionals, const bool fromInitToNormalMode) + const int macroOptionals, const bool fromInitToNormalMode, + const bool greedy) { MathMacro * macroInset = operator[](macroPos).nucleus()->asMacro(); // start at atom behind the macro again, maybe with some new arguments from above // to add them back into the macro inset size_t p = macroPos + 1; - size_t j = 0; - std::vectordetachedArgs; + std::vector detachedArgs; MathAtom scriptToPutAround; // find cursor slice again @@ -529,16 +535,61 @@ void MathData::attachMacroParameters(Cursor & cur, if (thisSlice != -1) thisPos = cur[thisSlice].pos(); + // find arguments behind the macro + if (greedy) { + collectOptionalParameters(cur, macroOptionals, detachedArgs, p, + macroPos, thisPos, thisSlice); + collectParameters(cur, macroNumArgs, detachedArgs, p, + scriptToPutAround, + macroPos, thisPos, thisSlice); + } + + // attach arguments back to macro inset + macroInset->attachArguments(detachedArgs, macroNumArgs, macroOptionals); + + // found tail script? E.g. \foo{a}b^x + if (scriptToPutAround.nucleus()) { + // put macro into a script inset + scriptToPutAround.nucleus()->asScriptInset()->nuc()[0] + = operator[](macroPos); + operator[](macroPos) = scriptToPutAround; + + if (thisPos == int(macroPos)) + cur.append(0, 0); + } + + // remove them from the MathData + erase(begin() + macroPos + 1, begin() + p); + + // fix cursor if right of p + if (thisPos >= int(p)) + cur[thisSlice].pos() -= p - (macroPos + 1); + + // was the macro inset just inserted and was now folded? + if (cur[thisSlice].pos() == int(macroPos + 1) + && fromInitToNormalMode + && macroInset->arity() > 0 + && thisSlice + 1 == int(cur.depth())) { + // then enter it if the cursor was just behind + cur[thisSlice].pos() = macroPos; + cur.push_back(CursorSlice(*macroInset)); + macroInset->idxFirst(cur); + } +} + + +void MathData::collectOptionalParameters(Cursor & cur, + const size_type numOptionalParams, std::vector & params, + size_t & pos, const pos_type macroPos, const int thisPos, const int thisSlice) +{ // insert optional arguments? - for (; j < macroOptionals && p < size(); ++j) { + while (params.size() < numOptionalParams && pos < size()) { // is a [] block following which could be an optional parameter? - if (operator[](p)->getChar() != '[') { - detachedArgs.push_back(MathData()); - continue; - } + if (operator[](pos)->getChar() != '[') + break; - // found optional argument, look for "]" - size_t right = p + 1; + // found possible optional argument, look for "]" + size_t right = pos + 1; for (; right < size(); ++right) { if (operator[](right)->getChar() == ']') // found right end @@ -546,130 +597,106 @@ void MathData::attachMacroParameters(Cursor & cur, } // found? - if (right < size()) { - // add everything between [ and ] as optional argument - MathData optarg(begin() + p + 1, begin() + right); - // a brace? - bool brace = false; - if (optarg.size() == 1 && optarg[0]->asBraceInset()) { - brace = true; - detachedArgs.push_back(optarg[0]->asBraceInset()->cell(0)); - } else - detachedArgs.push_back(optarg); - // place cursor in optional argument of macro - if (thisPos >= int(p) && thisPos <= int(right)) { - int pos = std::max(0, thisPos - int(p) - 1); - std::vector x; - cur.cutOff(thisSlice, x); - cur[thisSlice].pos() = macroPos; - if (brace) { - pos = x[0].pos(); - x.erase(x.begin()); - } - cur.append(0, pos); - cur.append(x); - } - p = right + 1; - } else { + if (right >= size()) { // no ] found, so it's not an optional argument - // Note: This was "macroPos = p" before, which probably - // does not make sense. We want to stop with optional - // argument handling instead, so go back to the beginning. - j = 0; break; } + + // add everything between [ and ] as optional argument + MathData optarg(begin() + pos + 1, begin() + right); + + // a brace? + bool brace = false; + if (optarg.size() == 1 && optarg[0]->asBraceInset()) { + brace = true; + params.push_back(optarg[0]->asBraceInset()->cell(0)); + } else + params.push_back(optarg); + + // place cursor in optional argument of macro + if (thisPos >= int(pos) && thisPos <= int(right)) { + int paramPos = std::max(0, thisPos - int(pos) - 1); + std::vector x; + cur.cutOff(thisSlice, x); + cur[thisSlice].pos() = macroPos; + if (brace) { + paramPos = x[0].pos(); + x.erase(x.begin()); + } + cur.append(0, paramPos); + cur.append(x); + } + pos = right + 1; } - - // insert normal arguments - for (; j < macroNumArgs && p < size(); ++j) { - MathAtom & cell = operator[](p); + // fill up empty optional parameters + while (params.size() < numOptionalParams) { + params.push_back(MathData()); + } +} + + +void MathData::collectParameters(Cursor & cur, + const size_type numParams, std::vector & params, + size_t & pos, MathAtom & scriptToPutAround, + const pos_type macroPos, const int thisPos, const int thisSlice) +{ + // insert normal arguments + for (; params.size() < numParams && pos < size();) { + MathAtom & cell = operator[](pos); + // fix cursor std::vector argSlices; int argPos = 0; - if (thisPos == int(p)) { + if (thisPos == int(pos)) { cur.cutOff(thisSlice, argSlices); } + // which kind of parameter is it? In {}? With index x^n? InsetMathBrace const * brace = cell->asBraceInset(); if (brace) { // found brace, convert into argument - detachedArgs.push_back(brace->cell(0)); + params.push_back(brace->cell(0)); // cursor inside of the brace or just in front of? - if (thisPos == int(p) && !argSlices.empty()) { + if (thisPos == int(pos) && !argSlices.empty()) { argPos = argSlices[0].pos(); argSlices.erase(argSlices.begin()); } - } else if (cell->asScriptInset() && j + 1 == macroNumArgs) { + } else if (cell->asScriptInset() && params.size() + 1 == numParams) { // last inset with scripts without braces // -> they belong to the macro, not the argument InsetMathScript * script = cell.nucleus()->asScriptInset(); if (script->nuc().size() == 1 && script->nuc()[0]->asBraceInset()) // nucleus in brace? Unpack! - detachedArgs.push_back(script->nuc()[0]->asBraceInset()->cell(0)); + params.push_back(script->nuc()[0]->asBraceInset()->cell(0)); else - detachedArgs.push_back(script->nuc()); + params.push_back(script->nuc()); // script will be put around below scriptToPutAround = cell; // this should only happen after loading, so make cursor handling simple - if (thisPos >= int(macroPos) && thisPos <= int(macroPos + macroNumArgs)) { + if (thisPos >= int(macroPos) && thisPos <= int(macroPos + numParams)) { argSlices.clear(); cur.append(0, 0); } } else { + // the simplest case: plain inset MathData array; array.insert(0, cell); - detachedArgs.push_back(array); + params.push_back(array); } // put cursor in argument again - if (thisPos == int(p)) { - cur.append(j, argPos); + if (thisPos == int(pos)) { + cur.append(params.size() - 1, argPos); cur.append(argSlices); cur[thisSlice].pos() = macroPos; } - ++p; - } - - // attach arguments back to macro inset - macroInset->attachArguments(detachedArgs, macroNumArgs, macroOptionals); - - // found tail script? E.g. \foo{a}b^x - if (scriptToPutAround.nucleus()) { - // put macro into a script inset - scriptToPutAround.nucleus()->asScriptInset()->nuc()[0] - = operator[](macroPos); - operator[](macroPos) = scriptToPutAround; - - if (thisPos == int(macroPos)) - cur.append(0, 0); - } - - // remove them from the MathData - erase(begin() + macroPos + 1, begin() + p); - - // fix cursor if right of p - if (thisPos >= int(p)) - cur[thisSlice].pos() -= p - (macroPos + 1); - - // was the macro inset just inserted and was now folded? - if (cur[thisSlice].pos() == int(macroPos + 1) - && fromInitToNormalMode - && macroInset->arity() > 0 - && thisSlice + 1 == int(cur.depth())) { - // then enter it if the cursor was just behind - cur[thisSlice].pos() = macroPos; - cur.push_back(CursorSlice(*macroInset)); - macroInset->idxFirst(cur); - } - - // FIXME: proper anchor handling, this removes the selection - cur.updateInsets(&cur.bottom().inset()); - cur.clearSelection(); + ++pos; + } } diff --git a/src/mathed/MathData.h b/src/mathed/MathData.h index 0e217a2f31..e6c4c378a4 100644 --- a/src/mathed/MathData.h +++ b/src/mathed/MathData.h @@ -173,7 +173,16 @@ private: /// void attachMacroParameters(Cursor & cur, const size_type macroPos, const size_type macroNumArgs, const int macroOptionals, - const bool fromInitToNormalMode); + const bool fromInitToNormalMode, const bool greedy); + /// + void collectOptionalParameters(Cursor & cur, + const size_type numOptionalParams, std::vector & params, + size_t & pos, const pos_type macroPos, const int thisPos, const int thisSlice); + /// + void collectParameters(Cursor & cur, + const size_type numParams, std::vector & params, + size_t & pos, MathAtom & scriptToPutAround, + const pos_type macroPos, const int thisPos, const int thisSlice); /// mutable std::vector atom_dims_; }; diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp index e664cf0cab..67a97cbd4d 100644 --- a/src/mathed/MathMacro.cpp +++ b/src/mathed/MathMacro.cpp @@ -158,7 +158,7 @@ bool MathMacro::editMode(Cursor const & cur) const { void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const { // calculate new metrics according to display mode - if (displayMode_ == DISPLAY_INIT) { + if (displayMode_ == DISPLAY_INIT || displayMode_ == DISPLAY_NONGREEDY_INIT) { mathed_string_dim(mi.base.font, from_ascii("\\") + name(), dim); } else if (displayMode_ == DISPLAY_UNFOLDED) { cell(0).metrics(mi, dim); @@ -273,7 +273,7 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const int expx = x; int expy = y; - if (displayMode_ == DISPLAY_INIT) { + if (displayMode_ == DISPLAY_INIT || displayMode_ == DISPLAY_NONGREEDY_INIT) { PainterInfo pi2(pi.base.bv, pi.pain); pi2.base.font.setColor(macro_ ? Color_latex : Color_error); //pi2.base.style = LM_ST_TEXT; diff --git a/src/mathed/MathMacro.h b/src/mathed/MathMacro.h index 88abdd0d85..45239e83eb 100644 --- a/src/mathed/MathMacro.h +++ b/src/mathed/MathMacro.h @@ -87,6 +87,7 @@ public: enum DisplayMode { DISPLAY_INIT, + DISPLAY_NONGREEDY_INIT, DISPLAY_UNFOLDED, DISPLAY_NORMAL, }; @@ -121,6 +122,7 @@ public: protected: friend class MathData; friend class ArgumentProxy; + friend class Cursor; /// update the display mode (should only be called after detaching arguments) void setDisplayMode(DisplayMode mode); -- 2.39.2