]> git.lyx.org Git - lyx.git/blobdiff - src/mathed/math_cursor.C
Jean-Marc's fix for wrong descent
[lyx.git] / src / mathed / math_cursor.C
index 09dcfe91a1c6b7b7b74be8a1e59757289dd5c845..cfc4f88e5688c1bb4a4b66f979b0ed0142a244ab 100644 (file)
 #include <config.h>
 #include <lyxrc.h>
 
-#ifdef __GNUG__
-#pragma implementation
-#endif
 
 #include "support/lstrings.h"
 #include "support/LAssert.h"
 #include "debug.h"
-#include "LColor.h"
 #include "frontends/Painter.h"
 #include "math_cursor.h"
 #include "formulabase.h"
+#include "funcrequest.h"
 #include "math_autocorrect.h"
-#include "math_arrayinset.h"
 #include "math_braceinset.h"
-#include "math_boxinset.h"
-#include "math_casesinset.h"
+#include "math_commentinset.h"
 #include "math_charinset.h"
-#include "math_deliminset.h"
+#include "math_extern.h"
 #include "math_factory.h"
-#include "math_hullinset.h"
+#include "math_fontinset.h"
+#include "math_gridinset.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_support.h"
 #include "math_unknowninset.h"
 
 #include <algorithm>
 #include <cctype>
 
-#define FILEDEBUG 0
+//#define FILEDEBUG 1
 
 using std::endl;
 using std::min;
@@ -64,131 +57,23 @@ using std::ostringstream;
 using std::isalpha;
 
 
-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)
-       {
-               MathCursorPos i1;
-               MathCursorPos i2;
-               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);
-                               }
-               }
-       }
-
-       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 {
-                       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
-       {
-               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_.nargs(); ++i)
-                       ar.push_back(data_.cell(i));
-               return ar;
-       }
-
-       void clear()
-       {
-               data_ = MathGridInset(1, 1);
-       }
-
-       MathGridInset data_;
-};
-
-
-Selection theSelection;
-
+// matheds own cut buffer
+string theCutBuffer;
 
 
+MathCursor::MathCursor(InsetFormulaBase * formula, bool front)
+       :       formula_(formula), autocorrect_(false), selection_(false), targetx_(-1)
+{
+       front ? first() : last();
+       Anchor_ = Cursor_;
 }
 
 
-MathCursor::MathCursor(InsetFormulaBase * formula, bool front)
-       :       formula_(formula), autocorrect_(false), selection_(false)
+MathCursor::~MathCursor()
 {
-       front ? first() : last();
+       // ensure that 'notifyCursorLeave' is called
+       while (popLeft())
+               ;
 }
 
 
@@ -200,7 +85,7 @@ void MathCursor::push(MathAtom & t)
 
 void MathCursor::pushLeft(MathAtom & t)
 {
-       //cerr << "Entering atom "; t->write(cerr, false); cerr << " left\n";
+       //lyxerr << "Entering atom " << t << " left\n";
        push(t);
        t->idxFirst(idx(), pos());
 }
@@ -208,7 +93,7 @@ void MathCursor::pushLeft(MathAtom & t)
 
 void MathCursor::pushRight(MathAtom & t)
 {
-       //cerr << "Entering atom "; t->write(cerr, false); cerr << " right\n";
+       //lyxerr << "Entering atom " << t << " right\n";
        posLeft();
        push(t);
        t->idxLast(idx(), pos());
@@ -218,8 +103,12 @@ void MathCursor::pushRight(MathAtom & t)
 bool MathCursor::popLeft()
 {
        //cerr << "Leaving atom to the left\n";
-       if (Cursor_.size() <= 1)
+       if (depth() <= 1) {
+               if (depth() == 1)
+                       par()->notifyCursorLeaves(idx());
                return false;
+       }
+       par()->notifyCursorLeaves(idx());
        Cursor_.pop_back();
        return true;
 }
@@ -228,8 +117,12 @@ bool MathCursor::popLeft()
 bool MathCursor::popRight()
 {
        //cerr << "Leaving atom "; par()->write(cerr, false); cerr << " right\n";
-       if (Cursor_.size() <= 1)
+       if (depth() <= 1) {
+               if (depth() == 1)
+                       par()->notifyCursorLeaves(idx());
                return false;
+       }
+       par()->notifyCursorLeaves(idx());
        Cursor_.pop_back();
        posRight();
        return true;
@@ -240,14 +133,14 @@ bool MathCursor::popRight()
 #if FILEDEBUG
        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";
+               lyxerr << "MC: " << what << endl;
+               lyxerr << " Cursor: " << depth() << endl;
+               for (unsigned i = 0; i < depth(); ++i)
+                       lyxerr << "    i: " << i << ' ' << Cursor_[i] << endl;
+               lyxerr << " Anchor: " << Anchor_.size() << endl;
                for (unsigned i = 0; i < Anchor_.size(); ++i)
-                       lyxerr << "    i: " << i << " " << Anchor_[i] << "\n";
-               lyxerr  << " sel: " << selection_ << "\n";
+                       lyxerr << "    i: " << i << ' ' << Anchor_[i] << endl;
+               lyxerr  << " sel: " << selection_ << endl;
        }
 #else
        void MathCursor::dump(char const *) const {}
@@ -256,7 +149,7 @@ bool MathCursor::popRight()
 
 bool MathCursor::isInside(MathInset const * p) const
 {
-       for (unsigned i = 0; i < Cursor_.size(); ++i)
+       for (unsigned i = 0; i < depth(); ++i)
                if (Cursor_[i].par_ == p)
                        return true;
        return false;
@@ -271,20 +164,23 @@ bool MathCursor::openable(MathAtom const & t, bool sel) const
        if (t->lock())
                return false;
 
-       if (t->asScriptInset())
-               return false;
-
        if (sel) {
                // we can't move into anything new during selection
-               if (Cursor_.size() == Anchor_.size())
+               if (depth() == Anchor_.size())
                        return false;
-               if (t.nucleus() != Anchor_[Cursor_.size()].par_)
+               if (t.operator->() != Anchor_[depth()].par_)
                        return false;
        }
        return true;
 }
 
 
+bool MathCursor::inNucleus() const
+{
+       return par()->asScriptInset() && idx() == 2;
+}
+
+
 bool MathCursor::posLeft()
 {
        if (pos() == 0)
@@ -359,15 +255,15 @@ void MathCursor::last()
 }
 
 
-bool positionable(MathCursor::cursor_type const & cursor,
-                 MathCursor::cursor_type const & anchor)
+bool positionable
+       (MathIterator const & cursor, MathIterator 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 (MathCursor::cursor_type::size_type i = 0; i < cursor.size(); ++i)
+       for (MathIterator::size_type i = 0; i < cursor.size(); ++i)
                if (cursor[i].par_ != anchor[i].par_)
                        return false;
 
@@ -383,36 +279,41 @@ void MathCursor::setPos(int x, int y)
                formula()->xlow(), formula()->xhigh(),
                formula()->ylow(), formula()->yhigh());
        if (!res) {
-               // this ccan happen on creation of "math-display"
+               // this can happen on creation of "math-display"
                dump("setPos 1.5");
                first();
        }
+       targetx_ = -1; // "no target"
        dump("setPos 2");
 }
 
 
 
