]> git.lyx.org Git - lyx.git/blobdiff - src/Cursor.cpp
refactoring visual cursor movement functions
[lyx.git] / src / Cursor.cpp
index b3bdadeb6cf3cbe65ba4500a0e550481dfa16253..b385f025a128175f7cd2942231ec629d29b9ad60 100644 (file)
@@ -5,6 +5,7 @@
  *
  * \author Alejandro Aguilar Sierra
  * \author Alfredo Braunstein
+ * \author Dov Feldstern
  * \author André Pönitz
  * \author Stefan Schimanski
  *
 #include <config.h>
 
 #include "Bidi.h"
-#include "BufferView.h"
-#include "bufferview_funcs.h"
 #include "Buffer.h"
-#include "Cursor.h"
+#include "BufferView.h"
 #include "CoordCache.h"
+#include "Cursor.h"
 #include "CutAndPaste.h"
-#include "debug.h"
 #include "DispatchResult.h"
 #include "Encoding.h"
+#include "Font.h"
+#include "FuncCode.h"
 #include "FuncRequest.h"
 #include "Language.h"
-#include "lfuns.h"
-#include "Font.h"
 #include "LyXFunc.h" // only for setMessage()
 #include "LyXRC.h"
-#include "Row.h"
-#include "Text.h"
-#include "Paragraph.h"
 #include "paragraph_funcs.h"
+#include "Paragraph.h"
 #include "ParIterator.h"
+#include "Row.h"
+#include "Text.h"
+#include "TextMetrics.h"
+#include "TocBackend.h"
+
+#include "support/lassert.h"
+#include "support/debug.h"
+#include "support/docstream.h"
 
 #include "insets/InsetTabular.h"
 #include "insets/InsetText.h"
 
-#include "mathed/MathData.h"
 #include "mathed/InsetMath.h"
+#include "mathed/InsetMathBrace.h"
 #include "mathed/InsetMathScript.h"
 #include "mathed/MacroTable.h"
+#include "mathed/MathData.h"
+#include "mathed/MathMacro.h"
 
-#include "support/limited_stack.h"
-
-#include <boost/assert.hpp>
 #include <boost/bind.hpp>
-#include <boost/current_function.hpp>
 
 #include <sstream>
 #include <limits>
 #include <map>
 
