Index: src/Cursor.h
===================================================================
--- src/Cursor.h	(Revision 18515)
+++ src/Cursor.h	(Arbeitskopie)
@@ -123,10 +123,12 @@
 
 	/// in pixels from left of screen
 	int targetX() const;
-	/// write acess to target x position of cursor
-	int & x_target();
+	/// set the targetX
+	void setTargetX(int x, int offset = 0);
 	/// return target x position of cursor
 	int x_target() const;
+	/// return target x position offset, !=0 if x_target cannot be hit
+	int x_targetOffset() const;
 	/// set targetX in current position
 	void setTargetX();
 	/// clear target x position of cursor
@@ -205,6 +207,8 @@
 	 * shorter than x()
 	 */
 	int x_target_;
+	/// if a x_target cannot be hit exactly, put the difference here
+	int x_targetOffset_;
 	/// do we have a selection?
 	bool selection_;
 	/// are we on the way to get one?
Index: src/Text2.cpp
===================================================================
--- src/Text2.cpp	(Revision 18515)
+++ src/Text2.cpp	(Arbeitskopie)
@@ -916,7 +916,7 @@
 	cur.pit() = pit;
 	cur.pos() = pos;
 	cur.boundary(bound);
-	cur.x_target() = x;
+	cur.setTargetX(x);
 
 	// try to descend into nested insets
 	Inset * inset = checkInsetHit(cur.bv(), x, y);
@@ -938,8 +938,10 @@
 
 	// Make sure the cursor points to the position before
 	// this inset.
-	if (inset == insetBefore)
+	if (inset == insetBefore) {
 		--cur.pos();
+		cur.boundary(false);
+	}
 
 	// Try to descend recursively inside the inset.
 	inset = inset->editXY(cur, x, y);
@@ -954,8 +956,10 @@
 {
 	if (cur.selection())
 		return false;
-	if (cur.pos() == cur.lastpos())
+	if (front && cur.pos() == cur.lastpos())
 		return false;
+	if (!front && cur.pos() == 0)
+		return false;
 	Inset * inset = front ? cur.nextInset() : cur.prevInset();
 	if (!isHighlyEditableInset(inset))
 		return false;
@@ -978,38 +982,29 @@
 	// Tell BufferView to test for FitCursor in any case!
 	cur.updateFlags(Update::FitCursor);
 
+	// not at paragraph start?
 	if (cur.pos() > 0) {
-		if (cur.boundary())
-			return setCursor(cur, cur.pit(), cur.pos(), true, false);
-
-		bool updateNeeded = false;
-		// If checkAndActivateInset returns true, that means that
-		// the cursor was placed inside it, so we're done
-		if (!checkAndActivateInset(cur, false)) {
-			if (!cur.boundary() && 
-			    cur.textRow().pos() == cur.pos() 
-			    // FIXME: the following two conditions are copied
-			    // from cursorRight; however, isLineSeparator()
-			    // is definitely wrong here, isNewline I'm not sure
-			    // about. I'm leaving them as comments for now,
-			    // until we understand why they should or shouldn't
-			    // be here.
-			    /*&& 
-			    !cur.paragraph().isLineSeparator(cur.pos()-1) &&
-			    !cur.paragraph().isNewline(cur.pos() - 1)*/) {
-				updateNeeded |= setCursor(cur, cur.pit(), cur.pos(), 
-										  true, true);
-			}
-			updateNeeded |= setCursor(cur, cur.pit(),cur.pos() - 1, 
-									  true, false);
+		// if on right side of boundary (i.e. not at paragraph end, but line end)
+		// -> just jump to the left side
+		if (!cur.boundary() &&
+				cur.lastpos() != cur.pos() &&
+				cur.textRow().pos() == cur.pos() &&
+				!cur.paragraph().isLineSeparator(cur.pos()) &&
+				!cur.paragraph().isNewline(cur.pos())) {
+			return setCursor(cur, cur.pit(), cur.pos(), true, true);
 		}
-		return updateNeeded;
+		
+		// go left and try to enter inset
+		if (checkAndActivateInset(cur, false))
+			return false;
+		
+		// normal character left
+		return setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
 	}
 
-	if (cur.pit() > 0) {
-		// Steps into the paragraph above
+	// move to the previous paragraph or do nothing
+	if (cur.pit() > 0)
 		return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size());
-	}
 	return false;
 }
 
