X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2Fmath_cursor.C;h=c12b10223e2f66daf339418349505741d50efce9;hb=3024e24e80a40ca97ef66310610515ec2c3b6457;hp=c0849df500f18abd2aaa85800fe04918d418344b;hpb=d1182f17daa1a164d9527ccbe6500840d7ac6bc8;p=lyx.git diff --git a/src/mathed/math_cursor.C b/src/mathed/math_cursor.C index c0849df500..c12b10223e 100644 --- a/src/mathed/math_cursor.C +++ b/src/mathed/math_cursor.C @@ -20,131 +20,254 @@ #endif #include +#include +#include -#include "math_inset.h" -#include "math_parser.h" -#include "math_cursor.h" -#include "math_macro.h" -#include "math_macroarg.h" -#include "math_macrotable.h" -#include "math_root.h" -#include "support/lstrings.h" #include "debug.h" #include "LColor.h" #include "Painter.h" -#include "math_matrixinset.h" -#include "math_spaceinset.h" -#include "math_funcinset.h" +#include "mathed/support.h" +#include "formulabase.h" +#include "math_cursor.h" +#include "math_arrayinset.h" #include "math_bigopinset.h" -#include "math_fracinset.h" #include "math_decorationinset.h" +#include "math_deliminset.h" #include "math_dotsinset.h" -#include "math_accentinset.h" +#include "math_fracinset.h" +#include "math_funcinset.h" +#include "math_gridinset.h" +#include "math_macro.h" +#include "math_macroarg.h" +#include "math_macrotable.h" #include "math_macrotemplate.h" +#include "math_matrixinset.h" +#include "math_rootinset.h" +#include "math_spaceinset.h" #include "math_sqrtinset.h" +#include "support/lstrings.h" #include "math_scriptinset.h" -#include "mathed/support.h" -#include "formulabase.h" - +#include "math_parser.h" using std::endl; using std::min; using std::max; +using std::isalnum; +#define RECTANGULAR_SELECT 1 namespace { -MathArray selarray; +struct Selection +{ + 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 { +#ifdef RECTANGULAR_SELECT + std::vector indices = i1.par_->idxBetween(i1.idx_, i2.idx_); + for (unsigned i = 0; i < indices.size(); ++i) + data_.push_back(i1.cell(indices[i])); +#else + data_.push_back(MathArray(i1.cell(), i1.pos_, i1.cell().size())); + for (int i = i1.idx_ + 1; i < i2.idx_; ++i) + data_.push_back(i1.cell(i)); + data_.push_back(MathArray(i2.cell(), 0, i2.pos_)); +#endif + } + } + + void erase(MathCursor & cursor) + { + MathCursorPos i1; + MathCursorPos i2; + cursor.getSelection(i1, i2); + if (i1.idx_ == i2.idx_) { + i1.cell().erase(i1.pos_, i2.pos_); + } else { +#ifdef RECTANGULAR_SELECT + std::vector indices = i1.par_->idxBetween(i1.idx_, i2.idx_); + for (unsigned i = 0; i < indices.size(); ++i) + i1.cell(indices[i]).erase(); +#else + i1.cell().erase(i1.pos_, i1.cell().size()); + for (int i = i1.idx_ + 1; i < i2.idx_; ++i) + i1.cell(i).erase(); + i2.cell().erase(0, i2.pos_); + + int from = i1.cell().size() ? i1.idx_ + 1 : i1.idx_; + int to = i2.cell().size() ? i2.idx_ : i2.idx_ + 1; + i1.par_->idxDeleteRange(from, to); +#endif + } + cursor.cursor() = i1; + } + + void paste(MathCursor & cursor) const + { +#ifdef RECTANGULAR_SELECT + cursor.cursor().cell().push_back(glue()); +#else + unsigned na = cursor.cursor().par_->nargs(); + unsigned idx = cursor.cursor().idx_; + unsigned end = std::min(idx + data_.size(), na); + for (int i = 0; i < end - idx; ++i) + cursor.cursor().cell(idx + i).push_back(data_[i]); + for (unsigned i = end - idx; i < data_.size(); ++i) + cursor.cursor().cell(end - 1).push_back(data_[i]); +#endif + } + + // glues selection to one cell + MathArray glue() const + { + MathArray ar; + for (unsigned i = 0; i < data_.size(); ++i) + ar.push_back(data_[i]); + return ar; + } + + void clear() + { + data_.clear(); + } + + + + std::vector data_; +}; + + +Selection theSelection; + bool IsMacro(short tok, int id) { return tok != LM_TK_STACK && tok != LM_TK_FRAC && tok != LM_TK_SQRT && - tok != LM_TK_WIDE && + tok != LM_TK_DECORATION && tok != LM_TK_SPACE && tok != LM_TK_DOTS && tok != LM_TK_FUNCLIM && tok != LM_TK_BIGSYM && - tok != LM_TK_ACCENT && !(tok == LM_TK_SYM && id < 255); } + +std::ostream & operator<<(std::ostream & os, MathCursorPos const & p) +{ + os << "(par: " << p.par_ << " idx: " << p.idx_ + << " pos: " << p.pos_ << ")"; + return os; } +} + + MathCursor::MathCursor(InsetFormulaBase * formula) : formula_(formula) { - accent = 0; lastcode = LM_TC_MIN; macro_mode = false; + selection = false; first(); } void MathCursor::push(MathInset * par, bool first) { - path_.push_back(MathIter()); - path_.back().par_ = par_; - path_.back().idx_ = idx_; - path_.back().cursor_ = cursor_; - dump("Pushed:"); - par_ = par; - first ? par_->idxFirst(idx_, cursor_) : par_->idxLast(idx_, cursor_); + MathCursorPos p; + p.par_ = par; + if (first) + par->idxFirst(p.idx_, p.pos_); + else + par->idxLast(p.idx_, p.pos_); + Cursor_.push_back(p); } -void MathCursor::pop() +bool MathCursor::pop() { - if (path_.empty()) - return; - par_ = path_.back().par_; - idx_ = path_.back().idx_; - cursor_ = path_.back().cursor_; - dump("Popped:"); - path_.pop_back(); + if (Cursor_.size() <= 1) + return false; + Cursor_.pop_back(); + return true; } MathInset * MathCursor::parInset(int i) const { - return path_[i].par_; + return Cursor_[i].par_; } + void MathCursor::dump(char const * what) const { - lyxerr << "MC: " << what - << " cursor: " << cursor_ - << " anchor: " << anchor_ - << " idx: " << idx_ - << " par: " << par_ - << " sel: " << selection - << " data: " << array() - << "\n"; + return; + + lyxerr << "MC: " << what << "\n"; + for (unsigned i = 0; i < Cursor_.size(); ++i) + lyxerr << " i: " << i + << " pos: " << Cursor_[i].pos_ + << " idx: " << Cursor_[i].idx_ + << " par: " << Cursor_[i].par_ << "\n"; + + //lyxerr << " sel: " << selection << " data: " << array() << "\n"; } -void MathCursor::seldump(char const * str) const + +void MathCursor::seldump(char const *) const { - lyxerr << "SEL: " << str << ": '" << selarray << "'\n"; - dump(" Pos"); + //lyxerr << "SEL: " << str << ": '" << theSelection << "'\n"; + //dump(" Pos"); return; - lyxerr << "\n\n\\n=================vvvvvvvvvvvvv======================= " - << str << "\nselarray: " << selarray; - for (unsigned int i = 0; i < path_.size(); ++i) - lyxerr << path_[i].par_ << "\n'" << path_[i].par_->cell(0) << "'\n"; - lyxerr << "\ncursor: " << cursor_; - lyxerr << "\nanchor: " << anchor_; - lyxerr << "\n===================^^^^^^^^^^^^=====================\n\n\n"; + //lyxerr << "\n\n\\n=================vvvvvvvvvvvvv======================= " + // << str << "\ntheSelection: " << theSelection; + //for (unsigned int i = 0; i < Cursor_.size(); ++i) + // lyxerr << Cursor_[i].par_ << "\n'" << Cursor_[i].cell() << "'\n"; + //lyxerr << "\ncursor.pos_: " << cursor().pos_; + //lyxerr << "\nanchor.pos_: " << anchor().pos_; + //lyxerr << "\n===================^^^^^^^^^^^^=====================\n\n\n"; } bool MathCursor::isInside(MathInset * p) const { - for (unsigned i = 0; i < path_.size(); ++i) + for (unsigned i = 0; i < Cursor_.size(); ++i) if (parInset(i) == p) return true; - return par_ == p; + return false; +} + + +bool MathCursor::openable(MathInset * p, bool sel, bool useupdown) const +{ + if (!p) + return false; + if (!(p->isActive() || (useupdown && p->isUpDownInset()))) + return false; + + if (sel) { + // we can't move into everything during selection + if (Cursor_.size() == Anchor_.size()) + return false; + if (p != Anchor_[Cursor_.size()].par_) + return false; + } + return true; +} + + +bool MathCursor::plainLeft() +{ + return array().prev(cursor().pos_); } @@ -160,41 +283,28 @@ bool MathCursor::Left(bool sel) MacroModeClose(); return true; } - clearLastCode(); SelHandle(sel); + clearLastCode(); - bool result = false; + MathInset * p = prevInset(); + if (openable(p, sel, false)) { + plainLeft(); + push(p, false); + return true; + } + if (plainLeft()) + return true; + if (cursor().par_->idxLeft(cursor().idx_, cursor().pos_)) + return true; + if (pop()) + return true; + return false; +} - if (selection) { - result = array().prev(cursor_); - if (!result && !path_.empty()) { - pop(); - anchor_ = cursor_; - result = array().next(anchor_); - } - } else { - MathInset * p = prevActiveInset(); - if (p) { - // We have to move deeper into the previous inset - array().prev(cursor_); - push(p, false); - result = true; - } else { - // The common case, where we are not - // entering a deeper inset - result = array().prev(cursor_); - if (!result) { - if (par_->idxLeft(idx_, cursor_)) { - result = true; - } else if (!path_.empty()) { - pop(); - result = true; - } - } - } - } - dump("Left 2"); - return result; + +bool MathCursor::plainRight() +{ + return array().next(cursor().pos_); } @@ -205,81 +315,57 @@ bool MathCursor::Right(bool sel) MacroModeClose(); return true; } - - clearLastCode(); SelHandle(sel); + clearLastCode(); - bool result = false; - - if (selection) { - result = array().next(cursor_); - if (!result && !path_.empty()) { - pop(); - anchor_ = cursor_; - result = array().prev(anchor_); - } - } else { - MathInset * p = nextActiveInset(); - if (p) { - push(p, true); - result = true; - } else { - result = array().next(cursor_); - if (!result) { - if (par_->idxRight(idx_, cursor_)) { - result = true; - } else if (!path_.empty()) { - pop(); - result = true; - array().next(cursor_); - } - } - } + MathInset * p = nextInset(); + if (openable(p, sel, false)) { + push(p, true); + return true; } - dump("Right 2"); - return result; + if (array().next(cursor().pos_)) + return true; + if (cursor().par_->idxRight(cursor().idx_, cursor().pos_)) + return true; + if (!pop()) + return false; + array().next(cursor().pos_); + return true; } void MathCursor::first() { - selection = false; - par_ = formula_->par(); - idx_ = 0; - cursor_ = 0; - anchor_ = 0; - par_->idxFirst(idx_, cursor_); + Cursor_.clear(); + push(formula_->par(), true); } void MathCursor::last() { - selection = false; - par_ = formula_->par(); - idx_ = 0; - cursor_ = 0; - anchor_ = 0; - par_->idxLast(idx_, cursor_); + Cursor_.clear(); + push(formula_->par(), false); } void MathCursor::SetPos(int x, int y) { dump("SetPos 1"); - lyxerr << "MathCursor::SetPos x: " << x << " y: " << y << "\n"; + //lyxerr << "MathCursor::SetPos x: " << x << " y: " << y << "\n"; MacroModeClose(); lastcode = LM_TC_MIN; - path_.clear(); + first(); - par_ = formula()->par(); + cursor().par_ = formula()->par(); while (1) { - idx_ = -1; - cursor_ = -1; + cursor().idx_ = -1; + cursor().pos_ = -1; + //lyxerr << "found idx: " << idx_ << " cursor: " << cursor().pos_ << "\n"; int distmin = 1 << 30; // large enough - for (int i = 0; i < par_->nargs(); ++i) { - MathXArray const & ar = par_->xcell(i); + for (int i = 0; i < cursor().par_->nargs(); ++i) { + MathXArray const & ar = cursor().par_->xcell(i); int x1 = x - ar.xo(); int y1 = y - ar.yo(); int c = ar.x2pos(x1); @@ -288,18 +374,19 @@ void MathCursor::SetPos(int x, int y) //lyxerr << "idx: " << i << " xx: " << xx << " yy: " << yy // << " c: " << c << " xo: " << ar.xo() << "\n"; if (yy + xx <= distmin) { - distmin = yy + xx; - idx_ = i; - cursor_ = c; + distmin = yy + xx; + cursor().idx_ = i; + cursor().pos_ = c; } } - lyxerr << "found idx: " << idx_ << " cursor: " << cursor_ << "\n"; - if (nextIsActive() && nextInset()->covers(x, y)) { - MathInset * p = nextActiveInset(); - push(p, true); - } else if (prevIsActive() && prevInset()->covers(x, y)) { - MathInset * p = prevActiveInset(); - array().prev(cursor_); + //lyxerr << "found idx: " << cursor().idx_ << " cursor: " + // << cursor().pos_ << "\n"; + MathInset * n = nextInset(); + MathInset * p = prevInset(); + if (openable(n, selection, true) && n->covers(x, y)) + push(n, true); + else if (openable(p, selection, true) && p->covers(x, y)) { + plainLeft(); push(p, false); } else break; @@ -314,9 +401,8 @@ void MathCursor::Home() if (macro_mode) MacroModeClose(); clearLastCode(); - if (!par_->idxHome(idx_, cursor_)) { + if (!cursor().par_->idxHome(cursor().idx_, cursor().pos_)) pop(); - } dump("Home 2"); } @@ -327,18 +413,17 @@ void MathCursor::End() if (macro_mode) MacroModeClose(); clearLastCode(); - if (!par_->idxEnd(idx_, cursor_)) { + if (!cursor().par_->idxEnd(cursor().idx_, cursor().pos_)) { pop(); - array().next(cursor_); + array().next(cursor().pos_); } dump("End 2"); } - void MathCursor::insert(char c, MathTextCodes t) { - lyxerr << "inserting '" << c << "'\n"; + //lyxerr << "inserting '" << c << "'\n"; if (selection) SelDel(); @@ -356,15 +441,10 @@ void MathCursor::insert(char c, MathTextCodes t) } } - if (accent) - doAccent(c, t); - else { - array().insert(cursor_, c, t); - array().next(cursor_); - } + array().insert(cursor().pos_, c, t); + array().next(cursor().pos_); lastcode = t; - return; } @@ -379,15 +459,19 @@ void MathCursor::insert(MathInset * p) SelDel(); } - if (accent && !p->nargs()) - doAccent(p); - else { - array().insert(cursor_, p); - array().next(cursor_); - } + array().insert(cursor().pos_, p); + array().next(cursor().pos_); +} + - //if (p->nargs()) - // push(p, true); +void MathCursor::insert(MathArray const & ar) +{ + MacroModeClose(); + if (selection) + SelCut(); + + array().insert(cursor().pos_, ar); + cursor().pos_ += ar.size(); } @@ -402,16 +486,17 @@ void MathCursor::Delete() return; } - if (cursor_ < array().size()) - array().erase(cursor_); + if (cursor().pos_ < array().size()) + array().erase(cursor().pos_); -#ifdef WITH_WARNINGS -#warning pullArg disabled -#endif - //if (cursor_ == 0 && !path_.empty()) { - // lyxerr << "Delete: popping...\n"; - // pop(); - //} + // delete empty cells if necessary + if (cursor().pos_ == 0 && array().size() == 0) { + bool popit; + bool removeit; + cursor().par_->idxDelete(cursor().idx_, popit, removeit); + if (popit && pop() && removeit) + Delete(); + } dump("Delete 2"); } @@ -426,8 +511,8 @@ void MathCursor::DelLine() return; } - if (par_->nrows() > 1) - par_->delRow(row()); + if (cursor().par_->nrows() > 1) + cursor().par_->delRow(row()); } @@ -436,18 +521,54 @@ bool MathCursor::Up(bool sel) dump("Up 1"); MacroModeClose(); SelHandle(sel); - SelClear(); - int x = xarray().pos2x(cursor_); - bool result = par_->idxUp(idx_, cursor_); - if (!result && !path_.empty()) { - pop(); - result = par_->idxUp(idx_, cursor_); + if (selection) { + int x = xarray().pos2x(cursor().pos_); + if (cursor().par_->idxUp(cursor().idx_, cursor().pos_)) { + cursor().pos_ = xarray().x2pos(x); + return true; + } + if (pop()) + return true; + return false; + } + + // check whether we could move into an inset on the right or on the left + MathInset * p = nextInset(); + if (p) { + int idx, pos; + if (p->idxFirstUp(idx, pos)) { + push(p, true); + cursor().par_ = p; + cursor().idx_ = idx; + cursor().pos_ = pos; + dump("Up 3"); + return true; + } + } + + p = prevInset(); + if (p) { + int idx, pos; + if (p->idxLastUp(idx, pos)) { + plainLeft(); + push(p, false); + cursor().par_ = p; + cursor().idx_ = idx; + cursor().pos_ = pos; + dump("Up 4"); + return true; + } } - cursor_ = xarray().x2pos(x); - dump("Up 2"); - return result; + int x = xarray().pos2x(cursor().pos_); + if (cursor().idxUp()) { + cursor().pos_ = xarray().x2pos(x); + return true; + } + if (pop()) + return true; + return false; } @@ -456,18 +577,52 @@ bool MathCursor::Down(bool sel) dump("Down 1"); MacroModeClose(); SelHandle(sel); - SelClear(); - int x = xarray().pos2x(cursor_); - bool result = par_->idxDown(idx_, cursor_); - if (!result && !path_.empty()) { - pop(); - result = par_->idxDown(idx_, cursor_); + if (selection) { + int x = xarray().pos2x(cursor().pos_); + if (cursor().idxDown()) { + cursor().pos_ = xarray().x2pos(x); + return true; + } + if (pop()) + return true; + return false; } - cursor_ = xarray().x2pos(x); - dump("Down 2"); - return result; + // check whether we could move into an inset on the right or on the left + MathInset * p = nextInset(); + if (p) { + int idx, pos; + if (p->idxFirstDown(idx, pos)) { + push(p, true); + cursor().idx_ = idx; + cursor().pos_ = pos; + dump("Down 3"); + return true; + } + } + + p = prevInset(); + if (p) { + int idx, pos; + if (p->idxLastDown(idx, pos)) { + plainLeft(); + push(p, false); + cursor().idx_ = idx; + cursor().pos_ = pos; + dump("Down 4"); + return true; + } + } + + int x = xarray().pos2x(cursor().pos_); + if (cursor().par_->idxDown(cursor().idx_, cursor().pos_)) { + cursor().pos_ = xarray().x2pos(x); + return true; + } + if (pop()) + return true; + return false; } @@ -476,44 +631,47 @@ bool MathCursor::toggleLimits() if (!prevIsInset()) return false; MathInset * p = prevInset(); - bool old = p->GetLimits(); - p->SetLimits(!old); - return old != p->GetLimits(); + int old = p->limits(); + p->limits(old < 0 ? 1 : -1); + return old != p->limits(); } void MathCursor::SetSize(MathStyles size) { - par_->UserSetSize(size); + cursor().par_->UserSetSize(size); } void MathCursor::Interpret(string const & s) { - lyxerr << "Interpret: '" << s << "'\n"; + lyxerr << "Interpret: '" << s << "' ('" << s.substr(0, 7) << "' " << +in_word_set(s) << " \n"; if (s[0] == '^') { - MathScriptInset * p = prevScriptInset(); + MathUpDownInset * p = nearbyUpDownInset(); if (!p) { - p = new MathScriptInset; + p = new MathScriptInset(true, false); insert(p); + plainLeft(); } - if (!p->up()) - p->up(true); push(p, true); + p->up(true); + cursor().idx_ = 0; return; } if (s[0] == '_') { - MathScriptInset * p = prevScriptInset(); + MathUpDownInset * p = nearbyUpDownInset(); if (!p) { - p = new MathScriptInset; + p = new MathScriptInset(false, true); insert(p); + plainLeft(); } - if (!p->down()) - p->down(true); push(p, true); + p->down(true); + cursor().idx_ = 1; return; } @@ -526,11 +684,26 @@ void MathCursor::Interpret(string const & s) MathInset * p = 0; latexkeys const * l = in_word_set(s); - if (!l) { + if (l == 0) { if (s == "root") p = new MathRootInset; else if (MathMacroTable::hasTemplate(s)) p = new MathMacro(MathMacroTable::provideTemplate(s)); + else if (s.size() > 7 && s.substr(0, 7) == "matrix ") { + int m = 1; + int n = 1; + string v_align; + string h_align; + istringstream is(s.substr(7).c_str()); + is >> m >> n >> v_align >> h_align; + m = std::max(1, m); + n = std::max(1, n); + v_align += 'c'; + MathArrayInset * pp = new MathArrayInset(m, n); + pp->valign(v_align[0]); + pp->halign(h_align); + p = pp; + } else p = new MathFuncInset(s, LM_OT_UNDEF); } else { @@ -539,28 +712,29 @@ void MathCursor::Interpret(string const & s) p = new MathBigopInset(l->name, l->id); break; - case LM_TK_SYM: - if (l->id < 255) - insert(static_cast(l->id), MathIsBOPS(l->id) ? - LM_TC_BOPS : LM_TC_SYMB); - else - p = new MathFuncInset(l->name); - break; - + case LM_TK_SYM: { + MathTextCodes code = static_cast(l->id); + if (code < 255) + insert(l->id, MathIsBOPS(code) ? LM_TC_BOPS : LM_TC_SYMB); + else + p = new MathFuncInset(l->name); + break; + } + case LM_TK_STACK: - p = new MathFracInset(LM_OT_STACKREL); + p = new MathFracInset("stackrel"); break; case LM_TK_FRAC: - p = new MathFracInset(LM_OT_FRAC); + p = new MathFracInset("frac"); break; case LM_TK_SQRT: p = new MathSqrtInset; break; - case LM_TK_WIDE: - p = new MathDecorationInset(l->id); + case LM_TK_DECORATION: + p = new MathDecorationInset(l->name, l->id); break; case LM_TK_FUNCLIM: @@ -575,10 +749,6 @@ void MathCursor::Interpret(string const & s) p = new MathDotsInset(l->name, l->id); break; - case LM_TK_ACCENT: - setAccent(l->id); - break; - case LM_TK_MACRO: p = new MathMacro(MathMacroTable::provideTemplate(s)); break; @@ -590,10 +760,15 @@ void MathCursor::Interpret(string const & s) } if (p) { + bool oldsel = selection; + if (oldsel) + SelCut(); insert(p); if (p->nargs()) { - array().prev(cursor_); + plainLeft(); push(p, true); + if (oldsel) + SelPaste(); } p->Metrics(p->size()); } @@ -628,10 +803,7 @@ void MathCursor::MacroModeClose() imacro->SetName(l->name); } else { Left(); - if (nextInset()->GetType() == LM_OT_ACCENT) - setAccent( - static_cast(nextInset())->getAccentCode()); - array().erase(cursor_); + array().erase(cursor().pos_); if (l || MathMacroTable::hasTemplate(imacro->name())) Interpret(imacro->name()); imacro->SetName(string()); @@ -645,36 +817,18 @@ void MathCursor::SelCopy() { seldump("SelCopy"); if (selection) { - int const p1 = min(cursor_, anchor_); - int const p2 = max(cursor_, anchor_); - selarray = array(); - selarray.erase(p2, selarray.size()); - selarray.erase(0, p1); + theSelection.grab(*this); SelClear(); } } -void MathCursor::selArray(MathArray & ar) const -{ - int const p1 = min(cursor_, anchor_); - int const p2 = max(cursor_, anchor_); - ar = array(); - ar.erase(p2, ar.size()); - ar.erase(0, p1); -} - void MathCursor::SelCut() { seldump("SelCut"); if (selection) { - int const p1 = min(cursor_, anchor_); - int const p2 = max(cursor_, anchor_); - cursor_ = p1; // move cursor to a same position - selarray = array(); - selarray.erase(p2, selarray.size()); - selarray.erase(0, p1); - array().erase(p1, p2); + theSelection.grab(*this); + theSelection.erase(*this); SelClear(); } } @@ -684,9 +838,7 @@ void MathCursor::SelDel() { seldump("SelDel"); if (selection) { - int const p1 = min(cursor_, anchor_); - int const p2 = max(cursor_, anchor_); - array().erase(p1, p2); + theSelection.erase(*this); SelClear(); } } @@ -695,11 +847,11 @@ void MathCursor::SelDel() void MathCursor::SelPaste() { seldump("SelPaste"); - array().insert(cursor_, selarray); - cursor_ += selarray.size(); + theSelection.paste(*this); SelClear(); } + void MathCursor::SelHandle(bool sel) { if (sel && !selection) @@ -715,7 +867,7 @@ void MathCursor::SelStart() if (selection) return; - anchor_ = cursor_; + Anchor_ = Cursor_; selection = true; } @@ -726,155 +878,132 @@ void MathCursor::SelClear() } - -void MathCursor::SelGetArea(int * xpoint, int * ypoint, int & n) +void MathCursor::drawSelection(Painter & pain) const { - if (!selection) { - n = 0; - xpoint[0] = 0; - ypoint[0] = 0; + if (!selection) return; - } - - // Balance anchor and cursor - int xo; - int yo; - par()->GetXY(xo, yo); - int w = par()->width(); - // cursor - int x1 = xarray().xo() + xarray().pos2x(cursor_); - int y1 = xarray().yo(); - //int a1 = xarray().ascent(); - //int d1 = xarray().descent(); - - // anchor - int x = xarray().xo() + xarray().pos2x(anchor_); - int y = xarray().yo(); - int a = xarray().ascent(); - int d = xarray().descent(); - - // single row selection - n = 0; - xpoint[n] = x; - ypoint[n++] = y + d; - xpoint[n] = x; - ypoint[n++] = y - a; - - if (y != y1) { - xpoint[n] = xo + w; - ypoint[n++] = y - a; - - if (x1 < xo + w) { - xpoint[n] = xo + w; - ypoint[n++] = y1 - a; - } - } - - xpoint[n] = x1; - ypoint[n++] = y1 - a; - xpoint[n] = x1; - ypoint[n++] = y1 + d; - - if (y != y1) { - xpoint[n] = xo; - ypoint[n++] = y1 + d; - if (x > xo) { - xpoint[n] = xo; - ypoint[n++] = y + d; - } - } - xpoint[n] = xpoint[0]; - ypoint[n++] = ypoint[0]; - - //lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "]\n"; - //lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "]\n"; - //for (i = 0; i < np; ++i) - // lyxerr << "XY[" << xpoint[i] << " " << ypoint[i] << "]\n"; -} + MathCursorPos i1; + MathCursorPos i2; + getSelection(i1, i2); -void MathCursor::setAccent(int ac) -{ - if (ac > 0 && accent < 8) - nestaccent[accent++] = ac; - else - accent = 0; // consumed! -} + //lyxerr << "selection from: " << i1 << " to " << i2 << "\n"; + if (i1.idx_ == i2.idx_) { + MathXArray & c = i1.xcell(); + int x1 = c.xo() + c.pos2x(i1.pos_); + int y1 = c.yo() - c.ascent(); + int x2 = c.xo() + c.pos2x(i2.pos_); + int y2 = c.yo() + c.descent(); + pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection); + } else { -int MathCursor::getAccent() const -{ - return accent > 0 ? nestaccent[accent - 1] : 0; +#if RECTANGULAR_SELECT + std::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(); + int y1 = c.yo() - c.ascent(); + int x2 = c.xo() + c.width(); + int y2 = c.yo() + c.descent(); + pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection); + } +#else + // leftmost cell + MathXArray & c = i1.xcell(); + int x1 = c.xo() + c.pos2x(i1.pos_); + int y1 = c.yo() - c.ascent(); + int x2 = c.xo() + c.width(); + int y2 = c.yo() + c.descent(); + pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection); + // middle cells + for (int idx = i1.idx_ + 1; idx < i2.idx_; ++idx) { + MathXArray & c = i1.xcell(idx); + int x1 = c.xo(); + int y1 = c.yo() - c.ascent(); + int x2 = c.xo() + c.width(); + int y2 = c.yo() + c.descent(); + pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection); + } + // rightmost cell + MathXArray & cr = i2.xcell(); + x1 = cr.xo(); + y1 = cr.yo() - cr.ascent(); + x2 = cr.xo() + cr.pos2x(i2.pos_); + y2 = cr.yo() + cr.descent(); + pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection); +#endif + } } -void MathCursor::doAccent(char c, MathTextCodes t) +void MathCursor::handleFont(MathTextCodes t) { - MathInset * ac = 0; - - for (int i = accent - 1; i >= 0; --i) { - if (i == accent - 1) - ac = new MathAccentInset(c, t, nestaccent[i]); - else - ac = new MathAccentInset(ac, nestaccent[i]); + if (selection) { + MathCursorPos i1; + MathCursorPos i2; + getSelection(i1, i2); + if (i1.idx_ == i2.idx_) { + MathArray & ar = i1.cell(); + for (int pos = i1.pos_; pos != i2.pos_; ar.next(pos)) + if (!ar.isInset(pos) && isalnum(ar.GetChar(pos))) { + MathTextCodes c = ar.GetCode(pos) == t ? LM_TC_VAR : t; + ar.setCode(pos, c); + } + } + } else { + lastcode = (lastcode == t) ? LM_TC_VAR : t; } - - if (ac) - insert(ac); - - accent = 0; // consumed! } -void MathCursor::doAccent(MathInset * p) +void MathCursor::handleAccent(string const & name, int code) { - MathInset * ac = 0; - - for (int i = accent - 1; i >= 0; --i) { - if (i == accent - 1) - ac = new MathAccentInset(p, nestaccent[i]); - else - ac = new MathAccentInset(ac, nestaccent[i]); + MathDecorationInset * p = new MathDecorationInset(name, code); + if (selection) { + SelCut(); + p->cell(0) = theSelection.glue(); } - - if (ac) - insert(ac); - - accent = 0; // consumed! + insert(p); + push(p, true); } -void MathCursor::toggleLastCode(MathTextCodes t) +void MathCursor::handleDelim(int l, int r) { - if (lastcode == t) - lastcode = LM_TC_VAR; - else - lastcode = t; + MathDelimInset * p = new MathDelimInset(l, r); + if (selection) { + SelCut(); + p->cell(0) = theSelection.glue(); + } + insert(p); + plainLeft(); + push(p, true); } void MathCursor::GetPos(int & x, int & y) { - x = xarray().xo() + xarray().pos2x(cursor_); + x = xarray().xo() + xarray().pos2x(cursor().pos_); y = xarray().yo(); } MathTextCodes MathCursor::nextCode() const { - return array().GetCode(cursor_); + return array().GetCode(cursor().pos_); } MathTextCodes MathCursor::prevCode() const { - return array().GetCode(cursor_ - 1); + return array().GetCode(cursor().pos_ - 1); } MathInset * MathCursor::par() const { - return par_; + return cursor().par_; } @@ -886,7 +1015,7 @@ InsetFormulaBase const * MathCursor::formula() int MathCursor::pos() const { - return cursor_; + return cursor().pos_; } @@ -922,30 +1051,26 @@ MathTextCodes MathCursor::getLastCode() const MathInset * MathCursor::enclosing(MathInsetTypes t, int & idx) const { - if (par_->GetType() == t) { - lyxerr << "enclosing par is current\n"; - idx = idx_; - return par_; - } - for (int i = path_.size() - 1; i >= 0; --i) { - lyxerr << "checking level " << i << "\n"; - if (path_[i].par_->GetType() == t) { - idx = path_[i].idx_; - return path_[i].par_; + for (int i = Cursor_.size() - 1; i >= 0; --i) { + //lyxerr << "checking level " << i << "\n"; + if (Cursor_[i].par_->GetType() == t) { + idx = Cursor_[i].idx_; + return Cursor_[i].par_; } } return 0; } + void MathCursor::pullArg() { // pullArg + dump("pullarg"); MathArray a = array(); - if (!Left()) - return; - normalize(); - array().erase(cursor_); - array().insert(cursor_, a); + if (pop()) { + array().erase(cursor().pos_); + array().insert(cursor().pos_, a); + } } @@ -962,38 +1087,38 @@ void MathCursor::normalize() const #endif MathCursor * it = const_cast(this); - if (idx_ < 0 || idx_ > par_->nargs()) + if (cursor().idx_ < 0 || cursor().idx_ > cursor().par_->nargs()) lyxerr << "this should not really happen - 1\n"; - it->idx_ = max(idx_, 0); - it->idx_ = min(idx_, par_->nargs()); + it->cursor().idx_ = max(cursor().idx_, 0); + it->cursor().idx_ = min(cursor().idx_, cursor().par_->nargs()); - if (cursor_ < 0 || cursor_ > array().size()) + if (cursor().pos_ < 0 || cursor().pos_ > array().size()) lyxerr << "this should not really happen - 2\n"; - it->cursor_ = max(cursor_, 0); - it->cursor_ = min(cursor_, array().size()); + it->cursor().pos_ = max(cursor().pos_, 0); + it->cursor().pos_ = min(cursor().pos_, array().size()); } int MathCursor::col() const { - return par_->col(idx_); + return par()->col(cursor().idx_); } int MathCursor::row() const { - return par_->row(idx_); + return par()->row(cursor().idx_); } /* -char MathIter::GetChar() const +char MathCursorPos::GetChar() const { - return array().GetChar(cursor_); + return array().GetChar(cursor().pos_); } -string MathIter::readString() +string MathCursorPos::readString() { string s; int code = nextCode(); @@ -1004,121 +1129,272 @@ string MathIter::readString() } */ + MathInset * MathCursor::prevInset() const { normalize(); - int c = cursor_; + int c = cursor().pos_; if (!array().prev(c)) return 0; - return array().GetInset(c); + return array().nextInset(c); } -MathInset * MathCursor::prevActiveInset() const + +MathInset * MathCursor::nextInset() const { - if (cursor_ <= 0 || !array().isInset(cursor_ - 1)) - return 0; - MathInset * inset = prevInset(); - return inset->nargs() ? inset : 0; + normalize(); + return array().nextInset(cursor().pos_); } -MathInset * MathCursor::nextInset() const +MathUpDownInset * MathCursor::nearbyUpDownInset() const { normalize(); - return array().GetInset(cursor_); + MathInset * p = array().prevInset(cursor().pos_); + if (p && p->isUpDownInset()) + return static_cast(p); + p = array().nextInset(cursor().pos_); + if (p && p->isUpDownInset()) + return static_cast(p); + return 0; } -MathInset * MathCursor::nextActiveInset() const +MathArray & MathCursor::array() const { - if (!array().isInset(cursor_)) - return 0; - MathInset * inset = nextInset(); - return inset->nargs() ? inset : 0; + static MathArray dummy; + if (!cursor().par_) { + lyxerr << "############ par_ not valid\n"; + return dummy; + } + + if (cursor().idx_ < 0 || cursor().idx_ >= cursor().par_->nargs()) { + lyxerr << "############ idx_ " << cursor().idx_ << " not valid\n"; + return dummy; + } + + return cursor().cell(); +} + + +MathXArray & MathCursor::xarray() const +{ + return cursor().xcell(); +} + + +bool MathCursor::nextIsInset() const +{ + return cursor().pos_ < array().size() && MathIsInset(nextCode()); } -MathScriptInset * MathCursor::prevScriptInset() const +bool MathCursor::prevIsInset() const +{ + return cursor().pos_ > 0 && MathIsInset(prevCode()); +} + + +int MathCursor::xpos() const { normalize(); - return array().prevScriptInset(cursor_); + return xarray().pos2x(cursor().pos_); } +void MathCursor::gotoX(int x) +{ + cursor().pos_ = xarray().x2pos(x); +} -MathArray & MathCursor::array() const + +void MathCursor::idxNext() { - static MathArray dummy; - if (!par_) { - lyxerr << "############ par_ not valid\n"; - return dummy; + cursor().par_->idxNext(cursor().idx_, cursor().pos_); +} + + +void MathCursor::idxPrev() +{ + cursor().par_->idxPrev(cursor().idx_, cursor().pos_); +} + + +void MathCursor::splitCell() +{ + if (cursor().idx_ == cursor().par_->nargs() - 1) + return; + MathArray ar = array(); + ar.erase(0, cursor().pos_); + array().erase(cursor().pos_, array().size()); + ++cursor().idx_; + cursor().pos_ = 0; + array().insert(0, ar); +} + + +void MathCursor::breakLine() +{ + MathMatrixInset * p = static_cast(formula()->par()); + if (p->GetType() == LM_OT_SIMPLE || p->GetType() == LM_OT_EQUATION) { + p->mutate(LM_OT_EQNARRAY); + p->addRow(0); + cursor().idx_ = p->nrows(); + cursor().pos_ = 0; + } else { + p->addRow(row()); + + // split line + const int r = row(); + for (int c = col() + 1; c < p->ncols(); ++c) { + const int i1 = p->index(r, c); + const int i2 = p->index(r + 1, c); + lyxerr << "swapping cells " << i1 << " and " << i2 << "\n"; + p->cell(i1).swap(p->cell(i2)); + } + + // split cell + splitCell(); + p->cell(cursor().idx_).swap(p->cell(cursor().idx_ + p->ncols() - 1)); } +} - if (idx_ < 0 || idx_ >= par_->nargs()) { - lyxerr << "############ idx_ " << idx_ << " not valid\n"; - return dummy; + +char MathCursor::valign() const +{ + int idx; + MathGridInset * p = + static_cast(enclosing(LM_OT_MATRIX, idx)); + return p ? p->valign() : 0; +} + + +char MathCursor::halign() const +{ + int idx; + MathGridInset * p = + static_cast(enclosing(LM_OT_MATRIX, idx)); + return p ? p->halign(idx % p->ncols()) : 0; +} + + +MathCursorPos MathCursor::firstSelectionPos() const +{ + MathCursorPos anc = normalAnchor(); + return anc < cursor() ? anc : cursor(); +} + + +MathCursorPos MathCursor::lastSelectionPos() const +{ + MathCursorPos anc = normalAnchor(); + return anc < cursor() ? cursor() : anc; +} + + +void MathCursor::getSelection(MathCursorPos & i1, MathCursorPos & i2) const +{ + MathCursorPos anc = normalAnchor(); + if (anc < cursor()) { + i1 = anc; + i2 = cursor(); + } else { + i1 = cursor(); + i2 = anc; } +} - return par_->cell(idx_); + +MathCursorPos & MathCursor::cursor() +{ + return Cursor_.back(); } -MathXArray & MathCursor::xarray() const +MathCursorPos const & MathCursor::cursor() const { - return par_->xcell(idx_); + return Cursor_.back(); } -bool MathCursor::nextIsInset() const +//////////////////////////////////////////////////////////////////////// + + +bool operator==(MathCursorPos const & ti, MathCursorPos const & it) { - return cursor_ < array().size() && MathIsInset(nextCode()); + return ti.par_ == it.par_ && ti.idx_ == it.idx_ && ti.pos_ == it.pos_; } -bool MathCursor::nextIsActive() const +bool operator<(MathCursorPos const & ti, MathCursorPos const & it) { - return nextIsInset() && nextInset()->nargs(); + 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_; } -bool MathCursor::prevIsInset() const +MathArray & MathCursorPos::cell(int idx) const +{ + return par_->cell(idx); +} + +MathArray & MathCursorPos::cell() const { - return cursor_ > 0 && MathIsInset(prevCode()); + return par_->cell(idx_); } -bool MathCursor::prevIsActive() const +MathXArray & MathCursorPos::xcell(int idx) const { - return prevIsInset() && prevInset()->nargs(); + return par_->xcell(idx); } -bool MathCursor::IsFont() const +MathXArray & MathCursorPos::xcell() const { - return MathIsFont(nextCode()); + return par_->xcell(idx_); } -bool MathCursor::IsScript() const +MathCursorPos MathCursor::normalAnchor() const { - normalize(); - return MathIsScript(nextCode()); + // use Anchor on the same level as Cursor + MathCursorPos normal = Anchor_[Cursor_.size() - 1]; + if (Cursor_.size() < Anchor_.size() && !(normal < cursor())) { + // anchor is behind cursor -> move anchor behind the inset + normal.cell().next(normal.pos_); + } + //lyxerr << "normalizing: from " << Anchor_[Anchor_.size() - 1] << " to " + // << normal << "\n"; + return normal; } -int MathCursor::xpos() const +bool MathCursorPos::idxUp() { - normalize(); - return xarray().pos2x(cursor_); + return par_->idxUp(idx_, pos_); } -void MathCursor::gotoX(int x) + +bool MathCursorPos::idxDown() { - cursor_ = xarray().x2pos(x); + return par_->idxDown(idx_, pos_); } -void MathCursor::idxRight() + +bool MathCursorPos::idxLeft() +{ + return par_->idxLeft(idx_, pos_); +} + + +bool MathCursorPos::idxRight() { - par_->idxRight(idx_, cursor_); + return par_->idxRight(idx_, pos_); }