extract simple cursor movement into DocumentEditor

Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/CursorMoves.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/CursorMoves.java
index 27e7c79..361e4fb 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/CursorMoves.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/CursorMoves.java
@@ -21,6 +21,8 @@
 	private static final ICursorMove DOWN = new MoveDown();
 	private static final ICursorMove TO_WORD_START = new MoveToWordStart();
 	private static final ICursorMove TO_WORD_END = new MoveToWordEnd();
+	private static final ICursorMove TO_NEXT_WORD = new MoveToNextWord();
+	private static final ICursorMove TO_PREVIOUS_WORD = new MoveToPreviousWord();
 
 	public static ICursorMove toOffset(final int offset) {
 		return new MoveToOffset(offset);
@@ -30,6 +32,10 @@
 		return new MoveToAbsoluteCoordinates(x, y);
 	}
 
+	public static ICursorMove by(final int distance) {
+		return new MoveBy(distance);
+	}
+
 	public static ICursorMove left() {
 		return LEFT;
 	}
@@ -53,4 +59,12 @@
 	public static ICursorMove toWordEnd() {
 		return TO_WORD_END;
 	}
+
+	public static ICursorMove toNextWord() {
+		return TO_NEXT_WORD;
+	}
+
+	public static ICursorMove toPreviousWord() {
+		return TO_PREVIOUS_WORD;
+	}
 }
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveBy.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveBy.java
new file mode 100644
index 0000000..f1223c2
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveBy.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Florian Thienel and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * 		Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.core.internal.cursor;
+
+import org.eclipse.vex.core.internal.boxes.IContentBox;
+import org.eclipse.vex.core.internal.core.Graphics;
+import org.eclipse.vex.core.internal.core.Rectangle;
+
+public class MoveBy implements ICursorMove {
+
+	private final int distance;
+
+	public MoveBy(final int distance) {
+		this.distance = distance;
+	}
+
+	@Override
+	public int calculateNewOffset(final Graphics graphics, final ContentTopology contentTopology, final int currentOffset, final IContentBox currentBox, final Rectangle hotArea, final int preferredX) {
+		return Math.max(0, Math.min(currentOffset + distance, contentTopology.getLastOffset()));
+	}
+
+	@Override
+	public boolean preferX() {
+		return true;
+	}
+
+	@Override
+	public boolean isAbsolute() {
+		return true;
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToNextWord.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToNextWord.java
new file mode 100644
index 0000000..4fd4905
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToNextWord.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Florian Thienel and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * 		Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.core.internal.cursor;
+
+import org.eclipse.vex.core.internal.boxes.IContentBox;
+import org.eclipse.vex.core.internal.core.Graphics;
+import org.eclipse.vex.core.internal.core.Rectangle;
+import org.eclipse.vex.core.provisional.dom.IContent;
+
+public class MoveToNextWord implements ICursorMove {
+
+	@Override
+	public int calculateNewOffset(final Graphics graphics, final ContentTopology contentTopology, final int currentOffset, final IContentBox currentBox, final Rectangle hotArea, final int preferredX) {
+		final IContent content = currentBox.getContent();
+		final int lastOffset = contentTopology.getLastOffset();
+		int offset = currentOffset;
+		while (offset < lastOffset && !Character.isLetterOrDigit(content.charAt(offset))) {
+			offset++;
+		}
+
+		while (offset < lastOffset && Character.isLetterOrDigit(content.charAt(offset))) {
+			offset++;
+		}
+		return offset;
+	}
+
+	@Override
+	public boolean preferX() {
+		return true;
+	}
+
+	@Override
+	public boolean isAbsolute() {
+		return true;
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToPreviousWord.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToPreviousWord.java
new file mode 100644
index 0000000..ec28f14
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToPreviousWord.java
@@ -0,0 +1,34 @@
+package org.eclipse.vex.core.internal.cursor;
+
+import org.eclipse.vex.core.internal.boxes.IContentBox;
+import org.eclipse.vex.core.internal.core.Graphics;
+import org.eclipse.vex.core.internal.core.Rectangle;
+import org.eclipse.vex.core.provisional.dom.IContent;
+
+public class MoveToPreviousWord implements ICursorMove {
+
+	@Override
+	public int calculateNewOffset(final Graphics graphics, final ContentTopology contentTopology, final int currentOffset, final IContentBox currentBox, final Rectangle hotArea, final int preferredX) {
+		final IContent content = currentBox.getContent();
+		int offset = currentOffset;
+		while (offset > 1 && !Character.isLetterOrDigit(content.charAt(offset - 1))) {
+			offset--;
+		}
+
+		while (offset > 1 && Character.isLetterOrDigit(content.charAt(offset - 1))) {
+			offset--;
+		}
+		return offset;
+	}
+
+	@Override
+	public boolean preferX() {
+		return true;
+	}
+
+	@Override
+	public boolean isAbsolute() {
+		return true;
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/DocumentEditor.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/DocumentEditor.java
index 17b78ae..596b95e 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/DocumentEditor.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/DocumentEditor.java
@@ -10,9 +10,14 @@
  *******************************************************************************/
 package org.eclipse.vex.core.internal.widget;
 
+import static org.eclipse.vex.core.internal.cursor.CursorMoves.by;
+import static org.eclipse.vex.core.internal.cursor.CursorMoves.down;
+import static org.eclipse.vex.core.internal.cursor.CursorMoves.toNextWord;
 import static org.eclipse.vex.core.internal.cursor.CursorMoves.toOffset;
+import static org.eclipse.vex.core.internal.cursor.CursorMoves.toPreviousWord;
 import static org.eclipse.vex.core.internal.cursor.CursorMoves.toWordEnd;
 import static org.eclipse.vex.core.internal.cursor.CursorMoves.toWordStart;
+import static org.eclipse.vex.core.internal.cursor.CursorMoves.up;
 
 import java.text.MessageFormat;
 import java.util.ArrayList;
@@ -384,26 +389,30 @@
 
 	@Override
 	public void moveBy(final int distance) {
-		// TODO Auto-generated method stub
-
+		moveBy(distance, false);
 	}
 
 	@Override
 	public void moveBy(final int distance, final boolean select) {
-		// TODO Auto-generated method stub
-
+		if (select) {
+			cursor.select(by(distance));
+		} else {
+			cursor.move(by(distance));
+		}
 	}
 
 	@Override
 	public void moveTo(final ContentPosition position) {
-		// TODO Auto-generated method stub
-
+		moveTo(position, false);
 	}
 
 	@Override
 	public void moveTo(final ContentPosition position, final boolean select) {
-		// TODO Auto-generated method stub
-
+		if (select) {
+			cursor.select(toOffset(position.getOffset()));
+		} else {
+			cursor.move(toOffset(position.getOffset()));
+		}
 	}
 
 	@Override
@@ -420,26 +429,33 @@
 
 	@Override
 	public void moveToNextLine(final boolean select) {
-		// TODO Auto-generated method stub
-
+		if (select) {
+			cursor.select(down());
+		} else {
+			cursor.move(down());
+		}
 	}
 
 	@Override
 	public void moveToNextPage(final boolean select) {
-		// TODO Auto-generated method stub
-
 	}
 
 	@Override
 	public void moveToNextWord(final boolean select) {
-		// TODO Auto-generated method stub
-
+		if (select) {
+			cursor.select(toNextWord());
+		} else {
+			cursor.move(toNextWord());
+		}
 	}
 
 	@Override
 	public void moveToPreviousLine(final boolean select) {
-		// TODO Auto-generated method stub
-
+		if (select) {
+			cursor.select(up());
+		} else {
+			cursor.move(up());
+		}
 	}
 
 	@Override
@@ -450,8 +466,11 @@
 
 	@Override
 	public void moveToPreviousWord(final boolean select) {
-		// TODO Auto-generated method stub
-
+		if (select) {
+			cursor.select(toPreviousWord());
+		} else {
+			cursor.move(toPreviousWord());
+		}
 	}
 
 	/*