-void MathCursor::home(bool sel)
+bool MathCursor::home(bool sel)
 {
        dump("home 1");
        autocorrect_ = false;
        selHandle(sel);
        macroModeClose();
        if (!par()->idxHome(idx(), pos()))
-               popLeft();
+               return popLeft();
        dump("home 2");
+       targetx_ = -1; // "no target"
+       return true;
 }
 
 
-void MathCursor::end(bool sel)
+bool MathCursor::end(bool sel)
 {
        dump("end 1");
        autocorrect_ = false;
        selHandle(sel);
        macroModeClose();
        if (!par()->idxEnd(idx(), pos()))
-               popRight();
+               return popRight();
        dump("end 2");
+       targetx_ = -1; // "no target"
+       return true;
 }
 
 
@@ -438,15 +339,25 @@ void MathCursor::markErase()
 
 void MathCursor::plainInsert(MathAtom const & t)
 {
+       dump("plainInsert");
        array().insert(pos(), t);
        ++pos();
 }
 
 
+void MathCursor::insert(string const & str)
+{
+       //lyxerr << "inserting '" << str << "'\n";
+       selClearOrDel();
+       for (string::const_iterator it = str.begin(); it != str.end(); ++it)
+               plainInsert(MathAtom(new MathCharInset(*it)));
+}
+
+
 void MathCursor::insert(char c)
 {
        //lyxerr << "inserting '" << c << "'\n";
-       selClearOrDel();        
+       selClearOrDel();
        plainInsert(MathAtom(new MathCharInset(c)));
 }
 
@@ -454,28 +365,31 @@ void MathCursor::insert(char c)
 void MathCursor::insert(MathAtom const & t)
 {
        macroModeClose();
+       selClearOrDel();
+       plainInsert(t);
+}
 
-       if (selection_) {
-               if (t->nargs())
-                       selCut();
-               else
-                       selClearOrDel();
-       }
 
-       plainInsert(t);
+void MathCursor::niceInsert(string const & t)
+{
+       MathArray ar = asArray(t);
+       if (ar.size() == 1)
+               niceInsert(ar[0]);
+       else
+               insert(ar);
 }
 
 
 void MathCursor::niceInsert(MathAtom const & t)
 {
-       selCut();
-       insert(t); // inserting invalidates the pointer!
-       MathAtom & p = prevAtom();
+       macroModeClose();
+       string safe = grabAndEraseSelection();
+       plainInsert(t);
        // enter the new inset and move the contents of the selection if possible
-       if (p->isActive()) {
-               push(p);
-               par()->idxLast(idx(), pos());
-               selPaste();
+       if (t->isActive()) {
+               posLeft();
+               pushLeft(nextAtom());
+               paste(safe);
        }
 }
 
@@ -484,103 +398,79 @@ void MathCursor::insert(MathArray const & ar)
 {
        macroModeClose();
        if (selection_)
-               selCut();
-
+               eraseSelection();
        array().insert(pos(), ar);
        pos() += ar.size();
 }
 
 
-void MathCursor::paste(MathArray const & ar)
+void MathCursor::paste(string const & data)
 {
-       Anchor_ = Cursor_;
-       selection_ = true;
-       array().insert(pos(), ar);
-       pos() += ar.size();
+       dispatch(FuncRequest(LFUN_PASTE, data));
 }
 
 
-void MathCursor::backspace()
+bool MathCursor::backspace()
 {
        autocorrect_ = false;
-       if (pos() == 0) {
-               pullArg(false);
-               return;
-       }
 
        if (selection_) {
                selDel();
-               return;
+               return true;
+       }
+
+       if (pos() == 0) {
+               if (par()->ncols() == 1 &&
+                         par()->nrows() == 1 &&
+                         depth() == 1 &&
+                         size() == 0)
+                       return false;
+               pullArg();
+               return true;
        }
 
-       MathScriptInset * p = prevAtom()->asScriptInset();
-       if (p) {
-               p->removeScript(p->hasUp());
-               // Don't delete if there is anything left
-               if (p->hasUp() || p->hasDown())
-                       return;
+       if (inMacroMode()) {
+               MathUnknownInset * p = activeMacro();
+               if (p->name().size() > 1) {
+                       p->setName(p->name().substr(0, p->name().size() - 1));
+                       return true;
+               }
        }
 
        --pos();
        plainErase();
+       return true;
 }
 
 
-void MathCursor::erase()
+bool MathCursor::erase()
 {
        autocorrect_ = false;
        if (inMacroMode())
-               return;
+               return true;
 
        if (selection_) {
                selDel();
-               return;
+               return true;
        }
 
        // delete empty cells if possible
        if (array().empty())
                if (par()->idxDelete(idx()))
-                       return;
+                       return true;
 
        // 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
-               if (p->hasUp() || p->hasDown())
-                       return;
+               if (par()->ncols() == 1 && par()->nrows() == 1 && depth() == 1 && size() == 0)
+                       return false;
+               else{
+                       par()->idxGlue(idx());
+                       return true;
+               }
        }
 
        plainErase();
-}
-
-
-void MathCursor::delLine()
-{
-       autocorrect_ = false;
-       macroModeClose();
-
-       if (selection_) {
-               selDel();
-               return;
-       }
-
-       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();
+       return true;
 }
 
 
@@ -589,7 +479,7 @@ bool MathCursor::up(bool sel)
        dump("up 1");
        macroModeClose();
        selHandle(sel);
-       cursor_type save = Cursor_;
+       MathIterator save = Cursor_;
        if (goUpDown(true))
                return true;
        Cursor_ = save;
@@ -603,7 +493,7 @@ bool MathCursor::down(bool sel)
        dump("down 1");
        macroModeClose();
        selHandle(sel);
-       cursor_type save = Cursor_;
+       MathIterator save = Cursor_;
        if (goUpDown(false))
                return true;
        Cursor_ = save;
@@ -612,36 +502,41 @@ bool MathCursor::down(bool sel)
 }
 
 
