+using std::endl;
+#ifndef CXX_GLOBAL_CSTD
+using std::isalpha;
+#endif
+using std::min;
+using std::swap;
+
+
+
+// our own cut buffer
+limited_stack<string> theCutBuffer;
+
+
+LCursor::LCursor(BufferView & bv)
+ : cursor_(1), anchor_(1), bv_(&bv), current_(0),
+ cached_y_(0), x_target_(-1),
+ selection_(false), mark_(false)
+{}
+
+
+void LCursor::reset()
+{
+ cursor_.clear();
+ anchor_.clear();
+ cursor_.push_back(CursorSlice());
+ anchor_.push_back(CursorSlice());
+ current_ = 0;
+ cached_y_ = 0;
+ clearTargetX();
+ selection_ = false;
+ mark_ = false;
+}
+
+
+DispatchResult LCursor::dispatch(FuncRequest const & cmd0)
+{
+ //lyxerr << "\nLCursor::dispatch: cmd: " << cmd0 << endl << *this << endl;
+ FuncRequest cmd = cmd0;
+ for (current_ = cursor_.size() - 1; current_ >= 1; --current_) {
+ lyxerr << "trying to dispatch to inset " << inset() << endl;
+ DispatchResult res = inset()->dispatch(*this, cmd);
+ if (res.dispatched()) {
+ lyxerr << " successfully dispatched to inset " << inset() << endl;
+ return DispatchResult(true, true);
+ }
+ // "Mutate" the request for semi-handled requests that need
+ // additional handling in outer levels.
+ switch (res.val()) {
+ case FINISHED:
+ cmd = FuncRequest(LFUN_FINISHED_LEFT);
+ break;
+ case FINISHED_RIGHT:
+ cmd = FuncRequest(LFUN_FINISHED_RIGHT);
+ break;
+ case FINISHED_UP:
+ cmd = FuncRequest(LFUN_FINISHED_UP);
+ break;
+ case FINISHED_DOWN:
+ cmd = FuncRequest(LFUN_FINISHED_DOWN);
+ break;
+ default:
+ //lyxerr << "not handled on level " << current_
+ // << " val: " << res.val() << endl;
+ break;
+ }
+ }
+ BOOST_ASSERT(current_ == 0);
+ lyxerr << "trying to dispatch to main text " << bv_->text()
+ << " with cursor: " << *this << endl;
+ DispatchResult res = bv_->text()->dispatch(*this, cmd);
+ //lyxerr << " result: " << res.val() << endl;
+ return res;
+}
+
+
+void LCursor::push(InsetBase * inset)
+{
+ lyxerr << "LCursor::push() inset: " << inset << endl;
+ cursor_.push_back(CursorSlice(inset));
+ anchor_.push_back(CursorSlice(inset));
+ ++current_;
+ updatePos();
+}
+
+
+void LCursor::pop(int depth)
+{
+ while (int(cursor_.size()) > depth + 1)
+ pop();
+ lyxerr << "LCursor::pop() result: " << *this << endl;
+}
+
+
+void LCursor::pop()
+{
+ BOOST_ASSERT(cursor_.size() >= 1);
+ cursor_.pop_back();
+ anchor_.pop_back();
+ current_ = cursor_.size() - 1;
+}
+
+
+void LCursor::pushLeft(InsetBase * p)
+{
+ BOOST_ASSERT(!cursor_.empty());
+ //lyxerr << "Entering inset " << t << " left" << endl;
+ push(p);
+ p->idxFirst(*this);
+}
+
+
+bool LCursor::popLeft()
+{
+ BOOST_ASSERT(!cursor_.empty());
+ //lyxerr << "Leaving inset to the left" << endl;
+ if (depth() <= 1) {
+ if (depth() == 1)
+ inset()->notifyCursorLeaves(idx());
+ return false;
+ }
+ inset()->notifyCursorLeaves(idx());
+ pop();
+ return true;
+}
+
+
+bool LCursor::popRight()
+{
+ BOOST_ASSERT(!cursor_.empty());
+ //lyxerr << "Leaving inset to the right" << endl;
+ if (depth() <= 1) {
+ if (depth() == 1)
+ inset()->notifyCursorLeaves(idx());
+ return false;
+ }
+ inset()->notifyCursorLeaves(idx());
+ pop();
+ posRight();
+ return true;
+}
+
+
+CursorSlice & LCursor::current()
+{
+ BOOST_ASSERT(!cursor_.empty());
+ //lyxerr << "accessing cursor slice " << current_
+ // << ": " << cursor_[current_] << endl;
+ return cursor_[current_];
+}
+
+
+CursorSlice const & LCursor::current() const
+{
+ //lyxerr << "accessing cursor slice " << current_
+ // << ": " << cursor_[current_] << endl;
+ return cursor_[current_];
+}
+
+
+int LCursor::currentMode()
+{
+ BOOST_ASSERT(!cursor_.empty());
+ for (int i = cursor_.size() - 1; i >= 1; --i) {
+ int res = cursor_[i].inset()->currentMode();
+ if (res != MathInset::UNDECIDED_MODE)
+ return res;
+ }
+ return MathInset::TEXT_MODE;
+}
+
+
+LyXText * LCursor::innerText() const
+{
+ BOOST_ASSERT(!cursor_.empty());
+ //lyxerr << "LCursor::innerText() depth: " << cursor_.size() << endl;
+ if (cursor_.size() > 1) {
+ // go up until first non-0 text is hit
+ // (innermost text is 0 in mathed)
+ for (int i = cursor_.size() - 1; i >= 1; --i)
+ if (cursor_[i].text())
+ return cursor_[i].text();
+ }
+ return bv_->text();
+}
+
+
+CursorSlice const & LCursor::innerTextSlice() const
+{
+ BOOST_ASSERT(!cursor_.empty());
+ //lyxerr << "LCursor::innerTextSlice() depth: " << cursor_.size() << endl;
+ if (cursor_.size() > 1) {
+ // go up until first non-0 text is hit
+ // (innermost text is 0 in mathed)
+ for (int i = cursor_.size() - 1; i >= 1; --i)
+ if (cursor_[i].text())
+ return cursor_[i];
+ }
+ return cursor_[0];
+}
+
+
+void LCursor::updatePos()
+{
+ BOOST_ASSERT(!cursor_.empty());
+ if (cursor_.size() > 1)
+ cached_y_ = bv_->top_y() + cursor_.back().inset()->yo();
+ //cached_y_ = cursor_.back().inset()->yo();
+}
+
+
+void LCursor::getDim(int & asc, int & des) const
+{
+ BOOST_ASSERT(!cursor_.empty());
+ LyXText * text = innerText();
+#warning crashes with text-in-math
+ if (0 && text) {
+ RowList::iterator const rit = text->cursorRow();
+ if (rit != text->endRow()) {
+ asc = rit->baseline();
+ des = rit->height() - asc;
+ } else {
+ asc = 10;
+ des = 10;
+ }
+ } else {
+ asc = 10;
+ des = 10;
+ //innerInset()->getCursorDim(asc, des);
+ }
+}
+
+
+void LCursor::getPos(int & x, int & y) const
+{
+ BOOST_ASSERT(!cursor_.empty());
+ x = 0;
+ y = 0;
+ if (cursor_.size() <= 1) {
+ x = bv_->text()->cursorX(cursor_.front());
+ y = bv_->text()->cursorY(cursor_.front());
+ } else {
+ inset()->getCursorPos(cursor_.back(), x, y);
+ // getCursorPos gives _screen_ coordinates. We need to add
+ // top_y to get document coordinates. This is hidden in cached_y_.
+ //y += cached_y_ - inset()->yo();
+ // The rest is non-obvious. The reason we have to have these
+ // extra computation is that the getCursorPos() calls rely
+ // on the inset's own knowledge of its screen position.
+ // If we scroll up or down in a big enough increment,
+ // inset->draw() is not called: this doesn't update
+ // inset.yo_, so getCursor() returns an old value.
+ // Ugly as you like.
+ }
+ //lyxerr << "#### LCursor::getPos: " << *this
+ // << " x: " << x << " y: " << y << endl;
+}
+
+
+void LCursor::paste(string const & data)
+{
+ dispatch(FuncRequest(LFUN_PASTE, data));
+}
+
+
+InsetBase * LCursor::innerInsetOfType(int code) const
+{
+ for (int i = cursor_.size() - 1; i >= 1; --i)
+ if (cursor_[i].inset_->lyxCode() == code)
+ return cursor_[i].inset_;
+ return 0;
+}
+
+
+InsetTabular * LCursor::innerInsetTabular() const
+{
+ return static_cast<InsetTabular *>(innerInsetOfType(InsetBase::TABULAR_CODE));
+}
+
+
+void LCursor::resetAnchor()
+{
+ anchor_ = cursor_;
+}
+
+
+BufferView & LCursor::bv() const
+{
+ return *bv_;
+}
+
+
+MathAtom const & LCursor::prevAtom() const
+{
+ BOOST_ASSERT(pos() > 0);
+ return cell()[pos() - 1];
+}
+
+
+MathAtom & LCursor::prevAtom()
+{
+ BOOST_ASSERT(pos() > 0);
+ return cell()[pos() - 1];
+}
+
+
+MathAtom const & LCursor::nextAtom() const
+{
+ BOOST_ASSERT(pos() < lastpos());
+ return cell()[pos()];
+}
+
+
+MathAtom & LCursor::nextAtom()
+{
+ BOOST_ASSERT(pos() < lastpos());
+ return cell()[pos()];
+}
+
+
+bool LCursor::posLeft()
+{
+ if (pos() == 0)
+ return false;
+ --pos();
+ return true;
+}
+
+
+bool LCursor::posRight()
+{
+ if (pos() == lastpos())
+ return false;
+ ++pos();
+ return true;
+}
+
+
+CursorSlice & LCursor::anchor()
+{
+ return anchor_.back();
+}
+
+
+CursorSlice const & LCursor::anchor() const
+{
+ return anchor_.back();
+}
+
+
+CursorSlice const & LCursor::selBegin() const
+{
+ if (!selection())
+ return cursor_.back();
+ // can't use std::min as this creates a new object
+ return anchor() < cursor_.back() ? anchor() : cursor_.back();
+}
+
+
+CursorSlice & LCursor::selBegin()
+{
+ if (!selection())
+ return cursor_.back();
+ return anchor() < cursor_.back() ? anchor() : cursor_.back();
+}
+
+
+CursorSlice const & LCursor::selEnd() const
+{
+ if (!selection())
+ return cursor_.back();
+ return anchor() > cursor_.back() ? anchor() : cursor_.back();
+}
+
+
+CursorSlice & LCursor::selEnd()
+{
+ if (selection())
+ return cursor_.back();
+ return anchor() > cursor_.back() ? anchor() : cursor_.back();
+}
+
+
+void LCursor::setSelection()
+{
+ selection() = true;
+ // a selection with no contents is not a selection
+ if (par() == anchor().par() && pos() == anchor().pos())
+ selection() = false;
+}
+
+
+void LCursor::setSelection(CursorBase const & where, size_t n)
+{
+ selection() = true;
+ cursor_ = where;
+ anchor_ = where;
+ pos() += n;
+}
+
+
+void LCursor::clearSelection()
+{
+ selection() = false;
+ mark() = false;
+ resetAnchor();
+ bv().unsetXSel();
+}
+
+
+int & LCursor::x_target()
+{
+ return x_target_;
+}
+
+
+int LCursor::x_target() const
+{
+ return x_target_;
+}
+
+
+void LCursor::clearTargetX()
+{
+ x_target_ = -1;
+}
+
+
+LyXText * LCursor::text() const
+{
+ return current_ ? current().text() : bv_->text();
+}
+
+
+Paragraph & LCursor::paragraph()
+{
+ BOOST_ASSERT(!inMathed());
+ return current_ ? current().paragraph() : *bv_->text()->getPar(par());
+}
+
+
+Paragraph const & LCursor::paragraph() const
+{
+ BOOST_ASSERT(!inMathed());
+ return current_ ? current().paragraph() : *bv_->text()->getPar(par());
+}
+
+
+LCursor::par_type LCursor::lastpar() const
+{
+ return inMathed() ? 0 : text()->paragraphs().size() - 1;
+}
+
+
+LCursor::pos_type LCursor::lastpos() const
+{
+ InsetBase * inset = current().inset();
+ return inset && inset->asMathInset() ? cell().size() : paragraph().size();
+}
+
+
+LCursor::row_type LCursor::crow() const
+{
+ return paragraph().row(pos());
+}
+
+
+LCursor::row_type LCursor::lastcrow() const
+{
+ return paragraph().rows.size();
+}
+
+
+size_t LCursor::nargs() const
+{
+ // assume 1x1 grid for 'plain text'
+ return current_ ? current().nargs() : 1;
+}
+
+
+size_t LCursor::ncols() const
+{
+ // assume 1x1 grid for 'plain text'
+ return current_ ? current().ncols() : 1;
+}
+
+
+size_t LCursor::nrows() const
+{
+ // assume 1x1 grid for 'plain text'
+ return current_ ? current().nrows() : 1;
+}
+
+
+LCursor::row_type LCursor::row() const
+{
+ BOOST_ASSERT(current_ > 0);
+ return current().row();
+}
+
+
+LCursor::col_type LCursor::col() const
+{
+ BOOST_ASSERT(current_ > 0);
+ return current().col();
+}
+
+
+MathArray const & LCursor::cell() const
+{
+ BOOST_ASSERT(current_ > 0);
+ return current().cell();
+}
+
+
+MathArray & LCursor::cell()
+{
+ BOOST_ASSERT(current_ > 0);
+ return current().cell();
+}
+
+
+void LCursor::info(std::ostream & os)
+{
+ for (int i = 1, n = depth(); i < n; ++i) {
+ cursor_[i].inset()->infoize(os);
+ os << " ";
+ }
+ if (pos() != 0)
+ prevInset()->infoize2(os);
+ // overwite old message
+ os << " ";
+}
+
+
+namespace {
+
+void region(CursorSlice const & i1, CursorSlice const & i2,
+ LCursor::row_type & r1, LCursor::row_type & r2,
+ LCursor::col_type & c1, LCursor::col_type & c2)
+{
+ InsetBase * p = i1.inset();
+ 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);
+}
+
+}
+
+
+string LCursor::grabSelection()
+{
+ if (!selection())
+ return string();
+
+ CursorSlice i1 = selBegin();
+ CursorSlice i2 = selEnd();
+
+ if (i1.idx_ == i2.idx_) {
+ if (i1.inset()->asMathInset()) {
+ MathArray::const_iterator it = i1.cell().begin();
+ return asString(MathArray(it + i1.pos_, it + i2.pos_));
+ } else {
+ return "unknown selection 1";
+ }
+ }
+
+ row_type r1, r2;
+ col_type c1, c2;
+ region(i1, i2, r1, r2, c1, c2);
+
+ string data;
+ if (i1.inset()->asMathInset()) {
+ for (row_type row = r1; row <= r2; ++row) {
+ if (row > r1)
+ data += "\\\\";
+ for (col_type col = c1; col <= c2; ++col) {
+ if (col > c1)
+ data += '&';
+ data += asString(i1.asMathInset()->cell(i1.asMathInset()->index(row, col)));
+ }
+ }
+ } else {
+ data = "unknown selection 2";
+ }
+ return data;
+}
+
+
+void LCursor::eraseSelection()
+{
+ CursorSlice i1 = selBegin();
+ CursorSlice i2 = selEnd();
+#warning FIXME
+ if (i1.inset()->asMathInset()) {
+ if (i1.idx_ == i2.idx_) {
+ i1.cell().erase(i1.pos_, i2.pos_);
+ } else {
+ MathInset * p = i1.asMathInset();
+ 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)).clear();
+ }
+ current() = i1;
+ } else {
+ lyxerr << "can't erase this selection 1" << endl;
+ }
+}
+
+
+string LCursor::grabAndEraseSelection()
+{
+ if (!selection())
+ return string();
+ string res = grabSelection();
+ eraseSelection();
+ selection() = false;
+ return res;
+}
+
+
+void LCursor::selClear()
+{
+ resetAnchor();
+ clearSelection();
+}
+
+
+void LCursor::selCopy()
+{
+ if (selection()) {
+ theCutBuffer.push(grabSelection());
+ selection() = false;
+ } else {
+ //theCutBuffer.erase();
+ }
+}
+
+
+void LCursor::selCut()
+{
+ theCutBuffer.push(grabAndEraseSelection());
+}
+
+
+void LCursor::selDel()
+{
+ if (selection()) {
+ eraseSelection();
+ selection() = false;
+ }
+}
+
+
+void LCursor::selPaste(size_t n)
+{
+ selClearOrDel();
+ if (n < theCutBuffer.size())
+ paste(theCutBuffer[n]);
+ //grabSelection();
+ selection() = false;
+}
+
+
+void LCursor::selHandle(bool sel)
+{
+ if (sel == selection())
+ return;
+ resetAnchor();
+ selection() = sel;
+}
+
+
+void LCursor::selStart()
+{
+ resetAnchor();
+ selection() = true;
+}
+
+
+void LCursor::selClearOrDel()
+{
+ if (lyxrc.auto_region_delete)
+ selDel();
+ else
+ selection() = false;
+}
+
+
+std::ostream & operator<<(std::ostream & os, LCursor const & cur)
+{
+ os << "\n";
+ for (size_t i = 0, n = cur.cursor_.size(); i != n; ++i)
+ os << " (" << cur.cursor_[i] << " | " << cur.anchor_[i] << "\n";
+ return os << "current: " << cur.current_ << endl;
+}
+
+
+
+
+//
+// CursorBase
+//
+
+
+void increment(CursorBase & it)
+{
+ CursorSlice & top = it.back();
+ MathArray & ar = top.asMathInset()->cell(top.idx_);
+
+ // move into the current inset if possible
+ // it is impossible for pos() == size()!
+ MathInset * n = 0;
+ if (top.pos() != top.lastpos())
+ n = (ar.begin() + top.pos_)->nucleus();
+ if (n && n->isActive()) {
+ it.push_back(CursorSlice(n));
+ return;
+ }
+
+ // otherwise move on one cell back if possible
+ if (top.pos() < top.lastpos()) {
+ // pos() == lastpos() is valid!
+ ++top.pos_;
+ return;
+ }
+
+ // otherwise try to move on one cell if possible
+ while (top.idx() < top.lastidx()) {
+ ++top.idx_;
+ if (top.asMathInset()->validCell(top.idx_)) {
+ top.pos_ = 0;
+ return;
+ }
+ }
+
+ // otherwise leave array, move on one back
+ // this might yield pos() == size(), but that's a ok.
+ it.pop_back();
+ // it certainly invalidates top
+ ++it.back().pos_;
+}
+
+
+CursorBase ibegin(InsetBase * p)
+{
+ CursorBase it;
+ it.push_back(CursorSlice(p));
+ return it;
+}
+
+
+CursorBase iend(InsetBase * p)
+{
+ CursorBase it;
+ it.push_back(CursorSlice(p));
+ CursorSlice & cur = it.back();
+ cur.idx() = cur.lastidx();
+ cur.pos() = cur.lastpos();
+ return it;
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////
+//
+// The part below is the non-integrated rest of the original math
+// cursor. This should be either generalized for texted or moved
+// back to the math insets.
+//
+///////////////////////////////////////////////////////////////////
+
+#include "mathed/math_braceinset.h"
+#include "mathed/math_charinset.h"
+#include "mathed/math_commentinset.h"
+#include "mathed/math_factory.h"
+#include "mathed/math_gridinset.h"
+#include "mathed/math_macroarg.h"
+#include "mathed/math_macrotemplate.h"
+#include "mathed/math_mathmlstream.h"
+#include "mathed/math_scriptinset.h"
+#include "mathed/math_spaceinset.h"
+#include "mathed/math_support.h"
+#include "mathed/math_unknowninset.h"
+
+//#define FILEDEBUG 1
+
+
+bool LCursor::isInside(InsetBase const * p)
+{
+ for (unsigned i = 0; i < depth(); ++i)
+ if (cursor_[i].inset() == p)
+ return true;
+ return false;
+}
+
+
+bool LCursor::openable(MathAtom const & t)
+{
+ if (!t->isActive())
+ return false;
+
+ if (t->lock())
+ return false;
+
+ if (!selection())
+ return true;
+
+ // we can't move into anything new during selection
+ if (depth() == anchor_.size())
+ return false;
+ if (t.nucleus() != anchor_[depth()].inset())
+ return false;
+
+ return true;
+}
+
+
+bool LCursor::inNucleus()
+{
+ return inset()->asMathInset()->asScriptInset() && idx() == 2;
+}
+
+
+bool LCursor::left()
+{
+ autocorrect() = false;
+ clearTargetX();
+ if (inMacroMode()) {
+ macroModeClose();
+ return true;
+ }
+
+ if (pos() != 0 && openable(prevAtom())) {
+ posLeft();
+ push(nextAtom().nucleus());
+ inset()->idxLast(*this);
+ return true;
+ }
+
+ return posLeft() || idxLeft() || popLeft() || selection();
+}
+
+
+bool LCursor::right()
+{
+ autocorrect() = false;
+ clearTargetX();
+ if (inMacroMode()) {
+ macroModeClose();
+ return true;
+ }
+
+ if (pos() != lastpos() && openable(nextAtom())) {
+ pushLeft(nextAtom().nucleus());
+ inset()->idxFirst(*this);
+ return true;
+ }
+
+ return posRight() || idxRight() || popRight() || selection();
+}
+
+
+bool positionable(CursorBase const & cursor, CursorBase const & anchor)
+{
+ // avoid deeper nested insets when selecting
+ if (cursor.size() > anchor.size())
+ return false;
+
+ // anchor might be deeper, should have same path then
+ for (size_t i = 0; i < cursor.size(); ++i)
+ if (cursor[i].inset() != anchor[i].inset())
+ return false;
+
+ // position should be ok.
+ return true;
+}
+
+
+void LCursor::setScreenPos(int x, int y)
+{
+ bool res = bruteFind(x, y, formula()->xlow(), formula()->xhigh(),
+ formula()->ylow(), formula()->yhigh());
+ if (!res) {
+ // this can happen on creation of "math-display"
+ idx() = 0;
+ pos() = 0;
+ }
+ clearTargetX();
+}
+
+
+
+bool LCursor::home()
+{
+ autocorrect() = false;
+ macroModeClose();
+ if (!inset()->idxHome(*this))
+ return popLeft();
+ clearTargetX();
+ return true;
+}
+
+
+bool LCursor::end()
+{
+ autocorrect() = false;
+ macroModeClose();
+ if (!inset()->idxEnd(*this))
+ return popRight();
+ clearTargetX();
+ return true;
+}
+
+
+void LCursor::plainErase()
+{
+ cell().erase(pos());
+}
+
+
+void LCursor::markInsert()
+{
+ cell().insert(pos(), MathAtom(new MathCharInset(0)));
+}
+
+
+void LCursor::markErase()
+{
+ cell().erase(pos());
+}
+
+
+void LCursor::plainInsert(MathAtom const & t)
+{
+ cell().insert(pos(), t);
+ ++pos();
+}
+
+
+void LCursor::insert2(string const & str)
+{
+ MathArray ar;
+ asArray(str, ar);
+ insert(ar);
+}
+
+
+void LCursor::insert(string const & str)
+{
+ lyxerr << "inserting '" << str << "'" << endl;
+ selClearOrDel();
+ for (string::const_iterator it = str.begin(); it != str.end(); ++it)
+ plainInsert(MathAtom(new MathCharInset(*it)));
+}
+
+
+void LCursor::insert(char c)
+{
+ lyxerr << "inserting '" << c << "'" << endl;
+ selClearOrDel();
+ plainInsert(MathAtom(new MathCharInset(c)));
+}
+
+
+void LCursor::insert(MathAtom const & t)
+{
+ macroModeClose();
+ selClearOrDel();
+ plainInsert(t);
+}
+
+
+void LCursor::niceInsert(string const & t)
+{
+ lyxerr << "*** LCursor::niceInsert 1: " << t << endl;
+ MathArray ar;
+ asArray(t, ar);
+ if (ar.size() == 1)
+ niceInsert(ar[0]);
+ else
+ insert(ar);
+}