-namespace lyx {
+using namespace std;
 
-using std::string;
-using std::vector;
-using std::endl;
-using std::min;
-using std::for_each;
+namespace lyx {
 
 namespace {
 
-       bool
-       positionable(DocIterator const & cursor, DocIterator const & anchor)
-       {
-               // avoid deeper nested insets when selecting
-               if (cursor.depth() > anchor.depth())
+bool positionable(DocIterator const & cursor, DocIterator const & anchor)
+{
+       // avoid deeper nested insets when selecting
+       if (cursor.depth() > anchor.depth())
+               return false;
+
+       // anchor might be deeper, should have same path then
+       for (size_t i = 0; i < cursor.depth(); ++i)
+               if (&cursor[i].inset() != &anchor[i].inset())
                        return false;
 
-               // anchor might be deeper, should have same path then
-               for (size_t i = 0; i < cursor.depth(); ++i)
-                       if (&cursor[i].inset() != &anchor[i].inset())
-                               return false;
+       // position should be ok.
+       return true;
+}
 
-               // position should be ok.
-               return true;
-       }
 
+// Find position closest to (x, y) in cell given by iter.
+// Used only in mathed
+DocIterator bruteFind2(Cursor const & c, int x, int y)
+{
+       double best_dist = numeric_limits<double>::max();
 
-       // Find position closest to (x, y) in cell given by iter.
-       // Used only in mathed
-       DocIterator bruteFind2(Cursor const & c, int x, int y)
-       {
-               double best_dist = std::numeric_limits<double>::max();
-
-               DocIterator result;
-
-               DocIterator it = c;
-               it.top().pos() = 0;
-               DocIterator et = c;
-               et.top().pos() = et.top().asInsetMath()->cell(et.top().idx()).size();
-               for (size_t i = 0;; ++i) {
-                       int xo;
-                       int yo;
-                       Inset const * inset = &it.inset();
-                       std::map<Inset const *, Point> const & data =
-                               c.bv().coordCache().getInsets().getData();
-                       std::map<Inset const *, Point>::const_iterator I = data.find(inset);
-
-                       // FIXME: in the case where the inset is not in the cache, this
-                       // means that no part of it is visible on screen. In this case
-                       // we don't do elaborate search and we just return the forwarded
-                       // DocIterator at its beginning.
-                       if (I == data.end()) {
-                               it.top().pos() = 0;
-                               return it;
-                       }
+       DocIterator result;
 
-                       Point o = I->second;
-                       inset->cursorPos(c.bv(), it.top(), c.boundary(), xo, yo);
-                       // Convert to absolute
-                       xo += o.x_;
-                       yo += o.y_;
-                       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'
-                       LYXERR(Debug::DEBUG) << "i: " << i << " d: " << d
-                               << " best: " << best_dist << endl;
-                       if (d <= best_dist) {
-                               best_dist = d;
-                               result = it;
-                       }
-                       if (it == et)
-                               break;
-                       it.forwardPos();
-               }
-               return result;
-       }
+       DocIterator it = c;
+       it.top().pos() = 0;
+       DocIterator et = c;
+       et.top().pos() = et.top().asInsetMath()->cell(et.top().idx()).size();
+       for (size_t i = 0;; ++i) {
+               int xo;
+               int yo;
+               Inset const * inset = &it.inset();
+               map<Inset const *, Geometry> const & data =
+                       c.bv().coordCache().getInsets().getData();
+               map<Inset const *, Geometry>::const_iterator I = data.find(inset);
 
+               // FIXME: in the case where the inset is not in the cache, this
+               // means that no part of it is visible on screen. In this case
+               // we don't do elaborate search and we just return the forwarded
+               // DocIterator at its beginning.
+               if (I == data.end()) {
+                       it.top().pos() = 0;
+                       return it;
+               }
 
-       /// moves position closest to (x, y) in given box
-       bool bruteFind(Cursor & cursor,
-               int x, int y, int xlow, int xhigh, int ylow, int yhigh)
-       {
-               BOOST_ASSERT(!cursor.empty());
-               Inset & inset = cursor[0].inset();
-               BufferView & bv = cursor.bv();
-
-               CoordCache::InnerParPosCache const & cache =
-                       bv.coordCache().getParPos().find(cursor.bottom().text())->second;
-               // Get an iterator on the first paragraph in the cache
-               DocIterator it(inset);
-               it.push_back(CursorSlice(inset));
-               it.pit() = cache.begin()->first;
-               // Get an iterator after the last paragraph in the cache
-               DocIterator et(inset);
-               et.push_back(CursorSlice(inset));
-               et.pit() = boost::prior(cache.end())->first;
-               if (et.pit() >= et.lastpit())
-                       et = doc_iterator_end(inset);
-               else
-                       ++et.pit();
-
-               double best_dist = std::numeric_limits<double>::max();;
-               DocIterator best_cursor = et;
-
-               for ( ; it != et; it.forwardPos(true)) {
-                       // avoid invalid nesting when selecting
-                       if (!cursor.selection() || positionable(it, cursor.anchor_)) {
-                               Point p = bv_funcs::getPos(bv, it, false);
-                               int xo = p.x_;
-                               int yo = p.y_;
-                               if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
-                                       double const dx = xo - x;
-                                       double const dy = yo - y;
-                                       double const d = dx * dx + dy * dy;
-                                       // '<=' 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) {
-                                               //      lyxerr << "*" << endl;
-                                               best_dist   = d;
-                                               best_cursor = it;
-                                       }
+               Point o = I->second.pos;
+               inset->cursorPos(c.bv(), it.top(), c.boundary(), xo, yo);
+               // Convert to absolute
+               xo += o.x_;
+               yo += o.y_;
+               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'
+               LYXERR(Debug::DEBUG, "i: " << i << " d: " << d
+                       << " best: " << best_dist);
+               if (d <= best_dist) {
+                       best_dist = d;
+                       result = it;
+               }
+               if (it == et)
+                       break;
+               it.forwardPos();
+       }
+       return result;
+}
+
+
+/*
+/// moves position closest to (x, y) in given box
+bool bruteFind(Cursor & cursor,
+       int x, int y, int xlow, int xhigh, int ylow, int yhigh)
+{
+       LASSERT(!cursor.empty(), return false);
+       Inset & inset = cursor[0].inset();
+       BufferView & bv = cursor.bv();
+
+       CoordCache::InnerParPosCache const & cache =
+               bv.coordCache().getParPos().find(cursor.bottom().text())->second;
+       // Get an iterator on the first paragraph in the cache
+       DocIterator it(inset);
+       it.push_back(CursorSlice(inset));
+       it.pit() = cache.begin()->first;
+       // Get an iterator after the last paragraph in the cache
+       DocIterator et(inset);
+       et.push_back(CursorSlice(inset));
+       et.pit() = boost::prior(cache.end())->first;
+       if (et.pit() >= et.lastpit())
+               et = doc_iterator_end(inset);
+       else
+               ++et.pit();
+
+       double best_dist = numeric_limits<double>::max();;
+       DocIterator best_cursor = et;
+
+       for ( ; it != et; it.forwardPos(true)) {
+               // avoid invalid nesting when selecting
+               if (!cursor.selection() || positionable(it, cursor.anchor_)) {
+                       Point p = bv.getPos(it, false);
+                       int xo = p.x_;
+                       int yo = p.y_;
+                       if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
+                               double const dx = xo - x;
+                               double const dy = yo - y;
+                               double const d = dx * dx + dy * dy;
+                               // '<=' 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) {
+                                       //      lyxerr << "*" << endl;
+                                       best_dist   = d;
+                                       best_cursor = it;
                                }
                        }
                }
+       }
 
-               if (best_cursor != et) {
-                       cursor.setCursor(best_cursor);
-                       return true;
-               }
-
-               return false;
+       if (best_cursor != et) {
+               cursor.setCursor(best_cursor);
+               return true;
        }
 
+       return false;
+}
+*/
 
-       /// moves position closest to (x, y) in given box
-       bool bruteFind3(Cursor & cur, int x, int y, bool up)
-       {
-               BufferView & bv = cur.bv();
-               int ylow  = up ? 0 : y + 1;
-               int yhigh = up ? y - 1 : bv.workHeight();
-               int xlow = 0;
-               int xhigh = bv.workWidth();
+
+/// moves position closest to (x, y) in given box
+bool bruteFind3(Cursor & cur, int x, int y, bool up)
+{
+       BufferView & bv = cur.bv();
+       int ylow  = up ? 0 : y + 1;
+       int yhigh = up ? y - 1 : bv.workHeight();
+       int xlow = 0;
+       int xhigh = bv.workWidth();
 
 // FIXME: bit more work needed to get 'from' and 'to' right.
-               pit_type from = cur.bottom().pit();
-               //pit_type to = cur.bottom().pit();
-               //lyxerr << "Pit start: " << from << endl;
-
-               //lyxerr << "bruteFind3: x: " << x << " y: " << y
-               //      << " xlow: " << xlow << " xhigh: " << xhigh
-               //      << " ylow: " << ylow << " yhigh: " << yhigh
-               //      << endl;
-               Inset & inset = bv.buffer()->inset();
-               DocIterator it = doc_iterator_begin(inset);
-               it.pit() = from;
-               DocIterator et = doc_iterator_end(inset);
-
-               double best_dist = std::numeric_limits<double>::max();
-               DocIterator best_cursor = et;
-
-               for ( ; it != et; it.forwardPos()) {
-                       // avoid invalid nesting when selecting
-                       if (bv_funcs::status(&bv, it) == bv_funcs::CUR_INSIDE
-                           && (!cur.selection() || positionable(it, cur.anchor_))) {
-                               Point p = bv_funcs::getPos(bv, it, false);
-                               int xo = p.x_;
-                               int yo = p.y_;
-                               if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
-                                       double const dx = xo - x;
-                                       double const dy = yo - y;
-                                       double const d = dx * dx + dy * dy;
-                                       //lyxerr << "itx: " << xo << " ity: " << yo << " d: " << d
-                                       //      << " dx: " << dx << " dy: " << dy
-                                       //      << " idx: " << it.idx() << " pos: " << it.pos()
-                                       //      << " it:\n" << it
-                                       //      << endl;
-                                       // '<=' 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) {
-                                               //lyxerr << "*" << endl;
-                                               best_dist   = d;
-                                               best_cursor = it;
-                                       }
+       pit_type from = cur.bottom().pit();
+       //pit_type to = cur.bottom().pit();
+       //lyxerr << "Pit start: " << from << endl;
+
+       //lyxerr << "bruteFind3: x: " << x << " y: " << y
+       //      << " xlow: " << xlow << " xhigh: " << xhigh
+       //      << " ylow: " << ylow << " yhigh: " << yhigh
+       //      << endl;
+       Inset & inset = bv.buffer().inset();
+       DocIterator it = doc_iterator_begin(inset);
+       it.pit() = from;
+       DocIterator et = doc_iterator_end(inset);
+
+       double best_dist = numeric_limits<double>::max();
+       DocIterator best_cursor = et;
+
+       for ( ; it != et; it.forwardPos()) {
+               // avoid invalid nesting when selecting
+               if (bv.cursorStatus(it) == CUR_INSIDE
+                               && (!cur.selection() || positionable(it, cur.anchor_))) {
+                       Point p = bv.getPos(it, false);
+                       int xo = p.x_;
+                       int yo = p.y_;
+                       if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
+                               double const dx = xo - x;
+                               double const dy = yo - y;
+                               double const d = dx * dx + dy * dy;
+                               //lyxerr << "itx: " << xo << " ity: " << yo << " d: " << d
+                               //      << " dx: " << dx << " dy: " << dy
+                               //      << " idx: " << it.idx() << " pos: " << it.pos()
+                               //      << " it:\n" << it
+                               //      << endl;
+                               // '<=' 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) {
+                                       //lyxerr << "*" << endl;
+                                       best_dist   = d;
+                                       best_cursor = it;
                                }
                        }
                }
-
-               //lyxerr << "best_dist: " << best_dist << " cur:\n" << best_cursor << endl;
-               if (best_cursor == et)
-                       return false;
-               cur.setCursor(best_cursor);
-               return true;
        }
 
-       docstring parbreak(Paragraph const & par)
-       {
-               odocstringstream ods;
+       //lyxerr << "best_dist: " << best_dist << " cur:\n" << best_cursor << endl;
+       if (best_cursor == et)
+               return false;
+       cur.setCursor(best_cursor);
+       return true;
+}
+
+docstring parbreak(Paragraph const & par)
+{
+       odocstringstream ods;
+       ods << '\n';
+       // only add blank line if we're not in an ERT or Listings inset
+       if (par.ownerCode() != ERT_CODE
+                       && par.ownerCode() != LISTINGS_CODE)
                ods << '\n';
-               // only add blank line if we're not in an ERT or Listings inset
-               if (par.ownerCode() != Inset::ERT_CODE
-                   && par.ownerCode() != Inset::LISTINGS_CODE)
-                       ods << '\n';
-               return ods.str();
-       }
+       return ods.str();
+}
 
 } // namespace anon
 
@@ -265,7 +265,8 @@ namespace {
 // bv functions are not yet available!
 Cursor::Cursor(BufferView & bv)
        : DocIterator(), bv_(&bv), anchor_(), x_target_(-1), textTargetOffset_(0),
-         selection_(false), mark_(false), logicalpos_(false)
+         selection_(false), mark_(false), logicalpos_(false),
+         current_font(inherit_font)
 {}
 
 
@@ -273,7 +274,8 @@ void Cursor::reset(Inset & inset)
 {
        clear();
        push_back(CursorSlice(inset));
-       anchor_ = DocIterator(inset);
+       anchor_ = doc_iterator_begin(inset);
+       anchor_.clear();
        clearTargetX();
        selection_ = false;
        mark_ = false;
@@ -289,9 +291,7 @@ void Cursor::setCursor(DocIterator const & cur)
 
 void Cursor::dispatch(FuncRequest const & cmd0)
 {
-       LYXERR(Debug::DEBUG) << BOOST_CURRENT_FUNCTION
-                            << " cmd: " << cmd0 << '\n'
-                            << *this << endl;
+       LYXERR(Debug::DEBUG, "cmd: " << cmd0 << '\n' << *this);
        if (empty())
                return;
 
@@ -300,15 +300,13 @@ void Cursor::dispatch(FuncRequest const & cmd0)
        Cursor safe = *this;
        
        // store some values to be used inside of the handlers
-       getPos(beforeDispX_, beforeDispY_);
-       beforeDispDepth_ = depth();
-       
-       for (; depth(); pop()) {
-               LYXERR(Debug::DEBUG) << "Cursor::dispatch: cmd: "
-                       << cmd0 << endl << *this << endl;
-               BOOST_ASSERT(pos() <= lastpos());
-               BOOST_ASSERT(idx() <= lastidx());
-               BOOST_ASSERT(pit() <= lastpit());
+       beforeDispatchCursor_ = *this;
+       for (; depth(); pop(), boundary(false)) {
+               LYXERR(Debug::DEBUG, "Cursor::dispatch: cmd: "
+                       << cmd0 << endl << *this);
+               LASSERT(pos() <= lastpos(), /**/);
+               LASSERT(idx() <= lastidx(), /**/);
+               LASSERT(pit() <= lastpit(), /**/);
 
                // The common case is 'LFUN handled, need update', so make the
                // LFUN handler's life easier by assuming this as default value.
@@ -319,13 +317,18 @@ void Cursor::dispatch(FuncRequest const & cmd0)
                if (disp_.dispatched())
                        break;
        }
+       
        // it completely to get a 'bomb early' behaviour in case this
        // object will be used again.
        if (!disp_.dispatched()) {
-               LYXERR(Debug::DEBUG) << "RESTORING OLD CURSOR!" << endl;
+               LYXERR(Debug::DEBUG, "RESTORING OLD CURSOR!");
                operator=(safe);
                disp_.update(Update::None);
                disp_.dispatched(false);
+       } else {
+               // restore the previous one because nested Cursor::dispatch calls
+               // are possible which would change it
+               beforeDispatchCursor_ = safe.beforeDispatchCursor_;
        }
 }
 
@@ -338,22 +341,21 @@ DispatchResult Cursor::result() const
 
 BufferView & Cursor::bv() const
 {
-       BOOST_ASSERT(bv_);
+       LASSERT(bv_, /**/);
        return *bv_;
 }
 
 
 Buffer & Cursor::buffer() const
 {
-       BOOST_ASSERT(bv_);
-       BOOST_ASSERT(bv_->buffer());
-       return *bv_->buffer();
+       LASSERT(bv_, /**/);
+       return bv_->buffer();
 }
 
 
 void Cursor::pop()
 {
-       BOOST_ASSERT(depth() >= 1);
+       LASSERT(depth() >= 1, /**/);
        pop_back();
 }
 
@@ -361,23 +363,22 @@ void Cursor::pop()
 void Cursor::push(Inset & p)
 {
        push_back(CursorSlice(p));
+       p.setBuffer(bv_->buffer());
 }
 
 
-void Cursor::pushLeft(Inset & p)
+void Cursor::pushBackward(Inset & p)
 {
-       BOOST_ASSERT(!empty());
-       //lyxerr << "Entering inset " << t << " left" << endl;
+       LASSERT(!empty(), /**/);
+       //lyxerr << "Entering inset " << t << " front" << endl;
        push(p);
        p.idxFirst(*this);
 }
 
 
-bool Cursor::popLeft()
+bool Cursor::popBackward()
 {
-       BOOST_ASSERT(!empty());
-       //lyxerr << "Leaving inset to the left" << endl;
-       inset().notifyCursorLeaves(*this);
+       LASSERT(!empty(), /**/);
        if (depth() == 1)
                return false;
        pop();
@@ -385,12 +386,11 @@ bool Cursor::popLeft()
 }
 
 
-bool Cursor::popRight()
+bool Cursor::popForward()
 {
-       BOOST_ASSERT(!empty());
-       //lyxerr << "Leaving inset to the right" << endl;
+       LASSERT(!empty(), /**/);
+       //lyxerr << "Leaving inset from in back" << endl;
        const pos_type lp = (depth() > 1) ? (*this)[depth() - 2].lastpos() : 0;
-       inset().notifyCursorLeaves(*this);
        if (depth() == 1)
                return false;
        pop();
@@ -401,7 +401,7 @@ bool Cursor::popRight()
 
 int Cursor::currentMode()
 {
-       BOOST_ASSERT(!empty());
+       LASSERT(!empty(), /**/);
        for (int i = depth() - 1; i >= 0; --i) {
                int res = operator[](i).inset().currentMode();
                if (res != Inset::UNDECIDED_MODE)
@@ -413,7 +413,7 @@ int Cursor::currentMode()
 
 void Cursor::getPos(int & x, int & y) const
 {
-       Point p = bv_funcs::getPos(bv(), *this, boundary());
+       Point p = bv().getPos(*this, boundary());
        x = p.x_;
        y = p.y_;
 }
@@ -421,8 +421,9 @@ void Cursor::getPos(int & x, int & y) const
 
 Row const & Cursor::textRow() const
 {
-       ParagraphMetrics const & pm = bv().parMetrics(text(), pit());
-       BOOST_ASSERT(!pm.rows().empty());
+       CursorSlice const & cs = innerTextSlice();
+       ParagraphMetrics const & pm = bv().parMetrics(cs.text(), cs.pit());
+       LASSERT(!pm.rows().empty(), /**/);
        return pm.getRow(pos(), boundary());
 }
 
@@ -434,7 +435,7 @@ void Cursor::resetAnchor()
 
 
 
-bool Cursor::posLeft()
+bool Cursor::posBackward()
 {
        if (pos() == 0)
                return false;
@@ -443,7 +444,7 @@ bool Cursor::posLeft()
 }
 
 
-bool Cursor::posRight()
+bool Cursor::posForward()
 {
        if (pos() == lastpos())
                return false;
@@ -452,9 +453,468 @@ bool Cursor::posRight()
 }
 
 
+bool Cursor::posVisRight(bool skip_inset)
+{
+       Cursor new_cur = *this; // where we will move to
+       pos_type left_pos; // position visually left of current cursor
+       pos_type right_pos; // position visually right of current cursor
+       bool new_pos_is_RTL; // is new position we're moving to RTL?
+
+       getSurroundingPos(left_pos, right_pos);
+
+       LYXERR(Debug::RTL, left_pos <<"|"<< right_pos << " (pos: "<< pos() <<")");
+
+       // Are we at an inset?
+       new_cur.pos() = right_pos;
+       new_cur.boundary(false);
+       if (!skip_inset &&
+               text()->checkAndActivateInsetVisual(new_cur, right_pos>=pos(), false)) {
+               // we actually move the cursor at the end of this function, for now
+               // we just keep track of the new position in new_cur...
+               LYXERR(Debug::RTL, "entering inset at: " << new_cur.pos());
+       }
+
+       // Are we already at rightmost pos in row?
+       else if (text()->empty() || right_pos == -1) {
+               
+               new_cur = *this;
+               if (!new_cur.posVisToNewRow(false)) {
+                       LYXERR(Debug::RTL, "not moving!");
+                       return false;
+               }
+               
+               // we actually move the cursor at the end of this function, for now 
+               // just keep track of the new position in new_cur...
+               LYXERR(Debug::RTL, "right edge, moving: " << int(new_cur.pit()) << "," 
+                       << int(new_cur.pos()) << "," << (new_cur.boundary() ? 1 : 0));
+
+       }
+       // normal movement to the right
+       else {
+               new_cur = *this;
+               // Recall, if the cursor is at position 'x', that means *before* 
+               // the character at position 'x'. In RTL, "before" means "to the 
+               // right of", in LTR, "to the left of". So currently our situation
+               // is this: the position to our right is 'right_pos' (i.e., we're 
+               // currently to the left of 'right_pos'). In order to move to the 
+               // right, it depends whether or not the character at 'right_pos' is RTL.
+               new_pos_is_RTL = paragraph().getFontSettings(
+                       bv().buffer().params(), right_pos).isVisibleRightToLeft();
+               // If the character at 'right_pos' *is* LTR, then in order to move to
+               // the right of it, we need to be *after* 'right_pos', i.e., move to
+               // position 'right_pos' + 1.
+               if (!new_pos_is_RTL) {
+                       new_cur.pos() = right_pos + 1;
+                       // set the boundary to true in two situations:
+                       if (
+                       // 1. if new_pos is now lastpos (which means that we're moving 
+                       // right to the end of an LTR chunk which is at the end of an
+                       // RTL paragraph);
+                               new_cur.pos() == lastpos()
+                       // 2. if the position *after* right_pos is RTL (we want to be 
+                       // *after* right_pos, not before right_pos + 1!)
+                               || paragraph().getFontSettings(bv().buffer().params(),
+                                               new_cur.pos()).isVisibleRightToLeft()
+                       )
+                               new_cur.boundary(true);
+                       else // set the boundary to false
+                               new_cur.boundary(false);
+               }
+               // Otherwise (if the character at position 'right_pos' is RTL), then
+               // moving to the right of it is as easy as setting the new position
+               // to 'right_pos'.
+               else {
+                       new_cur.pos() = right_pos;
+                       new_cur.boundary(false);
+               }
+       
+       }
+
+       bool moved = (new_cur.pos() != pos()
+                                 || new_cur.pit() != pit()
+                                 || new_cur.boundary() != boundary());
+       
+       if (moved) {
+               LYXERR(Debug::RTL, "moving to: " << new_cur.pos() 
+                       << (new_cur.boundary() ? " (boundary)" : ""));
+               *this = new_cur;
+       }
+
+       return moved;
+}
+
+
+bool Cursor::posVisLeft(bool skip_inset)
+{
+       Cursor new_cur = *this; // where we will move to
+       pos_type left_pos; // position visually left of current cursor
+       pos_type right_pos; // position visually right of current cursor
+       bool new_pos_is_RTL; // is new position we're moving to RTL?
+
+       getSurroundingPos(left_pos, right_pos);
+
+       LYXERR(Debug::RTL, left_pos <<"|"<< right_pos << " (pos: "<< pos() <<")");
+
+       // Are we at an inset?
+       new_cur.pos() = left_pos;
+       new_cur.boundary(false);
+       if (!skip_inset && 
+               text()->checkAndActivateInsetVisual(new_cur, left_pos >= pos(), true)) {
+               // we actually move the cursor at the end of this function, for now 
+               // we just keep track of the new position in new_cur...
+               LYXERR(Debug::RTL, "entering inset at: " << new_cur.pos());
+       }
+
+       // Are we already at leftmost pos in row?
+       else if (text()->empty() || left_pos == -1) {
+               
+               new_cur = *this;
+               if (!new_cur.posVisToNewRow(true)) {
+                       LYXERR(Debug::RTL, "not moving!");
+                       return false;
+               }
+               
+               // we actually move the cursor at the end of this function, for now 
+               // just keep track of the new position in new_cur...
+               LYXERR(Debug::RTL, "left edge, moving: " << int(new_cur.pit()) << "," 
+                       << int(new_cur.pos()) << "," << (new_cur.boundary() ? 1 : 0));
+
+       }
+       // normal movement to the left
+       else {
+               new_cur = *this;
+               // Recall, if the cursor is at position 'x', that means *before* 
+               // the character at position 'x'. In RTL, "before" means "to the 
+               // right of", in LTR, "to the left of". So currently our situation
+               // is this: the position to our left is 'left_pos' (i.e., we're 
+               // currently to the right of 'left_pos'). In order to move to the 
+               // left, it depends whether or not the character at 'left_pos' is RTL.
+               new_pos_is_RTL = paragraph().getFontSettings(
+                       bv().buffer().params(), left_pos).isVisibleRightToLeft();
+               // If the character at 'left_pos' *is* RTL, then in order to move to
+               // the left of it, we need to be *after* 'left_pos', i.e., move to
+               // position 'left_pos' + 1.
+               if (new_pos_is_RTL) {
+                       new_cur.pos() = left_pos + 1;
+                       // set the boundary to true in two situations:
+                       if (
+                       // 1. if new_pos is now lastpos (which means that we're moving left
+                       // to the end of an RTL chunk which is at the end of an LTR 
+                       // paragraph);
+                               new_cur.pos() == lastpos()
+                       // 2. if the position *after* left_pos is not RTL (we want to be 
+                       // *after* left_pos, not before left_pos + 1!)
+                               || !paragraph().getFontSettings(bv().buffer().params(),
+                                               new_cur.pos()).isVisibleRightToLeft()
+                       )
+                               new_cur.boundary(true);
+                       else // set the boundary to false
+                               new_cur.boundary(false);
+               }
+               // Otherwise (if the character at position 'left_pos' is LTR), then
+               // moving to the left of it is as easy as setting the new position
+               // to 'left_pos'.
+               else {
+                       new_cur.pos() = left_pos;
+                       new_cur.boundary(false);
+               }
+       
+       }
+
+       bool moved = (new_cur.pos() != pos() 
+                                 || new_cur.pit() != pit()
+                                 || new_cur.boundary() != boundary());
+
+       if (moved) {
+               LYXERR(Debug::RTL, "moving to: " << new_cur.pos() 
+                       << (new_cur.boundary() ? " (boundary)" : ""));
+               *this = new_cur;
+       }
+               
+       return moved;
+}
+
+
+void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos)
+{
+       // preparing bidi tables
+       Paragraph const & par = paragraph();
+       Buffer const & buf = buffer();
+       Row const & row = textRow();
+       Bidi bidi;
+       bidi.computeTables(par, buf, row);
+
+       LYXERR(Debug::RTL, "bidi: " << row.pos() << "--" << row.endpos());
+
+       // The cursor is painted *before* the character at pos(), or, if 'boundary'
+       // is true, *after* the character at (pos() - 1). So we already have one
+       // known position around the cursor:
+       pos_type known_pos = boundary() ? pos() - 1 : pos();
+       
+       // edge case: if we're at the end of the paragraph, things are a little 
+       // different (because lastpos is a position which does not really "exist" 
+       // --- there's no character there yet).
+       if (known_pos == lastpos()) {
+               if (par.isRTL(buf.params())) {
+                       left_pos = -1;
+                       right_pos = bidi.vis2log(row.pos());
+               }
+               else { // LTR paragraph
+                       right_pos = -1;
+                       left_pos = bidi.vis2log(row.endpos() - 1);
+               }
+               return;
+       }
+       
+       // Whether 'known_pos' is to the left or to the right of the cursor depends
+       // on whether it is an RTL or LTR character...
+       bool const cur_is_RTL = 
+               par.getFontSettings(buf.params(), known_pos).isVisibleRightToLeft();
+       // ... in the following manner:
+       // For an RTL character, "before" means "to the right" and "after" means
+       // "to the left"; and for LTR, it's the reverse. So, 'known_pos' is to the
+       // right of the cursor if (RTL && boundary) or (!RTL && !boundary):
+       bool known_pos_on_right = (cur_is_RTL == boundary());
+
+       // So we now know one of the positions surrounding the cursor. Let's 
+       // determine the other one:
+       
+       if (known_pos_on_right) {
+               right_pos = known_pos;
+               // *visual* position of 'left_pos':
+               pos_type v_left_pos = bidi.log2vis(right_pos) - 1;
+               // If the position we just identified as 'left_pos' is a "skipped 
+               // separator" (a separator which is at the logical end of a row,
+               // except for the last row in a paragraph; such separators are not
+               // painted, so they "are not really there"; note that in bidi text,
+               // such a separator could appear visually in the middle of a row),
+               // set 'left_pos' to the *next* position to the left.
+               if (bidi.inRange(v_left_pos) 
+                               && bidi.vis2log(v_left_pos) + 1 == row.endpos() 
+                               && row.endpos() < lastpos()
+                               && par.isSeparator(bidi.vis2log(v_left_pos))) {
+                       --v_left_pos;
+               }
+               // calculate the logical position of 'left_pos', if in row
+               if (!bidi.inRange(v_left_pos))
+                       left_pos = -1;
+               else
+                       left_pos = bidi.vis2log(v_left_pos);
+               // If the position we identified as 'right_pos' is a "skipped 
+               // separator", set 'right_pos' to the *next* position to the right.
+               if (right_pos + 1 == row.endpos() && row.endpos() < lastpos() 
+                               && par.isSeparator(right_pos)) {
+                       pos_type v_right_pos = bidi.log2vis(right_pos) + 1;
+                       if (!bidi.inRange(v_right_pos))
+                               right_pos = -1;
+                       else
+                               right_pos = bidi.vis2log(v_right_pos);
+               }
+       } 
+       else { // known_pos is on the left
+               left_pos = known_pos;
+               // *visual* position of 'right_pos'
+               pos_type v_right_pos = bidi.log2vis(left_pos) + 1;
+               // If the position we just identified as 'right_pos' is a "skipped 
+               // separator", set 'right_pos' to the *next* position to the right.
+               if (bidi.inRange(v_right_pos) 
+                               && bidi.vis2log(v_right_pos) + 1 == row.endpos() 
+                               && row.endpos() < lastpos()
+                               && par.isSeparator(bidi.vis2log(v_right_pos))) {
+                       ++v_right_pos;
+               }
+               // calculate the logical position of 'right_pos', if in row
+               if (!bidi.inRange(v_right_pos)) 
+                       right_pos = -1;
+               else
+                       right_pos = bidi.vis2log(v_right_pos);
+               // If the position we identified as 'left_pos' is a "skipped 
+               // separator", set 'left_pos' to the *next* position to the left.
+               if (left_pos + 1 == row.endpos() && row.endpos() < lastpos() 
+                               && par.isSeparator(left_pos)) {
+                       pos_type v_left_pos = bidi.log2vis(left_pos) - 1;
+                       if (!bidi.inRange(v_left_pos))
+                               left_pos = -1;
+                       else
+                               left_pos = bidi.vis2log(v_left_pos);
+               }
+       }
+       return;
+}
+
+
+bool Cursor::posVisToNewRow(bool movingLeft)
+{
+       Paragraph const & par = paragraph();
+       Buffer const & buf = buffer();
+       Row const & row = textRow();
+       bool par_is_LTR = !par.isRTL(buf.params());
+
+       // Inside a table, determining whether to move to the next or previous row
+       // should be done based on the table's direction. 
+       int s = depth() - 1;
+       if (s >= 1 && (*this)[s].inset().asInsetTabular()) {
+               par_is_LTR = !(*this)[s].inset().asInsetTabular()->isRightToLeft(*this);
+               LYXERR(Debug::RTL, "Inside table! par_is_LTR=" << (par_is_LTR ? 1 : 0));
+       }
+       
+       // if moving left in an LTR paragraph or moving right in an RTL one, 
+       // move to previous row
+       if (par_is_LTR == movingLeft) {
+               if (row.pos() == 0) { // we're at first row in paragraph
+                       if (pit() == 0) // no previous paragraph! don't move
+                               return false;
+                       // move to last pos in previous par
+                       --pit();
+                       pos() = lastpos();
+                       boundary(false);
+               } else { // move to previous row in this par
+                       pos() = row.pos() - 1; // this is guaranteed to be in previous row
+                       boundary(false);
+               }
+       }
+       // if moving left in an RTL paragraph or moving right in an LTR one, 
+       // move to next row
+       else {
+               if (row.endpos() == lastpos()) { // we're at last row in paragraph
+                       if (pit() == lastpit()) // last paragraph! don't move
+                               return false;
+                       // move to first row in next par
+                       ++pit();
+                       pos() = 0;
+                       boundary(false);
+               } else { // move to next row in this par
+                       pos() = row.endpos();
+                       boundary(false);
+               }
+       }
+       
+       // make sure we're at left-/right-most pos in new row
+       posVisToRowExtremity(!movingLeft);
+
+       return true;
+}
+
+
+void Cursor::posVisToRowExtremity(bool left)  
+{
+       // prepare bidi tables
+       Paragraph const & par = paragraph();
+       Buffer const & buf = buffer();
+       Row const & row = textRow();
+       Bidi bidi;
+       bidi.computeTables(par, buf, row);
+
+       LYXERR(Debug::RTL, "entering extremity: " << pit() << "," << pos() << ","
+               << (boundary() ? 1 : 0));
+
+       if (left) { // move to leftmost position
+               // if this is an RTL paragraph, and we're at the last row in the
+               // paragraph, move to lastpos
+               if (par.isRTL(buf.params()) && row.endpos() == lastpos())
+                       pos() = lastpos();
+               else {
+                       pos() = bidi.vis2log(row.pos());
+
+                       // Moving to the leftmost position in the row, the cursor should
+                       // normally be placed to the *left* of the leftmost position.
+                       // A very common exception, though, is if the leftmost character 
+                       // also happens to be the separator at the (logical) end of the row
+                       // --- in this case, the separator is positioned beyond the left 
+                       // margin, and we don't want to move the cursor there (moving to 
+                       // the left of the separator is equivalent to moving to the next
+                       // line). So, in this case we actually want to place the cursor 
+                       // to the *right* of the leftmost position (the separator). 
+                       // Another exception is if we're moving to the logically last 
+                       // position in the row, which is *not* a separator: this means
+                       // that the entire row has no separators (if there were any, the 
+                       // row would have been broken there); and therefore in this case
+                       // we also move to the *right* of the last position (this indicates
+                       // to the user that there is no space after this position, and is 
+                       // consistent with the behavior in the middle of a row --- moving
+                       // right or left moves to the next/previous character; if we were
+                       // to move to the *left* of this position, that would simulate 
+                       // a separator which is not really there!). 
+                       // Finally, there is an exception to the previous exception: if 
+                       // this non-separator-but-last-position-in-row is an inset, then
+                       // we *do* want to stay to the left of it anyway: this is the 
+                       // "boundary" which we simulate at insets.
+                       
+                       bool right_of_pos = false; // do we want to be to the right of pos?
+
+                       // as explained above, if at last pos in row, stay to the right
+                       if ((pos() == row.endpos() - 1) && !par.isInset(pos()))
+                               right_of_pos = true;
+
+                       // Now we know if we want to be to the left or to the right of pos,
+                       // let's make sure we are where we want to be.
+                       bool new_pos_is_RTL = 
+                               par.getFontSettings(buf.params(), pos()).isVisibleRightToLeft();
+
+                       if (new_pos_is_RTL == !right_of_pos) {
+                               ++pos();
+                               boundary(true);
+                       }
+                       
+               }
+       }
+       else { // move to rightmost position
+               // if this is an LTR paragraph, and we're at the last row in the
+               // paragraph, move to lastpos
+               if (!par.isRTL(buf.params()) && row.endpos() == lastpos())
+                       pos() = lastpos();
+               else {
+                       pos() = bidi.vis2log(row.endpos() - 1);
+
+                       // Moving to the rightmost position in the row, the cursor should
+                       // normally be placed to the *right* of the rightmost position.
+                       // A very common exception, though, is if the rightmost character 
+                       // also happens to be the separator at the (logical) end of the row
+                       // --- in this case, the separator is positioned beyond the right 
+                       // margin, and we don't want to move the cursor there (moving to 
+                       // the right of the separator is equivalent to moving to the next
+                       // line). So, in this case we actually want to place the cursor 
+                       // to the *left* of the rightmost position (the separator). 
+                       // Another exception is if we're moving to the logically last 
+                       // position in the row, which is *not* a separator: this means
+                       // that the entire row has no separators (if there were any, the 
+                       // row would have been broken there); and therefore in this case
+                       // we also move to the *left* of the last position (this indicates
+                       // to the user that there is no space after this position, and is 
+                       // consistent with the behavior in the middle of a row --- moving
+                       // right or left moves to the next/previous character; if we were
+                       // to move to the *right* of this position, that would simulate 
+                       // a separator which is not really there!). 
+                       // Finally, there is an exception to the previous exception: if 
+                       // this non-separator-but-last-position-in-row is an inset, then
+                       // we *do* want to stay to the right of it anyway: this is the 
+                       // "boundary" which we simulate at insets.
+                       
+                       bool left_of_pos = false; // do we want to be to the left of pos?
+
+                       // as explained above, if at last pos in row, stay to the left
+                       if ((pos() == row.endpos() - 1) && !par.isInset(pos()))
+                               left_of_pos = true;
+
+                       // Now we know if we want to be to the left or to the right of pos,
+                       // let's make sure we are where we want to be.
+                       bool new_pos_is_RTL = 
+                               par.getFontSettings(buf.params(), pos()).isVisibleRightToLeft();
+
+                       if (new_pos_is_RTL == left_of_pos) {
+                               ++pos();
+                               boundary(true);
+                       }
+               }
+       }
+       LYXERR(Debug::RTL, "leaving extremity: " << pit() << "," << pos() << ","
+               << (boundary() ? 1 : 0));
+}
+
+
 CursorSlice Cursor::anchor() const
 {
-       BOOST_ASSERT(anchor_.depth() >= depth());
+       LASSERT(anchor_.depth() >= depth(), /**/);
        CursorSlice normal = anchor_[depth() - 1];
        if (depth() < anchor_.depth() && top() <= normal) {
                // anchor is behind cursor -> move anchor behind the inset
@@ -507,9 +967,7 @@ void Cursor::setSelection()
 {
        selection() = true;
        // A selection with no contents is not a selection
-#ifdef WITH_WARNINGS
-#warning doesnt look ok
-#endif
+       // FIXME: doesnt look ok
        if (pit() == anchor().pit() && pos() == anchor().pos())
                selection() = false;
 }
@@ -581,17 +1039,21 @@ void Cursor::info(odocstream & os) const
 bool Cursor::selHandle(bool sel)
 {
        //lyxerr << "Cursor::selHandle" << endl;
+       if (mark())
+               sel = true;
        if (sel == selection())
                return false;
 
+       if (!sel)
+               cap::saveSelection(*this);
+
        resetAnchor();
        selection() = sel;
-       cap::saveSelection(*this);
        return true;
 }
 
 
-std::ostream & operator<<(std::ostream & os, Cursor const & cur)
+ostream & operator<<(ostream & os, Cursor const & cur)
 {
        os << "\n cursor:                                | anchor:\n";
        for (size_t i = 0, n = cur.depth(); i != n; ++i) {
@@ -610,11 +1072,20 @@ std::ostream & operator<<(std::ostream & os, Cursor const & cur)
        return os;
 }
 
+
+LyXErr & operator<<(LyXErr & os, Cursor const & cur)
+{
+       os.stream() << cur;
+       return os;
+}
+
+
 } // namespace lyx
 
 
 ///////////////////////////////////////////////////////////////////
 //
+// FIXME: Look here
 // The part below is the non-integrated rest of the original math
 // cursor. This should be either generalized for texted or moved
 // back to mathed (in most cases to InsetMathNest).
@@ -635,7 +1106,7 @@ namespace lyx {
 //#define FILEDEBUG 1
 
 
-bool Cursor::isInside(Inset const * p)
+bool Cursor::isInside(Inset const * p) const
 {
        for (size_t i = 0; i != depth(); ++i)
                if (&operator[](i).inset() == p)
@@ -669,17 +1140,17 @@ bool Cursor::openable(MathAtom const & t) const
        // we can't move into anything new during selection
        if (depth() >= anchor_.depth())
                return false;
-       if (!ptr_cmp(t.nucleus(), &anchor_[depth()].inset()))
+       if (t.nucleus() != &anchor_[depth()].inset())
                return false;
 
        return true;
 }
 
 
-void Cursor::setScreenPos(int x, int y)
+void Cursor::setScreenPos(int x, int /*y*/)
 {
        setTargetX(x);
-       bruteFind(*this, x, y, 0, bv().workWidth(), 0, bv().workHeight());
+       //bruteFind(*this, x, y, 0, bv().workWidth(), 0, bv().workHeight());
 }
 
 
@@ -706,6 +1177,8 @@ void Cursor::plainInsert(MathAtom const & t)
 {
        cell().insert(pos(), t);
        ++pos();
+       inset().setBuffer(bv_->buffer());
+       inset().initView();
 }
 
 
@@ -720,7 +1193,7 @@ void Cursor::insert(docstring const & str)
 void Cursor::insert(char_type c)
 {
        //lyxerr << "Cursor::insert char '" << c << "'" << endl;
-       BOOST_ASSERT(!empty());
+       LASSERT(!empty(), /**/);
        if (inMathed()) {
                cap::selClearOrDel(*this);
                insert(new InsetMathChar(c));
@@ -739,12 +1212,16 @@ void Cursor::insert(MathAtom const & t)
 }
 
 
-void Cursor::insert(Inset * inset)
+void Cursor::insert(Inset * inset0)
 {
+       LASSERT(inset0, /**/);
        if (inMathed())
-               insert(MathAtom(inset));
-       else
-               text()->insertInset(*this, inset);
+               insert(MathAtom(inset0));
+       else {
+               text()->insertInset(*this, inset0);
+               inset0->setBuffer(bv_->buffer());
+               inset0->initView();
+       }
 }
 
 
@@ -766,10 +1243,10 @@ void Cursor::niceInsert(MathAtom const & t)
        plainInsert(t);
        // enter the new inset and move the contents of the selection if possible
        if (t->isActive()) {
-               posLeft();
-               // be careful here: don't use 'pushLeft(t)' as this we need to
+               posBackward();
+               // be careful here: don't use 'pushBackward(t)' as this we need to
                // push the clone, not the original
-               pushLeft(*nextInset());
+               pushBackward(*nextInset());
                // We may not use niceInsert here (recursion)
                MathData ar;
                asArray(safe, ar);
@@ -800,7 +1277,7 @@ bool Cursor::backspace()
        if (pos() == 0) {
                // If empty cell, and not part of a big cell
                if (lastpos() == 0 && inset().nargs() == 1) {
-                       popLeft();
+                       popBackward();
                        // Directly delete empty cell: [|[]] => [|]
                        if (inMathed()) {
                                plainErase();
@@ -813,7 +1290,7 @@ bool Cursor::backspace()
                        if (inMathed())
                                pullArg();
                        else
-                               popLeft();
+                               popBackward();
                        return true;
                }
        }
@@ -859,7 +1336,7 @@ bool Cursor::erase()
        if (pos() == lastpos()) {
                bool one_cell = inset().nargs() == 1;
                if (one_cell && lastpos() == 0) {
-                       popLeft();
+                       popBackward();
                        // Directly delete empty cell: [|[]] => [|]
                        if (inMathed()) {
                                plainErase();
@@ -922,6 +1399,8 @@ bool Cursor::macroModeClose()
                return false;
        InsetMathUnknown * p = activeMacro();
        p->finalize();
+       MathData selection;
+       asArray(p->selection(), selection);
        docstring const s = p->name();
        --pos();
        cell().erase(pos());
@@ -938,7 +1417,38 @@ bool Cursor::macroModeClose()
        InsetMathNest * const in = inset().asInsetMath()->asNestInset();
        if (in && in->interpretString(*this, s))
                return true;
-       plainInsert(createInsetMath(name));
+       MathAtom atom = createInsetMath(name);
+
+       // try to put argument into macro, if we just inserted a macro
+       bool macroArg = false;
+       MathMacro * atomAsMacro = atom.nucleus()->asMacro();
+       if (atomAsMacro) {
+               // macros here are still unfolded (in init mode in fact). So
+               // we have to resolve the macro here manually and check its arity
+               // to put the selection behind it if arity > 0.
+               MacroData const * data = buffer().getMacro(atomAsMacro->name());
+               if (selection.size() > 0 && data && data->numargs() - data->optionals() > 0) {
+                       macroArg = true;
+                       atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 1);
+               } else
+                       // non-greedy case. Do not touch the arguments behind
+                       atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 0);
+       }
+
+       // insert remembered selection into first argument of a non-macro
+       else if (atom.nucleus()->nargs() > 0)
+               atom.nucleus()->cell(0).append(selection);
+       
+       plainInsert(atom);
+
+       // finally put the macro argument behind, if needed
+       if (macroArg) {
+               if (selection.size() > 1)
+                       plainInsert(MathAtom(new InsetMathBrace(selection)));
+               else
+                       insert(selection);
+       }
+       
        return true;
 }
 
@@ -955,8 +1465,8 @@ void Cursor::handleNest(MathAtom const & a, int c)
        MathAtom t = a;
        asArray(cap::grabAndEraseSelection(*this), t.nucleus()->cell(c));
        insert(t);
-       posLeft();
-       pushLeft(*nextInset());
+       posBackward();
+       pushBackward(*nextInset());
 }
 
 
@@ -1003,13 +1513,17 @@ InsetMathUnknown * Cursor::activeMacro()
 }
 
 
+InsetMathUnknown const * Cursor::activeMacro() const
+{
+       return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
+}
+
+
 void Cursor::pullArg()
 {
-#ifdef WITH_WARNINGS
-#warning Look here
-#endif
+       // FIXME: Look here
        MathData ar = cell();
-       if (popLeft() && inMathed()) {
+       if (popBackward() && inMathed()) {
                plainErase();
                cell().insert(pos(), ar);
                resetAnchor();
@@ -1021,9 +1535,7 @@ void Cursor::pullArg()
 
 void Cursor::touch()
 {
-#ifdef WITH_WARNINGS
-#warning look here
-#endif
+       // FIXME: look here
 #if 0
        DocIterator::const_iterator it = begin();
        DocIterator::const_iterator et = end();
@@ -1064,19 +1576,19 @@ bool Cursor::upDownInMath(bool up)
        int xo = 0;
        int yo = 0;
        getPos(xo, yo);
-       xo = beforeDispX_;
-
+       xo = theLyXFunc().cursorBeforeDispatchX();
+       
        // check if we had something else in mind, if not, this is the future
        // target
        if (x_target_ == -1)
                setTargetX(xo);
-       else if (inset().asTextInset() && xo - textTargetOffset() != x_target()) {
+       else if (inset().asInsetText() && xo - textTargetOffset() != x_target()) {
                // In text mode inside the line (not left or right) possibly set a new target_x,
                // but only if we are somewhere else than the previous target-offset.
                
                // We want to keep the x-target on subsequent up/down movements
                // that cross beyond the end of short lines. Thus a special
-               // handling when the cursor is at the end of line: Use the new 
+               // handling when the cursor is at the end of line: Use the new
                // x-target only if the old one was before the end of line
                // or the old one was after the beginning of the line
                bool inRTL = isWithinRtlParagraph(*this);
@@ -1090,12 +1602,12 @@ bool Cursor::upDownInMath(bool up)
                        right = pos() == textRow().endpos();
                }
                if ((!left && !right) ||
-                               (left && !right && xo < x_target_) || 
+                               (left && !right && xo < x_target_) ||
                                (!left && right && x_target_ < xo))
                        setTargetX(xo);
                else
                        xo = targetX();
-       } else 
+       } else
                xo = targetX();
 
        // try neigbouring script insets
@@ -1114,8 +1626,9 @@ bool Cursor::upDownInMath(bool up)
                                int x;
                                int y;
                                getPos(x, y);
-                               if ((!up && y <= beforeDispY_) ||
-                                               (up && y >= beforeDispY_))
+                               int oy = theLyXFunc().cursorBeforeDispatchY();
+                               if ((!up && y <= oy) ||
+                                               (up && y >= oy))
                                        operator=(old);
                                else
                                        return true;
@@ -1134,8 +1647,9 @@ bool Cursor::upDownInMath(bool up)
                                int x;
                                int y;
                                getPos(x, y);
-                               if ((!up && y <= beforeDispY_) ||
-                                               (up && y >= beforeDispY_))
+                               int oy = theLyXFunc().cursorBeforeDispatchY();
+                               if ((!up && y <= oy) ||
+                                               (up && y >= oy))
                                        operator=(old);
                                else
                                        return true;
@@ -1153,12 +1667,13 @@ bool Cursor::upDownInMath(bool up)
        }
        
        // any improvement going just out of inset?
-       if (popLeft() && inMathed()) {
-               //lyxerr << "updown: popLeft succeeded" << endl;
+       if (popBackward() && inMathed()) {
+               //lyxerr << "updown: popBackward succeeded" << endl;
                int xnew;
                int ynew;
+               int yold = theLyXFunc().cursorBeforeDispatchY();
                getPos(xnew, ynew);
-               if (up ? ynew < beforeDispY_ : ynew > beforeDispY_)
+               if (up ? ynew < yold : ynew > yold)
                        return true;
        }
        
@@ -1168,28 +1683,29 @@ bool Cursor::upDownInMath(bool up)
 }
 
 
-bool Cursor::upDownInText(bool up
+bool Cursor::upDownInText(bool up, bool & updateNeeded)
 {
-       BOOST_ASSERT(text());
+       LASSERT(text(), /**/);
 
        // where are we?
        int xo = 0;
        int yo = 0;
        getPos(xo, yo);
-       xo = beforeDispX_;
-       
+       xo = theLyXFunc().cursorBeforeDispatchX();
+
        // update the targetX - this is here before the "return false"
        // to set a new target which can be used by InsetTexts above
        // if we cannot move up/down inside this inset anymore
        if (x_target_ == -1)
                setTargetX(xo);
-       else if (xo - textTargetOffset() != x_target() && depth() == beforeDispDepth_) {
+       else if (xo - textTargetOffset() != x_target() &&
+                                        depth() == beforeDispatchCursor_.depth()) {
                // In text mode inside the line (not left or right) possibly set a new target_x,
                // but only if we are somewhere else than the previous target-offset.
                
                // We want to keep the x-target on subsequent up/down movements
                // that cross beyond the end of short lines. Thus a special
-               // handling when the cursor is at the end of line: Use the new 
+               // handling when the cursor is at the end of line: Use the new
                // x-target only if the old one was before the end of line
                // or the old one was after the beginning of the line
                bool inRTL = isWithinRtlParagraph(*this);
@@ -1203,16 +1719,16 @@ bool Cursor::upDownInText(bool up)
                        right = pos() == textRow().endpos();
                }
                if ((!left && !right) ||
-                               (left && !right && xo < x_target_) || 
+                               (left && !right && xo < x_target_) ||
                                (!left && right && x_target_ < xo))
                        setTargetX(xo);
                else
                        xo = targetX();
-       } else 
+       } else
                xo = targetX();
                
        // first get the current line
-       TextMetrics const & tm = bv_->textMetrics(text());
+       TextMetrics & tm = bv_->textMetrics(text());
        ParagraphMetrics const & pm = tm.parMetrics(pit());
        int row;
        if (pos() && boundary())
@@ -1225,20 +1741,20 @@ bool Cursor::upDownInText(bool up)
                if (pit() == 0 && row == 0)
                        return false;
        } else {
-               if (pit() + 1 >= int(text()->paragraphs().size()) && 
+               if (pit() + 1 >= int(text()->paragraphs().size()) &&
                                row + 1 >= int(pm.rows().size()))
                        return false;
-       }       
-       
+       }
+
        // with and without selection are handled differently
        if (!selection()) {
-               int yo = bv_funcs::getPos(bv(), *this, boundary()).y_;
+               int yo = bv().getPos(*this, boundary()).y_;
                Cursor old = *this;
                // To next/previous row
                if (up)
-                       text()->editXY(*this, xo, yo - textRow().ascent() - 1);
+                       tm.editXY(*this, xo, yo - textRow().ascent() - 1);
                else
-                       text()->editXY(*this, xo, yo + textRow().descent() + 1);
+                       tm.editXY(*this, xo, yo + textRow().descent() + 1);
                clearSelection();
                
                // This happens when you move out of an inset.
@@ -1248,35 +1764,40 @@ bool Cursor::upDownInText(bool up)
                Cursor dummy = *this;
                if (dummy == old)
                        ++dummy.pos();
-               
-               bool const changed = bv().checkDepm(dummy, old);
-               
-               // Make sure that cur gets back whatever happened to dummy(Lgb)
-               if (changed)
+               if (bv().checkDepm(dummy, old)) {
+                       updateNeeded = true;
+                       // Make sure that cur gets back whatever happened to dummy(Lgb)
                        operator=(dummy);
+               }
        } else {
                // if there is a selection, we stay out of any inset, and just jump to the right position:
                Cursor old = *this;
                if (up) {
                        if (row > 0) {
-                               top().pos() = std::min(tm.x2pos(pit(), row - 1, xo), top().lastpos());
+                               top().pos() = min(tm.x2pos(pit(), row - 1, xo), top().lastpos());
                        } else if (pit() > 0) {
                                --pit();
-                               ParagraphMetrics const & pmcur = bv_->parMetrics(text(), pit());
-                               top().pos() = std::min(tm.x2pos(pit(), pmcur.rows().size() - 1, xo), top().lastpos());
+                               TextMetrics & tm = bv_->textMetrics(text());
+                               if (!tm.contains(pit()))
+                                       tm.newParMetricsUp();
+                               ParagraphMetrics const & pmcur = tm.parMetrics(pit());
+                               top().pos() = min(tm.x2pos(pit(), pmcur.rows().size() - 1, xo), top().lastpos());
                        }
                } else {
                        if (row + 1 < int(pm.rows().size())) {
-                               top().pos() = std::min(tm.x2pos(pit(), row + 1, xo), top().lastpos());
+                               top().pos() = min(tm.x2pos(pit(), row + 1, xo), top().lastpos());
                        } else if (pit() + 1 < int(text()->paragraphs().size())) {
                                ++pit();
-                               top().pos() = std::min(tm.x2pos(pit(), 0, xo), top().lastpos());
+                               TextMetrics & tm = bv_->textMetrics(text());
+                               if (!tm.contains(pit()))
+                                       tm.newParMetricsDown();
+                               top().pos() = min(tm.x2pos(pit(), 0, xo), top().lastpos());
                        }
                }
-               
-               bv().checkDepm(*this, old);
+
+               updateNeeded |= bv().checkDepm(*this, old);
        }
-       
+
        updateTextTargetOffset();
        return true;
 }      
@@ -1284,7 +1805,7 @@ bool Cursor::upDownInText(bool up)
 
 void Cursor::handleFont(string const & font)
 {
-       LYXERR(Debug::DEBUG) << BOOST_CURRENT_FUNCTION << ": " << font << endl;
+       LYXERR(Debug::DEBUG, font);
        docstring safe;
        if (selection()) {
                macroModeClose();
@@ -1295,17 +1816,17 @@ void Cursor::handleFont(string const & font)
                // something left in the cell
                if (pos() == 0) {
                        // cursor in first position
-                       popLeft();
+                       popBackward();
                } else if (pos() == lastpos()) {
                        // cursor in last position
-                       popRight();
+                       popForward();
                } else {
                        // cursor in between. split cell
                        MathData::iterator bt = cell().begin();
                        MathAtom at = createInsetMath(from_utf8(font));
                        at.nucleus()->cell(0) = MathData(bt, bt + pos());
                        cell().erase(bt, bt + pos());
-                       popLeft();
+                       popBackward();
                        plainInsert(at);
                }
        } else {
@@ -1335,7 +1856,6 @@ docstring Cursor::selectionAsString(bool label) const
                return docstring();
 
        if (inTexted()) {
-               Buffer const & buffer = *bv().buffer();
                ParagraphList const & pars = text()->paragraphs();
 
                // should be const ...
@@ -1345,22 +1865,22 @@ docstring Cursor::selectionAsString(bool label) const
                size_t const endpos = selEnd().pos();
 
                if (startpit == endpit)
-                       return pars[startpit].asString(buffer, startpos, endpos, label);
+                       return pars[startpit].asString(startpos, endpos, label);
 
                // First paragraph in selection
                docstring result = pars[startpit].
-                       asString(buffer, startpos, pars[startpit].size(), label)
+                       asString(startpos, pars[startpit].size(), label)
                                 + parbreak(pars[startpit]);
 
                // The paragraphs in between (if any)
                for (pit_type pit = startpit + 1; pit != endpit; ++pit) {
                        Paragraph const & par = pars[pit];
-                       result += par.asString(buffer, 0, par.size(), label)
+                       result += par.asString(0, par.size(), label)
                                  + parbreak(pars[pit]);
                }
 
                // Last paragraph in selection
-               result += pars[endpit].asString(buffer, 0, endpos, label);
+               result += pars[endpit].asString(0, endpos, label);
 
                return result;
        }
@@ -1397,18 +1917,10 @@ Encoding const * Cursor::getEncoding() const
 {
        if (empty())
                return 0;
-       if (!bv().buffer())
-               return 0;
-       int s = 0;
-       // go up until first non-0 text is hit
-       // (innermost text is 0 in mathed)
-       for (s = depth() - 1; s >= 0; --s)
-               if (operator[](s).text())
-                       break;
-       CursorSlice const & sl = operator[](s);
+       CursorSlice const & sl = innerTextSlice();
        Text const & text = *sl.text();
        Font font = text.getPar(sl.pit()).getFont(
-               bv().buffer()->params(), sl.pos(), outerFont(sl.pit(), text.paragraphs()));
+               bv().buffer().params(), sl.pos(), outerFont(sl.pit(), text.paragraphs()));
        return font.language()->encoding();
 }
 
@@ -1439,30 +1951,212 @@ void Cursor::noUpdate()
 
 Font Cursor::getFont() const
 {
+       // The logic here should more or less match to the Cursor::setCurrentFont
+       // logic, i.e. the cursor height should give a hint what will happen
+       // if a character is entered.
+       
        // HACK. far from being perfect...
-       int s = 0;
-       // go up until first non-0 text is hit
-       // (innermost text is 0 in mathed)
-       for (s = depth() - 1; s >= 0; --s)
-               if (operator[](s).text())
-                       break;
-       CursorSlice const & sl = operator[](s);
+
+       CursorSlice const & sl = innerTextSlice();
        Text const & text = *sl.text();
-       Font font = text.getPar(sl.pit()).getFont(
-               bv().buffer()->params(),
-               sl.pos(),
+       Paragraph const & par = text.getPar(sl.pit());
+       
+       // on boundary, so we are really at the character before
+       pos_type pos = sl.pos();
+       if (pos > 0 && boundary())
+               --pos;
+       
+       // on space? Take the font before (only for RTL boundary stay)
+       if (pos > 0) {
+               TextMetrics const & tm = bv().textMetrics(&text);
+               if (pos == sl.lastpos()
+                       || (par.isSeparator(pos) 
+                       && !tm.isRTLBoundary(sl.pit(), pos)))
+                       --pos;
+       }
+       
+       // get font at the position
+       Font font = par.getFont(bv().buffer().params(), pos,
                outerFont(sl.pit(), text.paragraphs()));
 
        return font;
 }
 
 
-void Cursor::fixIfBroken()
+bool Cursor::fixIfBroken()
 {
        if (DocIterator::fixIfBroken()) {
                        clearSelection();
                        resetAnchor();
+                       return true;
        }
+       return false;
+}
+
+
+bool notifyCursorLeaves(Cursor const & old, Cursor & cur)
+{
+       // find inset in common
+       size_type i;
+       for (i = 0; i < old.depth() && i < cur.depth(); ++i) {
+               if (&old[i].inset() != &cur[i].inset())
+                       break;
+       }
+
+       // update words if we just moved to another paragraph
+       if (i == old.depth() && i == cur.depth()
+           && !cur.buffer().isClean()
+           && cur.inTexted() && old.inTexted()
+           && cur.pit() != old.pit()) {
+               old.paragraph().updateWords(old.top());
+               return false;
+       }
+       
+       // notify everything on top of the common part in old cursor,
+       // but stop if the inset claims the cursor to be invalid now
+       for (; i < old.depth(); ++i) {
+               Cursor insetPos = old;
+               insetPos.cutOff(i);
+               if (old[i].inset().notifyCursorLeaves(insetPos, cur))
+                       return true;
+       }
+       
+       return false;
+}
+
+
+void Cursor::setCurrentFont()
+{
+       CursorSlice const & cs = innerTextSlice();
+       Paragraph const & par = cs.paragraph();
+       pos_type cpit = cs.pit();
+       pos_type cpos = cs.pos();
+       Text const & ctext = *cs.text();
+       TextMetrics const & tm = bv().textMetrics(&ctext);
+
+       // are we behind previous char in fact? -> go to that char
+       if (cpos > 0 && boundary())
+               --cpos;
+
+       // find position to take the font from
+       if (cpos != 0) {
+               // paragraph end? -> font of last char
+               if (cpos == lastpos())
+                       --cpos;
+               // on space? -> look at the words in front of space
+               else if (cpos > 0 && par.isSeparator(cpos))     {
+                       // abc| def -> font of c
+                       // abc |[WERBEH], i.e. boundary==true -> font of c
+                       // abc [WERBEH]| def, font of the space
+                       if (!tm.isRTLBoundary(cpit, cpos))
+                               --cpos;
+               }
+       }
+
+       // get font
+       BufferParams const & bufparams = buffer().params();
+       current_font = par.getFontSettings(bufparams, cpos);
+       real_current_font = tm.displayFont(cpit, cpos);
+
+       // special case for paragraph end
+       if (cs.pos() == lastpos()
+           && tm.isRTLBoundary(cpit, cs.pos())
+           && !boundary()) {
+               Language const * lang = par.getParLanguage(bufparams);
+               current_font.setLanguage(lang);
+               current_font.fontInfo().setNumber(FONT_OFF);
+               real_current_font.setLanguage(lang);
+               real_current_font.fontInfo().setNumber(FONT_OFF);
+       }
+}
+
+
+bool Cursor::textUndo()
+{
+       DocIterator dit = *this;
+       // Undo::textUndo() will modify dit.
+       if (!bv_->buffer().undo().textUndo(dit))
+               return false;
+       // Set cursor
+       setCursor(dit);
+       selection() = false;
+       resetAnchor();
+       fixIfBroken();
+       return true;
+}
+
+
+bool Cursor::textRedo()
+{
+       DocIterator dit = *this;
+       // Undo::textRedo() will modify dit.
+       if (!bv_->buffer().undo().textRedo(dit))
+               return false;
+       // Set cursor
+       setCursor(dit);
+       selection() = false;
+       resetAnchor();
+       fixIfBroken();
+       return true;
+}
+
+
+void Cursor::finishUndo()
+{
+       bv_->buffer().undo().finishUndo();
+}
+
+
+void Cursor::recordUndo(UndoKind kind, pit_type from, pit_type to)
+{
+       bv_->buffer().undo().recordUndo(*this, kind, from, to);
+}
+
+
+void Cursor::recordUndo(UndoKind kind, pit_type from)
+{
+       bv_->buffer().undo().recordUndo(*this, kind, from);
+}
+
+
+void Cursor::recordUndo(UndoKind kind)
+{
+       bv_->buffer().undo().recordUndo(*this, kind);
+}
+
+
+void Cursor::recordUndoInset(UndoKind kind)
+{
+       bv_->buffer().undo().recordUndoInset(*this, kind);
+}
+
+
+void Cursor::recordUndoFullDocument()
+{
+       bv_->buffer().undo().recordUndoFullDocument(*this);
+}
+
+
+void Cursor::recordUndoSelection()
+{
+       if (inMathed()) {
+               if (cap::multipleCellsSelected(*this))
+                       recordUndoInset();
+               else
+                       recordUndo();
+       } else
+               bv_->buffer().undo().recordUndo(*this, ATOMIC_UNDO,
+                       selBegin().pit(), selEnd().pit());
+}
+
+
+void Cursor::checkBufferStructure()
+{
+       if (paragraph().layout().toclevel == Layout::NOT_IN_TOC)
+               return;
+       Buffer const * master = buffer().masterBuffer();
+       master->tocBackend().updateItem(ParConstIterator(*this));
+       master->structureChanged();
 }