-bool MathCursor::toggleLimits()
-{
-       if (!hasNextAtom())
-               return false;
-       MathScriptInset * t = nextAtom()->asScriptInset();
-       if (!t)
-               return false;
-       int old = t->limits();
-       t->limits(old < 0 ? 1 : -1);
-       return old != t->limits();
-}
-
-
 void MathCursor::macroModeClose()
 {
-       MathUnknownInset * p = inMacroMode();
-       if (!p)
+       if (!inMacroMode())
                return;
+       MathUnknownInset * p = activeMacro();
        p->finalize();
        string s = p->name();
        --pos();
        array().erase(pos());
-       if (s != "\\")
-               interpret(s);
+
+       // do nothing if the macro name is empty
+       if (s == "\\")
+               return;
+
+       string name = s.substr(1);
+
+       // prevent entering of recursive macros
+       if (formula()->lyxCode() == Inset::MATHMACRO_CODE
+                       && formula()->getInsetName() == name)
+               lyxerr << "can't enter recursive macro\n";
+
+       niceInsert(createMathInset(name));
 }
 
 
 string MathCursor::macroName() const
 {
-       return inMacroMode() ? inMacroMode()->name() : "";
+       return inMacroMode() ? activeMacro()->name() : string();
+}
+
+
+void MathCursor::selClear()
+{
+       Anchor_.clear();
+       selection_ = false;
 }
 
 
@@ -649,8 +544,10 @@ void MathCursor::selCopy()
 {
        dump("selCopy");
        if (selection_) {
-               theSelection.grab(*this);
-               //selClear();
+               theCutBuffer = grabSelection();
+               selection_ = false;
+       } else {
+               theCutBuffer.erase();
        }
 }
 
@@ -658,13 +555,7 @@ void MathCursor::selCopy()
 void MathCursor::selCut()
 {
        dump("selCut");
-       if (selection_) {
-               theSelection.grab(*this);
-               theSelection.erase(*this);
-               selClear();
-       } else {
-               theSelection.clear();
-       }
+       theCutBuffer = grabAndEraseSelection();
 }
 
 
@@ -672,10 +563,8 @@ void MathCursor::selDel()
 {
        dump("selDel");
        if (selection_) {
-               theSelection.erase(*this);
-               if (pos() > size())
-                       pos() = size();
-               selClear();
+               eraseSelection();
+               selection_ = false;
        }
 }
 
@@ -684,9 +573,9 @@ void MathCursor::selPaste()
 {
        dump("selPaste");
        selClearOrDel();
-       theSelection.paste(*this);
-       //theSelection.grab(*this);
-       selClear();
+       paste(theCutBuffer);
+       //grabSelection();
+       selection_ = false;
 }
 
 
@@ -694,8 +583,8 @@ void MathCursor::selHandle(bool sel)
 {
        if (sel == selection_)
                return;
-       //theSelection.clear();
-       Anchor_    = Cursor_;
+       //clear();
+       Anchor_ = Cursor_;
        selection_ = sel;
 }
 
@@ -703,112 +592,55 @@ void MathCursor::selHandle(bool sel)
 void MathCursor::selStart()
 {
        dump("selStart 1");
-       //theSelection.clear();
+       //clear();
        Anchor_ = Cursor_;
        selection_ = true;
        dump("selStart 2");
 }
 
 
-void MathCursor::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)
-{
-       dump("selGet");
-       if (!selection_)
-               return;
-
-       theSelection.grab(*this);
-       ar = theSelection.glue();
+               selection_ = false;
 }
 
 
-
-void MathCursor::drawSelection(MathPainterInfo & pain) const
+void MathCursor::drawSelection(MathPainterInfo & pi) const
 {
        if (!selection_)
                return;
-
        MathCursorPos i1;
        MathCursorPos i2;
        getSelection(i1, i2);
-
-       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.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
-       } else {
-               vector<MathInset::idx_type> 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.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
-               }
-       }
-
-#if 0
-       // draw anchor if different from selection boundary
-       MathCursorPos anc = Anchor_.back();
-       if (anc != i1 && anc != i2) {
-               MathXArray & c = anc.xcell();
-               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::math);
-       }
-#endif
+       i1.par_->drawSelection(pi, i1.idx_, i1.pos_, i2.idx_, i2.pos_);
 }
 
 
-void MathCursor::handleDelim(string const & l, string const & r)
+void MathCursor::handleNest(MathAtom const & a)
 {
-       handleNest(new MathDelimInset(l, r));
+       MathAtom at = a;
+       at.nucleus()->cell(0) = asArray(grabAndEraseSelection());
+       insert(at);
+       pushRight(prevAtom());
 }
 
 
