X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2Fmath_cursor.C;h=36834e7612654ec9826a7aa0dd2ef95018f9a7f7;hb=06f9f0ea08ddcf13e99ea02cff21471aa2020c9e;hp=bdca913e107a68aa4efd3c2cd384f69d8a992bf9;hpb=7f7275c914f22c3f83199958f1095d627d824123;p=lyx.git diff --git a/src/mathed/math_cursor.C b/src/mathed/math_cursor.C index bdca913e10..36834e7612 100644 --- a/src/mathed/math_cursor.C +++ b/src/mathed/math_cursor.C @@ -15,33 +15,41 @@ * the GNU General Public Licence version 2 or later. */ +#include +#include + #ifdef __GNUG__ #pragma implementation #endif -#include -#include -#include - #include "support/lstrings.h" #include "support/LAssert.h" #include "debug.h" #include "LColor.h" -#include "Painter.h" -#include "math_support.h" -#include "formulabase.h" +#include "frontends/Painter.h" #include "math_cursor.h" -#include "math_casesinset.h" -#include "math_factory.h" +#include "formulabase.h" #include "math_arrayinset.h" #include "math_braceinset.h" +#include "math_boxinset.h" +#include "math_casesinset.h" #include "math_charinset.h" #include "math_deliminset.h" +#include "math_factory.h" #include "math_hullinset.h" +#include "math_iterator.h" +#include "math_macroarg.h" +#include "math_macrotemplate.h" +#include "math_mathmlstream.h" +#include "math_parser.h" +#include "math_replace.h" #include "math_scriptinset.h" #include "math_spaceinset.h" #include "math_specialcharinset.h" -#include "math_mathmlstream.h" +#include "math_support.h" + +#include +#include #define FILEDEBUG 0 @@ -49,25 +57,54 @@ using std::endl; using std::min; using std::max; using std::swap; -using std::isalnum; +using std::vector; +using std::ostringstream; namespace { struct Selection { + typedef MathInset::col_type col_type; + typedef MathInset::row_type row_type; + typedef MathInset::idx_type idx_type; + + Selection() + : data_(1, 1) + {} + + void region(MathCursorPos const & i1, MathCursorPos const & i2, + row_type & r1, row_type & r2, col_type & c1, col_type & c2) + { + MathInset * p = i1.par_; + c1 = p->col(i1.idx_); + c2 = p->col(i2.idx_); + if (c1 > c2) + swap(c1, c2); + r1 = p->row(i1.idx_); + r2 = p->row(i2.idx_); + if (r1 > r2) + swap(r1, r2); + } + void grab(MathCursor const & cursor) { - data_.clear(); MathCursorPos i1; MathCursorPos i2; - cursor.getSelection(i1, i2); - if (i1.idx_ == i2.idx_) - data_.push_back(MathArray(i1.cell(), i1.pos_, i2.pos_)); - else { - std::vector indices = - (*i1.par_)->idxBetween(i1.idx_, i2.idx_); - for (MathInset::idx_type i = 0; i < indices.size(); ++i) - data_.push_back(i1.cell(indices[i])); + cursor.getSelection(i1, i2); + // shouldn'tt we assert on i1.par_ == i2.par_? + if (i1.idx_ == i2.idx_) { + data_ = MathGridInset(1, 1); + data_.cell(0) = MathArray(i1.cell(), i1.pos_, i2.pos_); + } else { + row_type r1, r2; + col_type c1, c2; + region(i1, i2, r1, r2, c1, c2); + data_ = MathGridInset(c2 - c1 + 1, r2 - r1 + 1); + for (row_type row = 0; row < data_.nrows(); ++row) + for (col_type col = 0; col < data_.ncols(); ++col) { + idx_type i = i1.par_->index(row + r1, col + c1); + data_.cell(data_.index(row, col)) = i1.par_->cell(i); + } } } @@ -75,58 +112,77 @@ struct Selection { MathCursorPos i1; MathCursorPos i2; - cursor.getSelection(i1, i2); + cursor.getSelection(i1, i2); if (i1.idx_ == i2.idx_) i1.cell().erase(i1.pos_, i2.pos_); else { - std::vector indices = - (*i1.par_)->idxBetween(i1.idx_, i2.idx_); - for (unsigned i = 0; i < indices.size(); ++i) - i1.cell(indices[i]).erase(); + MathInset * p = i1.par_; + row_type r1, r2; + col_type c1, c2; + region(i1, i2, r1, r2, c1, c2); + for (row_type row = r1; row <= r2; ++row) + for (col_type col = c1; col <= c2; ++col) + p->cell(p->index(row, col)).erase(); } cursor.cursor() = i1; } void paste(MathCursor & cursor) const { - MathArray ar = glue(); - cursor.paste(ar); + if (data_.nargs() == 1) { + // single cell/part of cell + cursor.paste(data_.cell(0)); + } else { + // mulitple cells + idx_type idx; // index of upper left cell + MathGridInset * p = cursor.enclosingGrid(idx); + col_type const numcols = min(data_.ncols(), p->ncols() - p->col(idx)); + row_type const numrows = min(data_.nrows(), p->nrows() - p->row(idx)); + for (row_type row = 0; row < numrows; ++row) { + for (col_type col = 0; col < numcols; ++col) { + idx_type i = p->index(row + p->row(idx), col + p->col(idx)); + p->cell(i).push_back(data_.cell(data_.index(row, col))); + } + // append the left over horizontal cells to the last column + idx_type i = p->index(row + p->row(idx), p->ncols() - 1); + for (col_type col = numcols; col < data_.ncols(); ++col) + p->cell(i).push_back(data_.cell(data_.index(row, col))); + } + // append the left over vertical cells to the last _cell_ + idx_type i = p->nargs() - 1; + for (row_type row = numrows; row < data_.nrows(); ++row) + for (col_type col = 0; col < data_.ncols(); ++col) + p->cell(i).push_back(data_.cell(data_.index(row, col))); + } } // glues selection to one cell MathArray glue() const { MathArray ar; - for (unsigned i = 0; i < data_.size(); ++i) - ar.push_back(data_[i]); + for (unsigned i = 0; i < data_.nargs(); ++i) + ar.push_back(data_.cell(i)); return ar; } void clear() { - data_.clear(); + data_ = MathGridInset(1, 1); } - std::vector data_; + MathGridInset data_; }; Selection theSelection; -#if FILEDEBUG -std::ostream & operator<<(std::ostream & os, MathCursorPos const & p) -{ - os << "(par: " << p.par_ << " idx: " << p.idx_ << " pos: " << p.pos_ << ")"; - return os; -} -#endif } MathCursor::MathCursor(InsetFormulaBase * formula, bool left) - : formula_(formula), lastcode_(LM_TC_VAR), selection_(false) + : formula_(formula), lastcode_(LM_TC_MIN), selection_(false) { left ? first() : last(); } @@ -134,11 +190,7 @@ MathCursor::MathCursor(InsetFormulaBase * formula, bool left) void MathCursor::push(MathAtom & t) { - MathCursorPos p; - p.par_ = &t; - p.idx_ = 0; - p.pos_ = 0; - Cursor_.push_back(p); + Cursor_.push_back(MathCursorPos(t.nucleus())); } @@ -161,14 +213,10 @@ void MathCursor::pushRight(MathAtom & t) bool MathCursor::popLeft() { - //cerr << "Leaving atom "; par()->write(cerr, false); cerr << " left\n"; + //cerr << "Leaving atom to the left\n"; if (Cursor_.size() <= 1) return false; - //if (nextInset()) - // nextInset()->removeEmptyScripts(); Cursor_.pop_back(); - //if (nextAtom()) - // nextAtom()->removeEmptyScripts(); return true; } @@ -178,11 +226,7 @@ bool MathCursor::popRight() //cerr << "Leaving atom "; par()->write(cerr, false); cerr << " right\n"; if (Cursor_.size() <= 1) return false; - //if (nextInset()) - // nextInset()->removeEmptyScripts(); Cursor_.pop_back(); - //if (nextInset()) - // nextInset()->removeEmptyScripts(); posRight(); return true; } @@ -190,61 +234,26 @@ bool MathCursor::popRight() #if FILEDEBUG -void MathCursor::dump(char const * what) const -{ - lyxerr << "MC: " << what << "\n"; - for (unsigned i = 0; i < Cursor_.size(); ++i) - lyxerr << " i: " << i - << " Cursor: pos: " << Cursor_[i].pos_ - << " idx: " << Cursor_[i].idx_ - << " par: " << Cursor_[i].par_ << "\n"; - - for (unsigned i = 0; i < Anchor_.size(); ++i) - lyxerr << " i: " << i - << " Anchor: pos: " << Anchor_[i].pos_ - << " idx: " << Anchor_[i].idx_ - << " par: " << Anchor_[i].par_ << "\n"; - - lyxerr << " sel: " << selection_ << "\n"; -} - - -void MathCursor::seldump(char const * str) const -{ - //lyxerr << "SEL: " << str << ": '" << theSelection << "'\n"; - //dump(" Pos"); - - lyxerr << "\n\n\n=================vvvvvvvvvvvvv======================= " - << str << "\ntheSelection: " << selection_ - << " '" << theSelection.glue() << "'\n"; - for (unsigned int i = 0; i < Cursor_.size(); ++i) - lyxerr << Cursor_[i].par_ << "\n'" << Cursor_[i].cell() << "'\n"; - lyxerr << "\n"; - for (unsigned int i = 0; i < Anchor_.size(); ++i) - lyxerr << Anchor_[i].par_ << "\n'" << Anchor_[i].cell() << "'\n"; - //lyxerr << "\ncursor.pos_: " << pos(); - //lyxerr << "\nanchor.pos_: " << anchor().pos_; - lyxerr << "\n===================^^^^^^^^^^^^=====================\n\n\n"; -} - + void MathCursor::dump(char const * what) const + { + lyxerr << "MC: " << what << "\n"; + lyxerr << " Cursor: " << Cursor_.size() << "\n"; + for (unsigned i = 0; i < Cursor_.size(); ++i) + lyxerr << " i: " << i << " " << Cursor_[i] << "\n"; + lyxerr << " Anchor: " << Anchor_.size() << "\n"; + for (unsigned i = 0; i < Anchor_.size(); ++i) + lyxerr << " i: " << i << " " << Anchor_[i] << "\n"; + lyxerr << " sel: " << selection_ << "\n"; + } #else - -void MathCursor::seldump(char const *) const {} -void MathCursor::dump(char const *) const {} - + void MathCursor::dump(char const *) const {} #endif -UpdatableInset * MathCursor::asHyperActiveInset() const -{ - return par()->asHyperActiveInset(); -} - - bool MathCursor::isInside(MathInset const * p) const { - for (unsigned i = 0; i < Cursor_.size(); ++i) - if (Cursor_[i].par_->nucleus() == p) + for (unsigned i = 0; i < Cursor_.size(); ++i) + if (Cursor_[i].par_ == p) return true; return false; } @@ -252,9 +261,6 @@ bool MathCursor::isInside(MathInset const * p) const bool MathCursor::openable(MathAtom const & t, bool sel) const { - if (t->isHyperActive()) - return true; - if (!t->isActive()) return false; @@ -265,27 +271,13 @@ bool MathCursor::openable(MathAtom const & t, bool sel) const // we can't move into anything new during selection if (Cursor_.size() == Anchor_.size()) return false; - if (&t != Anchor_[Cursor_.size()].par_) + if (t.nucleus() != Anchor_[Cursor_.size()].par_) return false; } return true; } -bool MathCursor::positionable(MathAtom const & t, int x, int y) const -{ - if (selection_) { - // we can't move into anything new during selection - if (Cursor_.size() == Anchor_.size()) - return 0; - //if (t != Anchor_[Cursor_.size()].par_) - // return 0; - } - - return t->nargs() && t->covers(x, y); -} - - bool MathCursor::posLeft() { if (pos() == 0) @@ -309,19 +301,16 @@ bool MathCursor::left(bool sel) dump("Left 1"); if (inMacroMode()) { macroModeClose(); - lastcode_ = LM_TC_VAR; + lastcode_ = LM_TC_MIN; return true; } selHandle(sel); - lastcode_ = LM_TC_VAR; + lastcode_ = LM_TC_MIN; if (hasPrevAtom() && openable(prevAtom(), sel)) { - if (prevAtom()->isHyperActive()) { - lyxerr << "entering hyperactive inset\n"; - } pushRight(prevAtom()); return true; - } + } return posLeft() || idxLeft() || popLeft() || selection_; } @@ -332,19 +321,13 @@ bool MathCursor::right(bool sel) dump("Right 1"); if (inMacroMode()) { macroModeClose(); - lastcode_ = LM_TC_VAR; + lastcode_ = LM_TC_MIN; return true; } selHandle(sel); - lastcode_ = LM_TC_VAR; + lastcode_ = LM_TC_MIN; if (hasNextAtom() && openable(nextAtom(), sel)) { - if (nextAtom()->isHyperActive()) { - lyxerr << "entering hyperactive inset\n"; - int x, y; - getPos(x, y); - nextAtom()->edit(formula()->view(), x, y, 0); - } pushLeft(nextAtom()); return true; } @@ -367,57 +350,46 @@ void MathCursor::last() } -void MathCursor::setPos(int x, int y) +bool positionable(MathCursor::cursor_type const & cursor, + MathCursor::cursor_type const & anchor) { - //dump("setPos 1"); - //lyxerr << "MathCursor::setPos x: " << x << " y: " << y << "\n"; + // avoid deeper nested insets when selecting + if (cursor.size() > anchor.size()) + return false; - macroModeClose(); - lastcode_ = LM_TC_VAR; - first(); + // anchor might be deeper, should have same path then + for (MathCursor::cursor_type::size_type i = 0; i < cursor.size(); ++i) + if (cursor[i].par_ != anchor[i].par_) + return false; - cursor().par_ = &formula_->par(); + // position should be ok. + return true; +} - while (1) { - idx() = 0; - cursor().pos_ = 0; - //lyxerr << "found idx: " << idx() << " cursor: " << pos() << "\n"; - int distmin = 1 << 30; // large enough - for (unsigned int i = 0; i < par()->nargs(); ++i) { - MathXArray const & ar = par()->xcell(i); - int x1 = x - ar.xo(); - int y1 = y - ar.yo(); - MathXArray::size_type c = ar.x2pos(x1); - int xx = abs(x1 - ar.pos2x(c)); - int yy = abs(y1); - //lyxerr << "idx: " << i << " xx: " << xx << " yy: " << yy - // << " c: " << c << " xo: " << ar.xo() << "\n"; - if (yy + xx <= distmin) { - distmin = yy + xx; - idx() = i; - pos() = c; - } - } - //lyxerr << "found idx: " << idx() << " cursor: " - // << pos() << "\n"; - if (hasNextAtom() && positionable(nextAtom(), x, y)) - pushLeft(nextAtom()); - else if (hasPrevAtom() && positionable(prevAtom(), x, y)) - pushRight(prevAtom()); - else - break; + +void MathCursor::setPos(int x, int y) +{ + dump("setPos 1"); + bool res = bruteFind(x, y, + formula()->xlow(), formula()->xhigh(), + formula()->ylow(), formula()->yhigh()); + if (!res) { + // this ccan happen on creation of "math-display" + dump("setPos 1.5"); + first(); } - //dump("setPos 2"); + dump("setPos 2"); } + void MathCursor::home(bool sel) { dump("home 1"); selHandle(sel); macroModeClose(); - lastcode_ = LM_TC_VAR; - if (!par()->idxHome(idx(), pos())) + lastcode_ = LM_TC_MIN; + if (!par()->idxHome(idx(), pos())) popLeft(); dump("home 2"); } @@ -428,7 +400,7 @@ void MathCursor::end(bool sel) dump("end 1"); selHandle(sel); macroModeClose(); - lastcode_ = LM_TC_VAR; + lastcode_ = LM_TC_MIN; if (!par()->idxEnd(idx(), pos())) popRight(); dump("end 2"); @@ -441,6 +413,20 @@ void MathCursor::plainErase() } +void MathCursor::markInsert() +{ + //lyxerr << "inserting mark\n"; + array().insert(pos(), MathAtom(new MathCharInset(0, lastcode_))); +} + + +void MathCursor::markErase() +{ + //lyxerr << "deleting mark\n"; + array().erase(pos()); +} + + void MathCursor::plainInsert(MathAtom const & t) { array().insert(pos(), t); @@ -451,10 +437,17 @@ void MathCursor::plainInsert(MathAtom const & t) void MathCursor::insert(char c, MathTextCodes t) { //lyxerr << "inserting '" << c << "'\n"; + selClearOrDel(); plainInsert(MathAtom(new MathCharInset(c, t))); } +void MathCursor::insert(char c) +{ + insert(c, lastcode_); +} + + void MathCursor::insert(MathAtom const & t) { macroModeClose(); @@ -463,14 +456,14 @@ void MathCursor::insert(MathAtom const & t) if (t->nargs()) selCut(); else - selDel(); + selClearOrDel(); } plainInsert(t); } -void MathCursor::niceInsert(MathAtom const & t) +void MathCursor::niceInsert(MathAtom const & t) { selCut(); insert(t); // inserting invalidates the pointer! @@ -480,10 +473,6 @@ void MathCursor::niceInsert(MathAtom const & t) right(); // do not push for e.g. MathSymbolInset selPaste(); } -#ifdef WITH_WARNINGS -#warning "redraw disabled" -#endif - //p->metrics(p->size()); } @@ -512,7 +501,7 @@ void MathCursor::backspace() if (pos() == 0) { pullArg(false); return; - } + } if (selection_) { selDel(); @@ -522,7 +511,7 @@ void MathCursor::backspace() MathScriptInset * p = prevAtom()->asScriptInset(); if (p) { p->removeScript(p->hasUp()); - // Don't delete if there is anything left + // Don't delete if there is anything left if (p->hasUp() || p->hasDown()) return; } @@ -542,23 +531,21 @@ void MathCursor::erase() return; } - // delete empty cells if necessary - if (array().empty()) { - bool popit; - bool removeit; - par()->idxDelete(idx(), popit, removeit); - if (popit && popLeft() && removeit) - plainErase(); - return; - } + // delete empty cells if possible + if (array().empty()) + if (par()->idxDelete(idx())) + return; - if (pos() == size()) + // old behaviour when in last position of cell + if (pos() == size()) { + par()->idxGlue(idx()); return; + } MathScriptInset * p = nextAtom()->asScriptInset(); if (p) { p->removeScript(p->hasUp()); - // Don't delete if there is anything left + // Don't delete if there is anything left if (p->hasUp() || p->hasDown()) return; } @@ -576,8 +563,14 @@ void MathCursor::delLine() return; } - if (par()->nrows() > 1) - par()->delRow(row()); + if (par()->nrows() > 1) { + // grid are the only things with more than one row... + lyx::Assert(par()->asGridInset()); + par()->asGridInset()->delRow(hullRow()); + } + + if (idx() >= par()->nargs()) + idx() = par()->nargs() - 1; if (pos() > size()) pos() = size(); @@ -589,31 +582,11 @@ bool MathCursor::up(bool sel) dump("up 1"); macroModeClose(); selHandle(sel); - - if (!selection_) { - // check whether we could move into a superscript - if (hasPrevAtom()) { - MathAtom & p = prevAtom(); - if (p->asScriptInset() && p->asScriptInset()->hasUp()) { - pushRight(p); - idx() = 1; - pos() = size(); - return true; - } - } - - if (hasNextAtom()) { - MathAtom & n = nextAtom(); - if (n->asScriptInset() && n->asScriptInset()->hasUp()) { - pushLeft(n); - idx() = 1; - pos() = 0; - return true; - } - } - } - - return goUp() || selection_; + cursor_type save = Cursor_; + if (goUpDown(true)) + return true; + Cursor_ = save; + return selection_; } @@ -622,39 +595,19 @@ bool MathCursor::down(bool sel) dump("down 1"); macroModeClose(); selHandle(sel); - - if (!selection_) { - // check whether we could move into a subscript - if (hasPrevAtom()) { - MathAtom & p = prevAtom(); - if (p->asScriptInset() && p->asScriptInset()->hasDown()) { - pushRight(p); - idx() = 0; - pos() = size(); - return true; - } - } - - if (hasNextAtom()) { - MathAtom & n = nextAtom(); - if (n->asScriptInset() && n->asScriptInset()->hasDown()) { - pushLeft(n); - idx() = 0; - pos() = 0; - return true; - } - } - } - - return goDown() || selection_; + cursor_type save = Cursor_; + if (goUpDown(false)) + return true; + Cursor_ = save; + return selection_; } bool MathCursor::toggleLimits() { - if (!hasPrevAtom()) + if (!hasNextAtom()) return false; - MathScriptInset * t = prevAtom()->asScriptInset(); + MathScriptInset * t = nextAtom()->asScriptInset(); if (!t) return false; int old = t->limits(); @@ -665,19 +618,20 @@ bool MathCursor::toggleLimits() void MathCursor::macroModeClose() { + MathInset::difference_type const t = macroNamePos(); + if (t == -1) + return; string s = macroName(); - if (s.size()) { - size_type old = pos(); - pos() -= s.size(); - array().erase(pos(), old); + array().erase(t, pos()); + pos() = t; + if (s != "\\") interpret(s); - } } -int MathCursor::macroNamePos() const +MathInset::difference_type MathCursor::macroNamePos() const { - for (int i = pos() - 1; i >= 0; --i) { + for (MathInset::difference_type i = pos() - 1; i >= 0; --i) { MathAtom & p = array().at(i); if (p->code() == LM_TC_TEX && p->getChar() == '\\') return i; @@ -689,7 +643,8 @@ int MathCursor::macroNamePos() const string MathCursor::macroName() const { string s; - for (int i = macroNamePos(); i >= 0 && i < int(pos()); ++i) + MathInset::difference_type i = macroNamePos(); + for (; i >= 0 && i < int(pos()); ++i) s += array().at(i)->getChar(); return s; } @@ -697,17 +652,17 @@ string MathCursor::macroName() const void MathCursor::selCopy() { - seldump("selCopy"); + dump("selCopy"); if (selection_) { theSelection.grab(*this); - selClear(); + //selClear(); } } void MathCursor::selCut() { - seldump("selCut"); + dump("selCut"); if (selection_) { theSelection.grab(*this); theSelection.erase(*this); @@ -720,7 +675,7 @@ void MathCursor::selCut() void MathCursor::selDel() { - seldump("selDel"); + dump("selDel"); if (selection_) { theSelection.erase(*this); if (pos() > size()) @@ -732,10 +687,11 @@ void MathCursor::selDel() void MathCursor::selPaste() { - seldump("selPaste"); + dump("selPaste"); + selClearOrDel(); theSelection.paste(*this); - theSelection.grab(*this); - //selClear(); + //theSelection.grab(*this); + selClear(); } @@ -743,8 +699,7 @@ void MathCursor::selHandle(bool sel) { if (sel == selection_) return; - - theSelection.clear(); + //theSelection.clear(); Anchor_ = Cursor_; selection_ = sel; } @@ -752,26 +707,34 @@ void MathCursor::selHandle(bool sel) void MathCursor::selStart() { - seldump("selStart"); - if (selection_) - return; - - theSelection.clear(); + dump("selStart 1"); + //theSelection.clear(); Anchor_ = Cursor_; selection_ = true; + dump("selStart 2"); } void MathCursor::selClear() { - seldump("selClear"); + dump("selClear 1"); selection_ = false; + dump("selClear 2"); +} + + +void MathCursor::selClearOrDel() +{ + if (lyxrc.auto_region_delete) + selDel(); + else + selClear(); } void MathCursor::selGet(MathArray & ar) { - seldump("selGet"); + dump("selGet"); if (!selection_) return; @@ -789,7 +752,6 @@ void MathCursor::drawSelection(Painter & pain) const MathCursorPos i1; MathCursorPos i2; getSelection(i1, i2); - //lyxerr << "selection from: " << i1 << " to " << i2 << "\n"; if (i1.idx_ == i2.idx_) { MathXArray & c = i1.xcell(); @@ -799,8 +761,8 @@ void MathCursor::drawSelection(Painter & pain) const int y2 = c.yo() + c.descent(); pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection); } else { - std::vector indices - = (*i1.par_)->idxBetween(i1.idx_, i2.idx_); + vector indices + = i1.par_->idxBetween(i1.idx_, i2.idx_); for (unsigned i = 0; i < indices.size(); ++i) { MathXArray & c = i1.xcell(indices[i]); int x1 = c.xo(); @@ -819,7 +781,7 @@ void MathCursor::drawSelection(Painter & pain) const int x = c.xo() + c.pos2x(anc.pos_); int y1 = c.yo() - c.ascent(); int y2 = c.yo() + c.descent(); - pain.line(x, y1, x, y2, LColor::mathline); + pain.line(x, y1, x, y2, LColor::math); } #endif } @@ -831,13 +793,13 @@ void MathCursor::handleFont(MathTextCodes t) if (selection_) { MathCursorPos i1; MathCursorPos i2; - getSelection(i1, i2); + getSelection(i1, i2); if (i1.idx_ == i2.idx_) { MathArray & ar = i1.cell(); for (MathInset::pos_type pos = i1.pos_; pos != i2.pos_; ++pos) ar.at(pos)->handleFont(t); } - } else + } else lastcode_ = (lastcode_ == t) ? LM_TC_VAR : t; } @@ -865,17 +827,20 @@ void MathCursor::getPos(int & x, int & y) #warning This should probably take cellXOffset and cellYOffset into account #endif x = xarray().xo() + xarray().pos2x(pos()); + // move cursor visually into empty cells ("blue rectangles"); + if (array().empty()) + x += 2; y = xarray().yo(); } -MathAtom & MathCursor::par() const +MathInset * MathCursor::par() const { - return *cursor().par_; + return cursor().par_; } -InsetFormulaBase const * MathCursor::formula() +InsetFormulaBase * MathCursor::formula() { return formula_; } @@ -911,6 +876,12 @@ bool MathCursor::inMacroMode() const } +bool MathCursor::inMacroArgMode() const +{ + return pos() > 0 && prevAtom()->getChar() == '#'; +} + + bool MathCursor::selection() const { return selection_; @@ -919,17 +890,25 @@ bool MathCursor::selection() const MathGridInset * MathCursor::enclosingGrid(MathCursor::idx_type & idx) const { - for (int i = Cursor_.size() - 1; i >= 0; --i) { - MathGridInset * p = (*Cursor_[i].par_)->asGridInset(); + for (MathInset::difference_type i = Cursor_.size() - 1; i >= 0; --i) { + MathGridInset * p = Cursor_[i].par_->asGridInset(); if (p) { idx = Cursor_[i].idx_; return p; + lyxerr << "found grid and idx: " << idx << "\n"; } } return 0; } +void MathCursor::popToEnclosingGrid() +{ + while (Cursor_.size() && !Cursor_.back().par_->asGridInset()) + Cursor_.pop_back(); +} + + void MathCursor::pullArg(bool goright) { dump("pullarg"); @@ -951,36 +930,68 @@ void MathCursor::pullArg(bool goright) if (popLeft()) { plainErase(); array().insert(pos(), a); - if (goright) + if (goright) pos() += a.size(); + } else { + formula()->mutateToText(); } } -void MathCursor::normalize() const +void MathCursor::touch() { -#ifdef WITH_WARNINGS -#warning This is evil! + cursor_type::const_iterator it = Cursor_.begin(); + cursor_type::const_iterator et = Cursor_.end(); + for ( ; it != et; ++it) + it->xcell().touch(); +} + + +void MathCursor::normalize() +{ +#if 0 + // rebreak + { + MathIterator it = ibegin(formula()->par().nucleus()); + MathIterator et = iend(formula()->par().nucleus()); + for (; it != et; ++it) + if (it.par()->asBoxInset()) + it.par()->asBoxInset()->rebreak(); + } #endif - MathCursor * it = const_cast(this); - if (idx() >= par()->nargs()) { + if (idx() >= par()->nargs()) { lyxerr << "this should not really happen - 1: " << idx() << " " << par()->nargs() << "\n"; dump("error 2"); } - it->idx() = min(idx(), par()->nargs() - 1); + idx() = min(idx(), par()->nargs() - 1); if (pos() > size()) { lyxerr << "this should not really happen - 2: " - << pos() << " " << size() << " in idx: " << it->idx() + << pos() << " " << size() << " in idx: " << idx() << " in atom: '"; - WriteStream wi(0, lyxerr, false); - it->par()->write(wi); + WriteStream wi(lyxerr, false, true); + par()->write(wi); lyxerr << "\n"; dump("error 4"); } - it->pos() = min(pos(), size()); + pos() = min(pos(), size()); + + // remove empty scripts if possible + if (1) { + for (pos_type i = 0; i < size(); ++i) { + MathScriptInset * p = array().at(i)->asScriptInset(); + if (p) { + p->removeEmptyScripts(); + //if (p->empty()) + // array().erase(i); + } + } + } + + // fix again position + pos() = min(pos(), size()); } @@ -990,15 +1001,15 @@ MathCursor::size_type MathCursor::size() const } -MathCursor::col_type MathCursor::col() const +MathCursor::col_type MathCursor::hullCol() const { - return par()->col(idx()); + return Cursor_[0].par_->asGridInset()->col(Cursor_[0].idx_); } -MathCursor::row_type MathCursor::row() const +MathCursor::row_type MathCursor::hullRow() const { - return par()->row(idx()); + return Cursor_[0].par_->asGridInset()->row(Cursor_[0].idx_); } @@ -1051,12 +1062,24 @@ MathArray & MathCursor::array() const return dummy; } + if (Cursor_.size() == 0) { + lyxerr << "############ Cursor_.size() == 0 not valid\n"; + return dummy; + } + return cursor().cell(); } MathXArray & MathCursor::xarray() const { + static MathXArray dummy; + + if (Cursor_.size() == 0) { + lyxerr << "############ Cursor_.size() == 0 not valid\n"; + return dummy; + } + return cursor().xcell(); } @@ -1075,7 +1098,7 @@ void MathCursor::idxPrev() void MathCursor::splitCell() { - if (idx() == par()->nargs() - 1) + if (idx() + 1 == par()->nargs()) return; MathArray ar = array(); ar.erase(0, pos()); @@ -1101,11 +1124,11 @@ void MathCursor::breakLine() idx() = 0; pos() = size(); } else { - p->addRow(row()); + p->addRow(hullRow()); // split line - const row_type r = row(); - for (col_type c = col() + 1; c < p->ncols(); ++c) + const row_type r = hullRow(); + for (col_type c = hullCol() + 1; c < p->ncols(); ++c) p->cell(p->index(r, c)).swap(p->cell(p->index(r + 1, c))); // split cell @@ -1115,12 +1138,12 @@ void MathCursor::breakLine() } -void MathCursor::readLine(MathArray & ar) const -{ - idx_type base = row() * par()->ncols(); - for (idx_type off = 0; off < par()->ncols(); ++off) - ar.push_back(par()->cell(base + off)); -} +//void MathCursor::readLine(MathArray & ar) const +//{ +// idx_type base = row() * par()->ncols(); +// for (idx_type off = 0; off < par()->ncols(); ++off) +// ar.push_back(par()->cell(base + off)); +//} char MathCursor::valign() const @@ -1154,108 +1177,140 @@ void MathCursor::getSelection(MathCursorPos & i1, MathCursorPos & i2) const MathCursorPos & MathCursor::cursor() { + lyx::Assert(Cursor_.size()); return Cursor_.back(); } MathCursorPos const & MathCursor::cursor() const { + lyx::Assert(Cursor_.size()); return Cursor_.back(); } -int MathCursor::cellXOffset() const +bool MathCursor::goUpDown(bool up) { - return par()->cellXOffset(idx()); -} - - -int MathCursor::cellYOffset() const -{ - return par()->cellYOffset(idx()); -} - - -int MathCursor::xpos() const -{ - return cellXOffset() + xarray().pos2x(pos()); -} - - -int MathCursor::ypos() const -{ - return cellYOffset(); -} + // Be warned: The 'logic' implemented in this function is highly fragile. + // A distance of one pixel or a '<' vs '<=' _really_ matters. + // So fiddle around with it only if you know what you are doing! + int xlow, xhigh, ylow, yhigh; + int xo, yo; + getPos(xo, yo); + // try neigbouring script insets + // try left + if (hasPrevAtom()) { + MathScriptInset * p = prevAtom()->asScriptInset(); + if (p && p->has(up)) { + --pos(); + push(nextAtom()); + idx() = up; // the superscript has index 1 + pos() = size(); + ///lyxerr << "updown: handled by scriptinset to the left\n"; + return true; + } + } -void MathCursor::gotoX(int x) -{ - pos() = xarray().x2pos(x - cellXOffset()); -} - + // try right + if (hasNextAtom()) { + MathScriptInset * p = nextAtom()->asScriptInset(); + if (p && p->has(up)) { + push(nextAtom()); + idx() = up; + pos() = 0; + ///lyxerr << "updown: handled by scriptinset to the right\n"; + return true; + } + } -bool MathCursor::goUp() -{ - // first ask the inset if it knows better then we - if (par()->idxUp(idx(), pos())) - return true; + // try current cell + //xarray().boundingBox(xlow, xhigh, ylow, yhigh); + //if (up) + // yhigh = yo - 4; + //else + // ylow = yo + 4; + //if (bruteFind(xo, yo, xlow, xhigh, ylow, yhigh)) { + // lyxerr << "updown: handled by brute find in the same cell\n"; + // return true; + //} + + // try to find an inset that knows better then we + while (1) { + ///lyxerr << "updown: We are in " << *par() << " idx: " << idx() << '\n'; + // ask inset first + if (par()->idxUpDown(idx(), up)) { + // we found a cell that thinks it has something "below" us. + ///lyxerr << "updown: found inset that handles UpDown\n"; + xarray().boundingBox(xlow, xhigh, ylow, yhigh); + // project (xo,yo) onto proper box + ///lyxerr << "\n xo: " << xo << " yo: " << yo + /// << "\n xlow: " << xlow << " ylow: " << ylow + /// << "\n xhigh: " << xhigh << " yhigh: " << yhigh; + xo = min(max(xo, xlow), xhigh); + yo = min(max(yo, ylow), yhigh); + ///lyxerr << "\n xo2: " << xo << " yo2: " << yo << "\n"; + bruteFind(xo, yo, xlow, xhigh, ylow, yhigh); + ///lyxerr << "updown: handled by final brute find\n"; + return true; + } - // leave subscript to the nearest side - MathScriptInset * p = par()->asScriptInset(); - if (p && idx() == 0) { - if (pos() <= size() / 2) - popLeft(); - else - popRight(); - return true; - } + // leave inset + if (!popLeft()) { + // no such inset found, just take something "above" + ///lyxerr << "updown: handled by strange case\n"; + return + bruteFind(xo, yo, + formula()->xlow(), + formula()->xhigh(), + up ? formula()->ylow() : yo + 4, + up ? yo - 4 : formula()->yhigh() + ); + } - // if not, apply brute force. - int x0; - int y0; - getPos(x0, y0); - std::vector save = Cursor_; - y0 -= xarray().ascent(); - for (int y = y0 - 4; y > formula()->upperY(); y -= 4) { - setPos(x0, y); - if (save != Cursor_ && xarray().yo() < y0) - return true; + // any improvement so far? + int xnew, ynew; + getPos(xnew, ynew); + if (up ? ynew < yo : ynew > yo) + return true; } - Cursor_ = save; - return false; } -bool MathCursor::goDown() +bool MathCursor::bruteFind + (int x, int y, int xlow, int xhigh, int ylow, int yhigh) { - // first ask the inset if it knows better then we - if (par()->idxDown(idx(), pos())) - return true; + cursor_type best_cursor; + double best_dist = 1e10; - // leave superscript to the nearest side - MathScriptInset * p = par()->asScriptInset(); - if (p && idx() == 1) { - if (pos() <= size() / 2) - popLeft(); - else - popRight(); - return true; - } + MathIterator it = ibegin(formula()->par().nucleus()); + MathIterator et = iend(formula()->par().nucleus()); + while (1) { + // avoid invalid nesting when selecting + if (!selection_ || positionable(it.cursor(), Anchor_)) { + MathCursorPos const & top = it.position(); + int xo = top.xpos(); + int yo = top.ypos(); + if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) { + double d = (x - xo) * (x - xo) + (y - yo) * (y - yo); + // '<=' in order to take the last possible position + // this is important for clicking behind \sum in e.g. '\sum_i a' + if (d <= best_dist) { + best_dist = d; + best_cursor = it.cursor(); + } + } + } - // if not, apply brute force. - int x0; - int y0; - getPos(x0, y0); - std::vector save = Cursor_; - y0 += xarray().descent(); - for (int y = y0 + 4; y < formula()->lowerY(); y += 4) { - setPos(x0, y); - if (save != Cursor_ && xarray().yo() > y0) - return true; + if (it == et) + break; + ++it; } - Cursor_ = save; - return false; + + if (best_dist < 1e10) + Cursor_ = best_cursor; + return best_dist < 1e10; } @@ -1277,18 +1332,15 @@ bool MathCursor::interpret(string const & s) if (s.empty()) return true; - if (s.size() == 1) - return interpret(s[0]); - //lyxerr << "char: '" << s[0] << "' int: " << int(s[0]) << endl; - //owner_->getIntl()->getTrans().TranslateAndInsert(s[0], lt); + //owner_->getIntl()->getTrans().TranslateAndInsert(s[0], lt); //lyxerr << "trans: '" << s[0] << "' int: " << int(s[0]) << endl; if (s.size() >= 5 && s.substr(0, 5) == "cases") { unsigned int n = 1; - istringstream is(s.substr(6).c_str()); + istringstream is(s.substr(5).c_str()); is >> n; - n = std::max(1u, n); + n = max(1u, n); niceInsert(MathAtom(new MathCasesInset(n))); return true; } @@ -1298,18 +1350,32 @@ bool MathCursor::interpret(string const & s) unsigned int n = 1; string v_align; string h_align; - istringstream is(s.substr(7).c_str()); + istringstream is(s.substr(6).c_str()); is >> m >> n >> v_align >> h_align; - m = std::max(1u, m); - n = std::max(1u, n); + m = max(1u, m); + n = max(1u, n); v_align += 'c'; - niceInsert(MathAtom(new MathArrayInset(m, n, v_align[0], h_align))); + niceInsert(MathAtom(new MathArrayInset("array", m, n, v_align[0], h_align))); + return true; + } + + if (s.size() >= 7 && s.substr(0, 7) == "replace") { + ReplaceData rep; + istringstream is(s.substr(7).c_str()); + string from, to; + is >> from >> to; + mathed_parse_cell(rep.from, from); + mathed_parse_cell(rep.to, to); + lyxerr << "replacing '" << from << "' with '" << to << "'\n"; + par()->replace(rep); return true; } - if (s == "\\over" || s == "\\choose" || s == "\\atop") { + string name = s.substr(1); + + if (name == "over" || name == "choose" || name == "atop") { MathArray ar = array(); - MathAtom t = createMathInset(s.substr(1)); + MathAtom t(createMathInset(name)); t->asNestInset()->cell(0).swap(array()); pos() = 0; niceInsert(t); @@ -1318,7 +1384,57 @@ bool MathCursor::interpret(string const & s) return true; } - niceInsert(createMathInset(s.substr(1))); + latexkeys const * l = in_word_set(name); + if (l && (l->token == LM_TK_FONT || l->token == LM_TK_OLDFONT)) { + lastcode_ = static_cast(l->id); + return true; + } + + // prevent entering of recursive macros + if (formula()->lyxCode() == Inset::MATHMACRO_CODE + && formula()->getInsetName() == name) + { + lyxerr << "can't enter recursive macro\n"; + return true; + } + + niceInsert(createMathInset(name)); + return true; +} + + +bool MathCursor::script(bool up) +{ + // Hack to get \\^ and \\_ working + if (inMacroMode() && macroName() == "\\") { + if (up) + interpret("\\mathcircumflex"); + else + interpret('_'); + return true; + } + + macroModeClose(); + selCut(); + if (hasPrevAtom() && prevAtom()->asScriptInset()) { + prevAtom()->asScriptInset()->ensure(up); + pushRight(prevAtom()); + idx() = up; + pos() = size(); + } else if (hasNextAtom() && nextAtom()->asScriptInset()) { + nextAtom()->asScriptInset()->ensure(up); + pushLeft(nextAtom()); + idx() = up; + pos() = 0; + } else { + plainInsert(MathAtom(new MathScriptInset(up))); + prevAtom()->asScriptInset()->ensure(up); + pushRight(prevAtom()); + idx() = up; + pos() = 0; + } + selPaste(); + dump("1"); return true; } @@ -1326,76 +1442,66 @@ bool MathCursor::interpret(string const & s) bool MathCursor::interpret(char c) { //lyxerr << "interpret 2: '" << c << "'\n"; - if (c == '^' || c == '_') { - macroModeClose(); - const bool up = (c == '^'); - selCut(); - if (hasPrevAtom() && prevAtom()->asScriptInset()) { - prevAtom()->asScriptInset()->ensure(up); - pushRight(prevAtom()); - idx() = up; - pos() = size(); - } else if (hasNextAtom() && nextAtom()->asScriptInset()) { - nextAtom()->asScriptInset()->ensure(up); - pushLeft(nextAtom()); - idx() = up; - pos() = 0; - } else { - plainInsert(MathAtom(new MathScriptInset(up))); - prevAtom()->asScriptInset()->ensure(up); - pushRight(prevAtom()); - idx() = up; - pos() = 0; + if (inMacroArgMode()) { + --pos(); + plainErase(); + int n = c - '0'; + MathMacroTemplate * p = formula()->par()->asMacroTemplate(); + if (p && 1 <= n && n <= p->numargs()) + insert(MathAtom(new MathMacroArgument(c - '0', lastcode_))); + else { + insert(MathAtom(new MathSpecialCharInset('#'))); + interpret(c); // try again } - selPaste(); - dump("1"); return true; } - // handle macroMode if (inMacroMode()) { string name = macroName(); + //lyxerr << "interpret name: '" << name << "'\n"; - if (name == "\\" && c == '#') { + // extend macro name if possible + if (isalpha(c)) { insert(c, LM_TC_TEX); return true; } - if (name == "\\" && c == '\\') { - backspace(); - interpret("\\backslash"); - return true; - } - - if (name == "\\#" && '1' <= c && c <= '9') { - insert(c, LM_TC_TEX); + // leave macro mode if explicitly requested + if (c == ' ') { macroModeClose(); return true; } - if (isalpha(c)) { - insert(c, LM_TC_TEX); - return true; - } - + // handle 'special char' macros if (name == "\\") { - insert(c, LM_TC_TEX); - macroModeClose(); + // remove the '\\' + backspace(); + if (c == '\\') + interpret("\\backslash"); + else + interpret(string("\\") + c); return true; } + // leave macro mode and try again macroModeClose(); + interpret(c); return true; } - if (selection_) - selDel(); + // just clear selection on pressing the space par + if (selection_ && c == ' ') { + selClear(); + return true; + } - if (lastcode_ == LM_TC_TEXTRM) { - // suppress direct insertion of to spaces in a row + selClearOrDel(); + + if (lastcode_ == LM_TC_TEXTRM || par()->asBoxInset()) { + // suppress direct insertion of two spaces in a row // the still allows typing 'a' and deleting the 'a', but - // it is better than nothing + // it is better than nothing... if (c == ' ' && hasPrevAtom() && prevAtom()->getChar() == ' ') return true; insert(c, LM_TC_TEXTRM); @@ -1407,16 +1513,19 @@ bool MathCursor::interpret(char c) prevAtom()->asSpaceInset()->incSpace(); return true; } - - if (mathcursor->popRight()) + if (popRight()) return true; - // if are at the very end, leave the formula return pos() != size(); } + if (c == '#') { + insert(c, LM_TC_TEX); + return true; + } + /* - if (strchr("{}", c)) { + if (c == '{' || c == '}', c)) { insert(c, LM_TC_TEX); return true; } @@ -1431,8 +1540,8 @@ bool MathCursor::interpret(char c) return true; } - if (strchr("#$%", c)) { - insert(MathAtom(new MathSpecialCharInset(c))); + if (c == '$' || c == '%') { + insert(MathAtom(new MathSpecialCharInset(c))); lastcode_ = LM_TC_VAR; return true; } @@ -1451,63 +1560,25 @@ bool MathCursor::interpret(char c) if (c == '\\') { insert(c, LM_TC_TEX); //bv->owner()->message(_("TeX mode")); - return true; + return true; } // no special circumstances, so insert the character without any fuss - insert(c, LM_TC_MIN); + insert(c, lastcode_ == LM_TC_MIN ? MathCharInset::nativeCode(c) : lastcode_); + lastcode_ = LM_TC_MIN; return true; } -//////////////////////////////////////////////////////////////////////// - - -bool operator==(MathCursorPos const & ti, MathCursorPos const & it) -{ - return ti.par_ == it.par_ && ti.idx_ == it.idx_ && ti.pos_ == it.pos_; -} - - -bool operator<(MathCursorPos const & ti, MathCursorPos const & it) -{ - if (ti.par_ != it.par_) { - lyxerr << "can't compare cursor and anchor in different insets\n"; - return true; - } - if (ti.idx_ != it.idx_) - return ti.idx_ < it.idx_; - return ti.pos_ < it.pos_; -} - - -MathArray & MathCursorPos::cell(MathCursor::idx_type idx) const -{ - return (*par_)->cell(idx); -} - - -MathArray & MathCursorPos::cell() const -{ - return (*par_)->cell(idx_); -} - - -MathXArray & MathCursorPos::xcell(MathCursor::idx_type idx) const -{ - return (*par_)->xcell(idx); -} - - -MathXArray & MathCursorPos::xcell() const -{ - return (*par_)->xcell(idx_); -} - - MathCursorPos MathCursor::normalAnchor() const { + if (Anchor_.size() < Cursor_.size()) { + Anchor_ = Cursor_; + lyxerr << "unusual Anchor size\n"; + dump("1"); + } + //lyx::Assert(Anchor_.size() >= Cursor_.size()); // use Anchor on the same level as Cursor MathCursorPos normal = Anchor_[Cursor_.size() - 1]; if (Cursor_.size() < Anchor_.size() && !(normal < cursor())) { @@ -1529,7 +1600,23 @@ void MathCursor::stripFromLastEqualSign() // delete everything behind this position ar.erase(et - ar.begin(), ar.size()); - pos() = ar.size(); + pos() = ar.size(); +} + + +void MathCursor::setSelection(cursor_type const & where, size_type n) +{ + selection_ = true; + Anchor_ = where; + Cursor_ = where; + cursor().pos_ += n; } +string MathCursor::info() const +{ + ostringstream os; + if (pos() > 0) + prevAtom()->infoize(os); + return os.str().c_str(); // .c_str() needed for lyxstring +}