@@ -1019,168 +1014,36 @@
 	// Tell BufferView to test for FitCursor in any case!
 	cur.updateFlags(Update::FitCursor);
 
+	// not at paragraph end?
 	if (cur.pos() != cur.lastpos()) {
+		// if left of boundary -> just jump to right side 
 		if (cur.boundary())
-			return setCursor(cur, cur.pit(), cur.pos(),
-					 true, false);
+			return setCursor(cur, cur.pit(), cur.pos(), true, false);
 
-		bool updateNeeded = false;
-		// If checkAndActivateInset returns true, that means that 
-		// the cursor was placed inside it, so we're done
-		if (!checkAndActivateInset(cur, true)) {
-			if (cur.textRow().endpos() == cur.pos() + 1 &&
-			    cur.textRow().endpos() != cur.lastpos() &&
-			    !cur.paragraph().isLineSeparator(cur.pos()) &&
-			    !cur.paragraph().isNewline(cur.pos())) {
-				cur.boundary(true);
-			}
-			updateNeeded |= setCursor(cur, cur.pit(), cur.pos() + 1, true, cur.boundary());
+		// in front of editable inset, i.e. jump into it?
+		if (checkAndActivateInset(cur, true))
+			return false;
+		
+		// next character is left of boundary, but no explicit newline or line sep?
+		bool willBeLeftOfBoundary = false;
+		if (cur.textRow().endpos() == cur.pos() + 1 &&
+		    cur.textRow().endpos() != cur.lastpos() &&
+				!cur.paragraph().isLineSeparator(cur.pos()) &&
+				!cur.paragraph().isNewline(cur.pos())) {
+			willBeLeftOfBoundary = true;
 		}
-		return updateNeeded;
+		
+		// move right
+		return setCursor(cur, cur.pit(), cur.pos() + 1, true, willBeLeftOfBoundary);
 	}
 
+	// move to next paragraph
 	if (cur.pit() != cur.lastpit())
 		return setCursor(cur, cur.pit() + 1, 0);
 	return false;
 }
 
 