-void MathCursor::handleNest(MathInset * p)
+void MathCursor::getPos(int & x, int & y) const
 {
-       if (selection_) {
-               selCut();
-               p->cell(0) = theSelection.glue();
-       }
-       insert(MathAtom(p)); // this invalidates p!
-       pushRight(prevAtom());
+       par()->getPos(idx(), pos(), x, y);
 }
 
 
-void MathCursor::getPos(int & x, int & y)
+int MathCursor::targetX() const
 {
-#ifdef WITH_WARNINGS
-#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();
+       if (targetx_ != -1)
+               return targetx_;
+       int x = 0, y = 0;
+       getPos(x, y);
+       return x;
 }
 
 
@@ -818,7 +650,7 @@ MathInset * MathCursor::par() const
 }
 
 
-InsetFormulaBase * MathCursor::formula()
+InsetFormulaBase * MathCursor::formula() const
 {
        return formula_;
 }
@@ -842,18 +674,42 @@ MathCursor::pos_type MathCursor::pos() const
 }
 
 
+void MathCursor::adjust(pos_type from, difference_type diff)
+{
+       if (cursor().pos_ > from)
+               cursor().pos_ += diff;
+       if (Anchor_.back().pos_ > from)
+               Anchor_.back().pos_ += diff;
+       // just to be on the safe side
+       // theoretically unecessary
+       normalize();
+}
+
+
 MathCursor::pos_type & MathCursor::pos()
 {
        return cursor().pos_;
 }
 
 
-MathUnknownInset * MathCursor::inMacroMode() const
+bool MathCursor::inMacroMode() const
 {
-       if (pos() == 0)
-               return 0;
-       MathUnknownInset * p = prevAtom()->asUnknownInset();
-       return (p && !p->final()) ? p : 0;
+       if (!hasPrevAtom())
+               return false;
+       MathUnknownInset const * p = prevAtom()->asUnknownInset();
+       return p && !p->final();
+}
+
+
+MathUnknownInset * MathCursor::activeMacro()
+{
+       return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
+}
+
+
+MathUnknownInset const * MathCursor::activeMacro() const
+{
+       return inMacroMode() ? prevAtom()->asUnknownInset() : 0;
 }
 
 