-bool Text::cursorUp(Cursor & cur)
-{
-	// Tell BufferView to test for FitCursor in any case!
-	cur.updateFlags(Update::FitCursor);
-
-	TextMetrics const & tm = cur.bv().textMetrics(this);
-	ParagraphMetrics const & pm = tm.parMetrics(cur.pit());
-
-	int row;
-	if (cur.pos() && cur.boundary())
-		row = pm.pos2row(cur.pos()-1);
-	else
-		row = pm.pos2row(cur.pos());
-
-	int x = cur.targetX();
-	cur.setTargetX();
-	// We want to keep the x-target on subsequent up 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 
-	// x-target only if the old one was before the end of line.
-	if (cur.pos() != pm.rows()[row].endpos() 
-		|| (!isWithinRtlParagraph(cur) && x < cur.targetX())
-		|| (isWithinRtlParagraph(cur) && x > cur.targetX())) {
-
-		x = cur.targetX();
-	}
-
-	if (!cur.selection()) {
-		int const y = bv_funcs::getPos(cur.bv(), cur, cur.boundary()).y_;
-		Cursor old = cur;
-		// Go to middle of previous row. 16 found to work OK;
-		// 12 = top/bottom margin of display math
-		int const margin = 3 * InsetMathHull::displayMargin() / 2;
-		editXY(cur, x, y - pm.rows()[row].ascent() - margin);
-		cur.clearSelection();
-
-		// This happens when you move out of an inset.
-		// And to give the DEPM the possibility of doing
-		// something we must provide it with two different
-		// cursors. (Lgb)
-		Cursor dummy = cur;
-		if (dummy == old)
-			++dummy.pos();
-
-		cur.bv().checkDepm(dummy, old);
-		return false;
-	}
-
-	bool updateNeeded = false;
-
-	if (row > 0) {
-		updateNeeded |= setCursor(cur, cur.pit(),
-			tm.x2pos(cur.pit(), row - 1, x));
-	} else if (cur.pit() > 0) {
-		--cur.pit();
-		//cannot use 'par' now
-		ParagraphMetrics const & pmcur = cur.bv().parMetrics(this, cur.pit());
-		updateNeeded |= setCursor(cur, cur.pit(),
-			tm.x2pos(cur.pit(), pmcur.rows().size() - 1, x));
-	}
-
-	cur.x_target() = x;
-
-	return updateNeeded;
-}
-
-
-bool Text::cursorDown(Cursor & cur)
-{
-	// Tell BufferView to test for FitCursor in any case!
-	cur.updateFlags(Update::FitCursor);
-
-	TextMetrics const & tm = cur.bv().textMetrics(this);
-	ParagraphMetrics const & pm = tm.parMetrics(cur.pit());
-
-	int row;
-	if (cur.pos() && cur.boundary())
-		row = pm.pos2row(cur.pos()-1);
-	else
-		row = pm.pos2row(cur.pos());
-
-	int x = cur.targetX();
-	cur.setTargetX();
-	// We want to keep the x-target on subsequent 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 
-	// x-target only if the old one was before the end of line.
-	if (cur.pos() != pm.rows()[row].endpos() 
-		|| (!isWithinRtlParagraph(cur) && x < cur.targetX())
-		|| (isWithinRtlParagraph(cur) && x > cur.targetX())) {
-
-		x = cur.targetX();
-	}
-
-	if (!cur.selection()) {
-		int const y = bv_funcs::getPos(cur.bv(), cur, cur.boundary()).y_;
-		Cursor old = cur;
-		// To middle of next row
-		int const margin = 3 * InsetMathHull::displayMargin() / 2;
-		editXY(cur, x, y + pm.rows()[row].descent() + margin);
-		cur.clearSelection();
-
-		// This happens when you move out of an inset.
-		// And to give the DEPM the possibility of doing
-		// something we must provide it with two different
-		// cursors. (Lgb)
-		Cursor dummy = cur;
-		if (dummy == old)
-			++dummy.pos();
-		
-		bool const changed = cur.bv().checkDepm(dummy, old);
-
-		// Make sure that cur gets back whatever happened to dummy(Lgb)
-		if (changed)
-			cur = dummy;
-
-		return false;
-	}
-
-	bool updateNeeded = false;
-
-	if (row + 1 < int(pm.rows().size())) {
-		updateNeeded |= setCursor(cur, cur.pit(),
-			tm.x2pos(cur.pit(), row + 1, x));
-	} else if (cur.pit() + 1 < int(paragraphs().size())) {
-		++cur.pit();
-		updateNeeded |= setCursor(cur, cur.pit(),
-			tm.x2pos(cur.pit(), 0, x));
-	}
-
-	cur.x_target() = x;
-
-	return updateNeeded;
-}
-
-
 bool Text::cursorUpParagraph(Cursor & cur)
 {
 	bool updated = false;
Index: src/Text3.cpp
===================================================================
--- src/Text3.cpp	(Revision 18515)
+++ src/Text3.cpp	(Arbeitskopie)
@@ -471,7 +471,7 @@
 		//lyxerr << "handle LFUN_UP[SEL]:\n" << cur << endl;
 		needsUpdate |= cur.selHandle(cmd.action == LFUN_UP_SELECT);
 
-		needsUpdate |= cursorUp(cur);
+		needsUpdate |= cur.up();
 
 		if (!needsUpdate && oldTopSlice == cur.top()
 			  && cur.boundary() == oldBoundary) {
@@ -486,7 +486,7 @@
 	case LFUN_DOWN_SELECT:
 		//lyxerr << "handle LFUN_DOWN[SEL]:\n" << cur << endl;
 		needsUpdate |= cur.selHandle(cmd.action == LFUN_DOWN_SELECT);
-		needsUpdate |= cursorDown(cur);
+		needsUpdate |= cur.down();
 
 		if (!needsUpdate && oldTopSlice == cur.top() &&
 		    cur.boundary() == oldBoundary)
@@ -1071,7 +1071,7 @@
 			int const y = std::max(0, std::min(wh - 1, cmd.y));
 
 			setCursorFromCoordinates(cur, cmd.x, y);
-			cur.x_target() = cmd.x;
+			cur.setTargetX(cmd.x);
 			if (cmd.y >= wh)
 				lyx::dispatch(FuncRequest(LFUN_DOWN_SELECT));
 			else if (cmd.y < 0)
@@ -1448,12 +1448,12 @@
 
 	case LFUN_FINISHED_UP:
 		LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_UP:\n" << cur << endl;
-		cursorUp(cur);
+		cur.up();
 		break;
 
 	case LFUN_FINISHED_DOWN:
 		LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_DOWN:\n" << cur << endl;
-		cursorDown(cur);
+		cur.down();
 		break;
 
 	case LFUN_LAYOUT_PARAGRAPH: {
Index: src/Cursor.cpp
===================================================================
--- src/Cursor.cpp	(Revision 18515)
+++ src/Cursor.cpp	(Arbeitskopie)
@@ -6,12 +6,14 @@
  * \author Alejandro Aguilar Sierra
  * \author Alfredo Braunstein
  * \author André Pönitz
+ * \author Stefan Schimanski
  *
  * Full author contact details are available in file CREDITS.
  */
 
 #include <config.h>
 
+#include "Bidi.h"
 #include "BufferView.h"
 #include "bufferview_funcs.h"
 #include "Buffer.h"
@@ -262,7 +264,7 @@
 // be careful: this is called from the bv's constructor, too, so
 // bv functions are not yet available!
 Cursor::Cursor(BufferView & bv)
-	: DocIterator(), bv_(&bv), anchor_(), x_target_(-1),
+	: DocIterator(), bv_(&bv), anchor_(), x_target_(-1), x_targetOffset_(0),
 	  selection_(false), mark_(false), logicalpos_(false)
 {}
 
@@ -526,9 +528,10 @@
 }
 
 
-int & Cursor::x_target()
+void Cursor::setTargetX(int x, int offset)
 {
-	return x_target_;
+	x_target_ = x;
+	x_targetOffset_ = offset;
 }
 
 
@@ -541,10 +544,10 @@
 void Cursor::clearTargetX()
 {
 	x_target_ = -1;
+	x_targetOffset_ = 0;
 }
 
 
-
 void Cursor::info(odocstream & os) const
 {
 	for (int i = 1, n = depth(); i < n; ++i) {
@@ -662,7 +665,7 @@
 
 void Cursor::setScreenPos(int x, int y)
 {
-	x_target() = x;
+	setTargetX(x);
 	bruteFind(*this, x, y, 0, bv().workWidth(), 0, bv().workHeight());
 }
 
@@ -951,17 +954,25 @@
 }
 
 
+int Cursor::x_targetOffset() const
+{
+	return x_targetOffset_;
+}
+
+
 void Cursor::setTargetX()
 {
 	int x;
 	int y;
 	getPos(x, y);
-	x_target_ = x;
+	setTargetX(x);
 }
 
 
 bool Cursor::inMacroMode() const
 {
+	if (!inMathed())
+		return false;
 	if (pos() == 0)
 		return false;
 	InsetMathUnknown const * p = prevAtom()->asUnknownInset();
@@ -1033,20 +1044,45 @@
 	// fragile. A distance of one pixel or a '<' vs '<=' _really
 	// matters. So fiddle around with it only if you think you know
 	// what you are doing!
-
 	int xo = 0;
 	int yo = 0;
 	getPos(xo, yo);
 
 	// check if we had something else in mind, if not, this is the future
 	// target
-	if (x_target() == -1)
-		x_target() = xo;
-	else
-		xo = x_target();
+	bool inText = depth() == 0 || top().inset().lyxCode() == Inset::TEXT_CODE;
+	if (x_target_ == -1)
+		setTargetX(xo);
+	else if (inText && xo - x_targetOffset() != 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 
+		// 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);
+		bool left;
+		bool right;
+		if (inRTL) {
+			left = pos() == textRow().endpos();
+			right = pos() == textRow().pos();
+		} else {
+			left = pos() == textRow().pos();
+			right = pos() == textRow().endpos();
+		}
+		if ((!left && !right) ||
+				(left && !right && xo < x_target_) || 
+				(!left && right && x_target_ < xo))
+			setTargetX(xo);
+		else
+			xo = targetX();
+	} else 
+		xo = targetX();
 
 	// try neigbouring script insets
-	if (!selection()) {
+	if (inMathed() && !selection()) {
 		// try left
 		if (pos() != 0) {
 			InsetMathScript const * p = prevAtom()->asScriptInset();
@@ -1071,23 +1107,7 @@
 		}
 	}
 
-// FIXME: Switch this on for more robust movement
-#if 0
-
-	return bruteFind3(*this, xo, yo, up);
-
-#else
-	//xarray().boundingBox(xlow, xhigh, ylow, yhigh);
-	//if (up)
-	//	yhigh = yo - 4;
-	//else
-	//	ylow = yo + 4;
-	//if (bruteFind(*this, xo, yo, xlow, xhigh, ylow, yhigh)) {
-	//	lyxerr << "updown: handled by brute find in the same cell" << endl;
-	//	return true;
-	//}
-
-	// try to find an inset that knows better then we
+	// try to find an inset that knows better then we,
 	while (true) {
 		//lyxerr << "updown: We are in " << &inset() << " idx: " << idx() << endl;
 		// ask inset first
@@ -1099,13 +1119,9 @@
 			return true;
 		}
 
-		// no such inset found, just take something "above"
-		if (!popLeft()) {
-			//lyxerr << "updown: popleft failed (strange case)" << endl;
-			int ylow  = up ? 0 : yo + 1;
-			int yhigh = up ? yo - 1 : bv().workHeight();
-			return bruteFind(*this, xo, yo, 0, bv().workWidth(), ylow, yhigh);
-		}
+		// no such inset found, continue with text line jumps below
+		if (!popLeft())
+			break;
 
 		// any improvement so far?
 		//lyxerr << "updown: popLeft succeeded" << endl;
@@ -1115,10 +1131,83 @@
 		if (up ? ynew < yo : ynew > yo)
 			return true;
 	}
+	
+	// From here we are in the text and jump one line up/down:
+	updateFlags(Update::FitCursor);
+	if (!selection()) {
+		yo = bv_funcs::getPos(bv(), *this, boundary()).y_;
+		Cursor old = *this;
+		// To next/previous row
+		if (up)
+			buffer().text().editXY(*this, xo, yo - textRow().ascent() - 1);
+		else
+			buffer().text().editXY(*this, xo, yo + textRow().descent() + 1);
+		clearSelection();
+		
+		// compute the offset of the new position to the target
+		int newx = 0;
+		int newy = 0;
+		getPos(newx, newy);
+		setTargetX(xo, newx - xo);
+		
+		// This happens when you move out of an inset.
+		// And to give the DEPM the possibility of doing
+		// something we must provide it with two different
+		// cursors. (Lgb)
+		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)
+			operator=(dummy);
+		
+		return true;
+	} else {
+		// if there is a selection, we stay out of any inset, and just jump to the right position:
+		
+		// first get the current line
+		TextMetrics const & tm = bv_->textMetrics(text());
+		ParagraphMetrics const & pm = tm.parMetrics(pit());
+		int row;
+		if (pos() && boundary())
+			row = pm.pos2row(pos() - 1);
+		else
+			row = pm.pos2row(pos());
+		
+		// then jump
+		Cursor old = *this;
+		if (up) {
+			if (row > 0) {
+				top().pos() = std::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());
+			}
+		} else {
+			if (row + 1 < int(pm.rows().size())) {
+				top().pos() = std::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());
+			}
+		}
+		
+		// compute the offset of the new position to the target
+		int newx = 0;
+		int newy = 0;
+		getPos(newx, newy);
+		setTargetX(xo, newx - xo);
 
+		bv().checkDepm(*this, old);
+		return true;
+	}
+	
 	// we should not come here.
 	BOOST_ASSERT(false);
-#endif
 }
 
 
Index: src/Text.h
===================================================================
--- src/Text.h	(Revision 18515)
+++ src/Text.h	(Arbeitskopie)
@@ -208,18 +208,6 @@
 	/// setCursorFromCoordinates() instead of checkInsetHit().
 	Inset * editXY(Cursor & cur, int x, int y);
 	
-	/// Move cursor one line up.
-	/**
-	 * Returns true if an update is needed after the move.
-	 */
-	/// FIXME: move to TextMetrics.
-	bool cursorUp(Cursor & cur);
-	/// Move cursor one line down.
-	/**
-	 * Returns true if an update is needed after the move.
-	 */
-	/// FIXME: move to TextMetrics.
-	bool cursorDown(Cursor & cur);
 	/// Move cursor one position left
 	/**
 	 * Returns true if an update is needed after the move.