@@ -871,48 +727,46 @@ bool MathCursor::selection() const
 
 MathGridInset * MathCursor::enclosingGrid(MathCursor::idx_type & idx) const
 {
-       for (MathInset::difference_type i = Cursor_.size() - 1; i >= 0; --i) {
+       for (MathInset::difference_type i = depth() - 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::popToHere(MathInset const * p)
+{
+       while (depth() && Cursor_.back().par_ != p)
+               Cursor_.pop_back();
+}
+
+
 void MathCursor::popToEnclosingGrid()
 {
-       while (Cursor_.size() && !Cursor_.back().par_->asGridInset())
+       while (depth() && !Cursor_.back().par_->asGridInset())
                Cursor_.pop_back();
 }
 
 
-void MathCursor::pullArg(bool goright)
+void MathCursor::popToEnclosingHull()
 {
-       dump("pullarg");
-       MathArray a = array();
+       while (depth() && !Cursor_.back().par_->asHullInset())
+               Cursor_.pop_back();
+}
 
-       MathScriptInset const * p = par()->asScriptInset();
-       if (p) {
-               // special handling for scripts
-               const bool up = p->hasUp();
-               popLeft();
-               MathScriptInset * q = nextAtom()->asScriptInset();
-               if (q)
-                       q->removeScript(up);
-               ++pos();
-               array().insert(pos(), a);
-               return;
-       }
 
+void MathCursor::pullArg()
+{
+       dump("pullarg");
+       MathArray a = array();
        if (popLeft()) {
                plainErase();
                array().insert(pos(), a);
-               if (goright)
-                       pos() += a.size();
+               Anchor_ = Cursor_;
        } else {
                formula()->mutateToText();
        }
@@ -921,58 +775,33 @@ void MathCursor::pullArg(bool goright)
 
 void MathCursor::touch()
 {
-       cursor_type::const_iterator it = Cursor_.begin();
-       cursor_type::const_iterator et = Cursor_.end();
+       MathIterator::const_iterator it = Cursor_.begin();
+       MathIterator::const_iterator et = Cursor_.end();
        for ( ; it != et; ++it)
-               it->xcell().touch();
+               it->cell().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
-
        if (idx() >= par()->nargs()) {
                lyxerr << "this should not really happen - 1: "
-                      << idx() << " " << par()->nargs() << "\n";
+                      << idx() << ' ' << par()->nargs()
+                      << " in: " << par() << endl;
                dump("error 2");
        }
        idx() = min(idx(), par()->nargs() - 1);
 
        if (pos() > size()) {
                lyxerr << "this should not really happen - 2: "
-                       << pos() << " " << size() <<  " in idx: " << idx()
-                       << " in atom: '";
+                       << pos() << ' ' << size() <<  " in idx: " << idx()
+                      << " in atom: '";
                WriteStream wi(lyxerr, false, true);
                par()->write(wi);
-               lyxerr << "\n";
+               lyxerr << endl;
                dump("error 4");
        }
        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());
 }
 
 
@@ -982,18 +811,6 @@ MathCursor::size_type MathCursor::size() const
 }
 
 
-MathCursor::col_type MathCursor::hullCol() const
-{
-       return Cursor_[0].par_->asGridInset()->col(Cursor_[0].idx_);
-}
-
-
-MathCursor::row_type MathCursor::hullRow() const
-{
-       return Cursor_[0].par_->asGridInset()->row(Cursor_[0].idx_);
-}
-
-
 bool MathCursor::hasPrevAtom() const
 {
        return pos() > 0;
@@ -1009,28 +826,28 @@ bool MathCursor::hasNextAtom() const
 MathAtom const & MathCursor::prevAtom() const
 {
        lyx::Assert(pos() > 0);
-       return array().at(pos() - 1);
+       return array()[pos() - 1];
 }
 
 
 MathAtom & MathCursor::prevAtom()
 {
        lyx::Assert(pos() > 0);
-       return array().at(pos() - 1);
+       return array()[pos() - 1];
 }
 
 
 MathAtom const & MathCursor::nextAtom() const
 {
        lyx::Assert(pos() < size());
-       return array().at(pos());
+       return array()[pos()];
 }
 
 
 MathAtom & MathCursor::nextAtom()
 {
        lyx::Assert(pos() < size());
-       return array().at(pos());
+       return array()[pos()];
 }
 
 
@@ -1043,8 +860,8 @@ MathArray & MathCursor::array() const
                return dummy;
        }
 
-       if (Cursor_.size() == 0) {
-               lyxerr << "############  Cursor_.size() == 0 not valid\n";
+       if (depth() == 0) {
+               lyxerr << "############  depth() == 0 not valid\n";
                return dummy;
        }
 
@@ -1052,19 +869,6 @@ MathArray & MathCursor::array() const
 }
 
 
-MathXArray & MathCursor::xarray() const
-{
-       static MathXArray dummy;
-
-       if (Cursor_.size() == 0) {
-               lyxerr << "############  Cursor_.size() == 0 not valid\n";
-               return dummy;
-       }
-
-       return cursor().xcell();
-}
-
-
 void MathCursor::idxNext()
 {
        par()->idxNext(idx(), pos());
@@ -1077,56 +881,6 @@ void MathCursor::idxPrev()
 }
 
 
-void MathCursor::splitCell()
-{
-       if (idx() + 1 == par()->nargs())
-               return;
-       MathArray ar = array();
-       ar.erase(0, pos());
-       array().erase(pos(), size());
-       ++idx();
-       pos() = 0;
-       array().insert(0, ar);
-}
-
-
-void MathCursor::breakLine()
-{
-       // leave inner cells
-       while (popRight())
-               ;
-
-       MathHullInset * p = formula()->par()->asHullInset();
-       if (!p)
-               return;
-
-       if (p->getType() == LM_OT_SIMPLE || p->getType() == LM_OT_EQUATION) {
-               p->mutate(LM_OT_EQNARRAY);
-               idx() = 0;
-               pos() = size();
-       } else {
-               p->addRow(hullRow());
-
-               // split line
-               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
-               splitCell();
-               p->cell(idx()).swap(p->cell(idx() + p->ncols() - 1));
-       }
-}
-
-
-//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
 {
        idx_type idx;
@@ -1158,14 +912,14 @@ void MathCursor::getSelection(MathCursorPos & i1, MathCursorPos & i2) const
 
 MathCursorPos & MathCursor::cursor()
 {
-       lyx::Assert(Cursor_.size());
+       lyx::Assert(depth());
        return Cursor_.back();
 }
 
 
 MathCursorPos const & MathCursor::cursor() const
 {
-       lyx::Assert(Cursor_.size());
+       lyx::Assert(depth());
        return Cursor_.back();
 }
 
@@ -1175,21 +929,20 @@ bool MathCursor::goUpDown(bool up)
        // 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;
+  int xo = 0;
+       int yo = 0;
        getPos(xo, yo);
 
        // check if we had something else in mind, if not, this is the future goal
        if (targetx_ == -1)
                targetx_ = xo;
-       else    
+       else
                xo = targetx_;
 
        // try neigbouring script insets
        // try left
        if (hasPrevAtom()) {
-               MathScriptInset * p = prevAtom()->asScriptInset();
+               MathScriptInset const * p = prevAtom()->asScriptInset();
                if (p && p->has(up)) {
                        --pos();
                        push(nextAtom());
@@ -1202,7 +955,7 @@ bool MathCursor::goUpDown(bool up)
 
        // try right
        if (hasNextAtom()) {
-               MathScriptInset * p = nextAtom()->asScriptInset();
+               MathScriptInset const * p = nextAtom()->asScriptInset();
                if (p && p->has(up)) {
                        push(nextAtom());
                        idx() = up;
@@ -1227,26 +980,16 @@ bool MathCursor::goUpDown(bool up)
        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";
+               if (par()->idxUpDown(idx(), pos(), up, targetx_)) {
+                       // try to find best position within this inset
+                       if (!selection())
+                               bruteFind2(xo, yo);
                        return true;
                }
 
-               // leave inset
-               if (!popLeft()) {
-                       // no such inset found, just take something "above"
-                       ///lyxerr << "updown: handled by strange case\n";
+               // no such inset found, just take something "above"
+               ///lyxerr << "updown: handled by strange case\n";
+               if (!popLeft())
                        return
                                bruteFind(xo, yo,
                                        formula()->xlow(),
@@ -1254,7 +997,6 @@ bool MathCursor::goUpDown(bool up)
                                        up ? formula()->ylow() : yo + 4,
                                        up ? yo - 4 : formula()->yhigh()
                                );
-               }
 
                // any improvement so far?
                int xnew, ynew;
@@ -1268,24 +1010,23 @@ bool MathCursor::goUpDown(bool up)
 bool MathCursor::bruteFind
        (int x, int y, int xlow, int xhigh, int ylow, int yhigh)
 {
-       cursor_type best_cursor;
+       MathIterator best_cursor;
        double best_dist = 1e10;
 
        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 (!selection_ || positionable(it, Anchor_)) {
+                       int xo, yo;
+                       it.back().getPos(xo, yo);
                        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();
+                                       best_cursor = it;
                                }
                        }
                }
@@ -1301,86 +1042,48 @@ bool MathCursor::bruteFind
 }
 
 
-bool MathCursor::idxLeft()
+void MathCursor::bruteFind2(int x, int y)
 {
-       return par()->idxLeft(idx(), pos());
+       double best_dist = 1e10;
+
+       MathIterator it = Cursor_;
+       it.back().setPos(0);
+       MathIterator et = Cursor_;
+       et.back().setPos(it.cell().size());
+       while (1) {
+               int xo, yo;
+               it.back().getPos(xo, yo);
+               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;
+                       Cursor_   = it;
+               }
+               if (it == et)
+                       break;
+               ++it;
+       }
 }
 
 
-bool MathCursor::idxRight()
+bool MathCursor::idxLineLast()
 {
-       return par()->idxRight(idx(), pos());
+       idx() -= idx() % par()->ncols();
+       idx() += par()->ncols() - 1;
+       pos() = size();
+       return true;
 }
 
-
-bool MathCursor::interpret(string const & s)
+bool MathCursor::idxLeft()
 {
-       //lyxerr << "interpret 1: '" << s << "'\n";
-       if (s.empty())
-               return true;
-
-       //lyxerr << "char: '" << s[0] << "'  int: " << int(s[0]) << endl;
-       //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(5).c_str());
-               is >> n;
-               n = max(1u, n);
-               niceInsert(MathAtom(new MathCasesInset(n)));
-               return true;
-       }
-
-       if (s.size() >= 6 && s.substr(0, 6) == "matrix") {
-               unsigned int m = 1;
-               unsigned int n = 1;
-               string v_align;
-               string h_align;
-               istringstream is(s.substr(6).c_str());
-               is >> m >> n >> v_align >> h_align;
-               m = max(1u, m);
-               n = max(1u, n);
-               v_align += 'c';
-               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;
-       }
-
-       string name = s.substr(1);
-
-       if (name == "over" || name == "choose" || name == "atop") {
-               MathArray ar = array();
-               MathAtom t(createMathInset(name));
-               t->asNestInset()->cell(0).swap(array());
-               pos() = 0;
-               niceInsert(t);
-               popRight();
-               left();
-               return true;
-       }
+       return par()->idxLeft(idx(), pos());
+}
 
-       // 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::idxRight()
+{
+       return par()->idxRight(idx(), pos());
 }
 
 
@@ -1389,43 +1092,43 @@ bool MathCursor::script(bool up)
        // Hack to get \\^ and \\_ working
        if (inMacroMode() && macroName() == "\\") {
                if (up)
-                       interpret("\\mathcircumflex");
+                       niceInsert(createMathInset("mathcircumflex"));
                else
                        interpret('_');
                return true;
        }
 
        macroModeClose();
-       selCut();
-       if (hasPrevAtom() && prevAtom()->asScriptInset()) {
-               prevAtom()->asScriptInset()->ensure(up);
+       string safe = grabAndEraseSelection();
+       if (inNucleus()) {
+               // we are in a nucleus of a script inset, move to _our_ script
+               par()->asScriptInset()->ensure(up);
+               idx() = up;
+               pos() = 0;
+       } else if (hasPrevAtom() && prevAtom()->asScriptInset()) {
+               prevAtom().nucleus()->asScriptInset()->ensure(up);
                pushRight(prevAtom());
                idx() = up;
                pos() = size();
-       } else if (hasNextAtom() && nextAtom()->asScriptInset()) {
-               nextAtom()->asScriptInset()->ensure(up);
+       } else if (hasPrevAtom()) {
+               --pos();
+               array()[pos()] = MathAtom(new MathScriptInset(nextAtom(), up));
                pushLeft(nextAtom());
                idx() = up;
                pos() = 0;
        } else {
                plainInsert(MathAtom(new MathScriptInset(up)));
-               prevAtom()->asScriptInset()->ensure(up);
+               prevAtom().nucleus()->asScriptInset()->ensure(up);
                pushRight(prevAtom());
                idx() = up;
                pos() = 0;
        }
-       selPaste();
+       paste(safe);
        dump("1");
        return true;
 }
 
 
-bool MathCursor::inMathMode() const
-{
-       return !par()->asBoxInset();
-}
-
-
 bool MathCursor::interpret(char c)
 {
        //lyxerr << "interpret 2: '" << c << "'\n";
@@ -1434,11 +1137,11 @@ bool MathCursor::interpret(char c)
                --pos();
                plainErase();
                int n = c - '0';
-               MathMacroTemplate * p = formula()->par()->asMacroTemplate();
+               MathMacroTemplate const * p = formula()->par()->asMacroTemplate();
                if (p && 1 <= n && n <= p->numargs())
                        insert(MathAtom(new MathMacroArgument(c - '0')));
                else {
-                       insert(MathAtom(new MathSpecialCharInset('#')));
+                       insert(createMathInset("#"));
                        interpret(c); // try again
                }
                return true;
@@ -1449,14 +1152,8 @@ bool MathCursor::interpret(char c)
                string name = macroName();
                //lyxerr << "interpret name: '" << name << "'\n";
 
-               if (name.empty() && c == '\\') {
-                       backspace();
-                       interpret("\\backslash");
-                       return true;
-               }
-
                if (isalpha(c)) {
-                       inMacroMode()->name() += c;
+                       activeMacro()->setName(activeMacro()->name() + c);
                        return true;
                }
 
@@ -1464,52 +1161,71 @@ bool MathCursor::interpret(char c)
                if (name == "\\") {
                        // remove the '\\'
                        backspace();
-                       if (c == '\\')
-                               interpret("\\backslash");
-                       else
-                               interpret(string("\\") + c);
+                       if (c == '\\') {
+                               if (currentMode() == MathInset::TEXT_MODE)
+                                       niceInsert(createMathInset("textbackslash"));
+                               else
+                                       niceInsert(createMathInset("backslash"));
+                       } else if (c == '{') {
+                               niceInsert(MathAtom(new MathBraceInset));
+                       } else {
+                               niceInsert(createMathInset(string(1, c)));
+                       }
                        return true;
                }
 
-               // leave macro mode and try again
+               // leave macro mode and try again if necessary
                macroModeClose();
-               interpret(c);
+               if (c == '{')
+                       niceInsert(MathAtom(new MathBraceInset));
+               else if (c != ' ')
+                       interpret(c);
                return true;
        }
 
-       // leave autocorrect mode if necessary
-       if (autocorrect_ && c == ' ') {
-               autocorrect_ = false;
-               return true;
+       // This is annoying as one has to press <space> far too often.
+       // Disable it.
+
+       if (0) {
+               // leave autocorrect mode if necessary
+               if (autocorrect_ && c == ' ') {
+                       autocorrect_ = false;
+                       return true;
+               }
        }
 
-       // just clear selection on pressing the space par
+       // just clear selection on pressing the space bar
        if (selection_ && c == ' ') {
-               selClear();
+               selection_ = false;
                return true;
        }
 
        selClearOrDel();
 
-       if (!inMathMode()) {
-               // suppress direct insertion of two spaces in a row
-               // the still allows typing  '<space>a<space>' and deleting the 'a', but
-               // it is better than nothing...
-               if (c == ' ' && hasPrevAtom() && prevAtom()->getChar() == ' ')
-                       return true;
-               insert(c);
+       if (c == '\\') {
+               //lyxerr << "starting with macro\n";
+               insert(MathAtom(new MathUnknownInset("\\", false)));
                return true;
        }
 
-       if (c == '\\') {
-               lyxerr << "starting with macro\n";
-               insert(MathAtom(new MathUnknownInset("\\", false)));
+       if (c == '\n') {
+               if (currentMode() == MathInset::TEXT_MODE)
+                       insert(c);
                return true;
        }
 
        if (c == ' ') {
+               if (currentMode() == MathInset::TEXT_MODE) {
+                       // insert spaces in text mode,
+                       // but suppress direct insertion of two spaces in a row
+                       // the still allows typing  '<space>a<space>' and deleting the 'a', but
+                       // it is better than nothing...
+                       if (!hasPrevAtom() || prevAtom()->getChar() != ' ')
+                               insert(c);
+                       return true;
+               }
                if (hasPrevAtom() && prevAtom()->asSpaceInset()) {
-                       prevAtom()->asSpaceInset()->incSpace();
+                       prevAtom().nucleus()->asSpaceInset()->incSpace();
                        return true;
                }
                if (popRight())
@@ -1519,73 +1235,174 @@ bool MathCursor::interpret(char c)
        }
 
        if (c == '#') {
-               insert(c); // LM_TC_TEX;
+               insert(c);
                return true;
        }
 
-/*
-       if (c == '{' || c == '}', c)) {
-               insert(c); // LM_TC_TEX;
+       if (c == '{' || c == '}') {
+               niceInsert(createMathInset(string(1, c)));
                return true;
        }
-*/
 
-       if (c == '{') {
-               niceInsert(MathAtom(new MathBraceInset));
+       if (c == '$') {
+               insert(createMathInset("$"));
                return true;
        }
 
-       if (c == '}') {
+       if (c == '%') {
+               niceInsert(MathAtom(new MathCommentInset));
                return true;
        }
 
-       if (c == '$' || c == '%') {
-               insert(MathAtom(new MathSpecialCharInset(c)));
-               return true;
-       }
+       // try auto-correction
+       //if (autocorrect_ && hasPrevAtom() && math_autocorrect(prevAtom(), c))
+       //      return true;
 
-/*
-       if (isalpha(c) && lastcode_ == LM_TC_GREEK) {
-               insert(c, LM_TC_VAR);
-               return true;
+       // no special circumstances, so insert the character without any fuss
+       insert(c);
+       autocorrect_ = true;
+       return true;
+}
+
+
+void MathCursor::setSelection(MathIterator const & where, size_type n)
+{
+       selection_ = true;
+       Anchor_ = where;
+       Cursor_ = where;
+       cursor().pos_ += n;
+}
+
+
+void MathCursor::insetToggle()
+{
+       if (hasNextAtom()) {
+               // toggle previous inset ...
+               nextAtom().nucleus()->lock(!nextAtom()->lock());
+       } else if (popLeft() && hasNextAtom()) {
+               // ... or enclosing inset if we are in the last inset position
+               nextAtom().nucleus()->lock(!nextAtom()->lock());
+               posRight();
        }
+}
 
-       if (isalpha(c) && lastcode_ == LM_TC_GREEK1) {
-               insert(c, LM_TC_VAR);
-               lastcode_ = LM_TC_VAR;
-               return true;
+
+string MathCursor::info() const
+{
+       ostringstream os;
+       os << "Math editor mode.  ";
+       for (int i = 0, n = depth(); i < n; ++i) {
+               Cursor_[i].par_->infoize(os);
+               os << "  ";
        }
+       if (hasPrevAtom())
+               prevAtom()->infoize2(os);
+       os << "                    ";
+       return STRCONV(os.str());
+}
 
-       if (c == '\\') {
-               insert(c, LM_TC_TEX);
-               //bv->owner()->message(_("TeX mode"));
-               return true;
+
+unsigned MathCursor::depth() const
+{
+       return Cursor_.size();
+}
+
+
+
+
+namespace {
+
+void region(MathCursorPos const & i1, MathCursorPos const & i2,
+       MathInset::row_type & r1, MathInset::row_type & r2,
+       MathInset::col_type & c1, MathInset::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);
+}
+
+}
+
+
+string MathCursor::grabSelection() const
+{
+       if (!selection_)
+               return string();
+
+       MathCursorPos i1;
+       MathCursorPos i2;
+       getSelection(i1, i2);
+
+       if (i1.idx_ == i2.idx_) {
+               MathArray::const_iterator it = i1.cell().begin();
+               return asString(MathArray(it + i1.pos_, it + i2.pos_));
+       }
+
+       row_type r1, r2;
+       col_type c1, c2;
+       region(i1, i2, r1, r2, c1, c2);
+
+       string data;
+       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.par_->cell(i1.par_->index(row, col)));
+               }
        }
-*/
+       return data;
+}
 
-       // try auto-correction
-       if (autocorrect_ && hasPrevAtom() && math_autocorrect(prevAtom(), c))
-               return true;
 
-       // no special circumstances, so insert the character without any fuss
-       insert(c);
-       autocorrect_ = true;  
-       return true;
+void MathCursor::eraseSelection()
+{
+       MathCursorPos i1;
+       MathCursorPos i2;
+       getSelection(i1, i2);
+       if (i1.idx_ == i2.idx_)
+               i1.cell().erase(i1.pos_, i2.pos_);
+       else {
+               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)).clear();
+       }
+       cursor() = i1;
 }
 
 
+string MathCursor::grabAndEraseSelection()
+{
+       if (!selection_)
+               return string();
+       string res = grabSelection();
+       eraseSelection();
+       selection_ = false;
+       return res;
+}
+
 
 MathCursorPos MathCursor::normalAnchor() const
 {
-       if (Anchor_.size() < Cursor_.size()) {
+       if (Anchor_.size() < depth()) {
                Anchor_ = Cursor_;
                lyxerr << "unusual Anchor size\n";
-               dump("1");
        }
-       //lyx::Assert(Anchor_.size() >= Cursor_.size());
+       //lyx::Assert(Anchor_.size() >= cursor.depth());
        // use Anchor on the same level as Cursor
-       MathCursorPos normal = Anchor_[Cursor_.size() - 1];
-       if (Cursor_.size() < Anchor_.size() && !(normal < cursor())) {
+       MathCursorPos normal = Anchor_[depth() - 1];
+       if (depth() < Anchor_.size() && !(normal < cursor())) {
                // anchor is behind cursor -> move anchor behind the inset
                ++normal.pos_;
        }
@@ -1593,47 +1410,101 @@ MathCursorPos MathCursor::normalAnchor() const
 }
 
 
-void MathCursor::stripFromLastEqualSign()
-{
-       // find position of last '=' in the array
-       MathArray & ar = cursor().cell();
-       MathArray::const_iterator et = ar.end();
-       for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
-               if ((*it)->getChar() == '=')
-                       et = it;
+dispatch_result MathCursor::dispatch(FuncRequest const & cmd)
+{
+       // mouse clicks are somewhat special 
+       // check
+       switch (cmd.action) {
+               case LFUN_MOUSE_PRESS:
+               case LFUN_MOUSE_MOTION:
+               case LFUN_MOUSE_RELEASE:
+               case LFUN_MOUSE_DOUBLE: {
+                       MathCursorPos & pos = Cursor_.back();
+                       dispatch_result res = UNDISPATCHED;
+                       int x = 0, y = 0;
+                       getPos(x, y);
+                       if (x < cmd.x && hasPrevAtom()) {
+                               res = prevAtom().nucleus()->dispatch(cmd, pos.idx_, pos.pos_);
+                               if (res != UNDISPATCHED)
+                                       return res;
+                       }
+                       if (x > cmd.x && hasNextAtom()) {
+                               res = nextAtom().nucleus()->dispatch(cmd, pos.idx_, pos.pos_);
+                               if (res != UNDISPATCHED)
+                                       return res;
+                       }
+               }
+               default:
+                       break;
+       }
 
-       // delete everything behind this position
-       ar.erase(et - ar.begin(), ar.size());
-       pos() = ar.size();
+       for (int i = Cursor_.size() - 1; i >= 0; --i) {
+               MathCursorPos & pos = Cursor_[i];
+               dispatch_result res = pos.par_->dispatch(cmd, pos.idx_, pos.pos_);
+               if (res != UNDISPATCHED) {
+                       if (res == DISPATCHED_POP) {
+                               Cursor_.shrink(i + 1);
+                               selClear();
+                       }
+                       return res;
+               }
+       }
+       return UNDISPATCHED;
 }
 
 
-void MathCursor::setSelection(cursor_type const & where, size_type n)
+MathInset::mode_type MathCursor::currentMode() const
 {
-       selection_ = true;
-       Anchor_ = where;
-       Cursor_ = where;
-       cursor().pos_ += n;
+       for (int i = Cursor_.size() - 1; i >= 0; --i) {
+               MathInset::mode_type res = Cursor_[i].par_->currentMode();
+               if (res != MathInset::UNDECIDED_MODE)
+                       return res;
+       }
+       return MathInset::UNDECIDED_MODE;
 }
 
 
-void MathCursor::insetToggle()
+void MathCursor::handleFont(string const & font)
 {
-       if (hasNextAtom())
-               nextAtom()->lock(!nextAtom()->lock());
+       string safe;
+       if (selection()) {
+               macroModeClose();
+               safe = grabAndEraseSelection();
+       }
+
+       if (array().size()) {
+               // something left in the cell
+               if (pos() == 0) {
+                       // cursor in first position
+                       popLeft();
+               } else if (pos() == array().size()) {
+                       // cursor in last position
+                       popRight();
+               } else {
+                       // cursor in between. split cell
+                       MathArray::iterator bt = array().begin();
+                       MathAtom at = createMathInset(font);
+                       at.nucleus()->cell(0) = MathArray(bt, bt + pos());
+                       cursor().cell().erase(bt, bt + pos());
+                       popLeft();
+                       plainInsert(at);
+               }
+       } else {
+               // nothing left in the cell
+               pullArg();
+               plainErase();
+       }
+       insert(safe);
 }
 
 
-string MathCursor::info() const
+void releaseMathCursor(BufferView * bv)
 {
-       ostringstream os;
-       os << "Math editor mode ";
-       for (int i = 0, n = Cursor_.size(); i < n; ++i) {
-               Cursor_[i].par_->infoize(os);
-               os << "  ";
+       if (mathcursor) {
+               InsetFormulaBase * f =  mathcursor->formula();
+               f->hideInsetCursor(bv);
+               delete mathcursor;
+               mathcursor = 0;
+               f->insetUnlock(bv);
        }
-       //if (pos() > 0) 
-       //      prevAtom()->infoize(os);
-       os << "                ";
-       return os.str().c_str(); // .c_str() needed for lyxstring
 }