improve move to next/previous page
Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/ContentTopology.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/ContentTopology.java
index 6353811..62b9cfc 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/ContentTopology.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/ContentTopology.java
@@ -11,7 +11,9 @@
package org.eclipse.vex.core.internal.cursor;
import java.util.Collection;
+import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
import org.eclipse.vex.core.internal.boxes.BaseBoxVisitorWithResult;
import org.eclipse.vex.core.internal.boxes.DepthFirstBoxTraversal;
@@ -334,6 +336,118 @@
});
}
+ public static IContentBox findClosestContentBoxChildBelow(final IContentBox parent, final int x, final int y) {
+ final Iterable<IContentBox> candidates = findVerticallyClosestContentBoxChildrenBelow(parent, y);
+ return findHorizontallyClosestContentBox(candidates, x);
+ }
+
+ public static Iterable<IContentBox> findVerticallyClosestContentBoxChildrenBelow(final IContentBox parent, final int y) {
+ final LinkedList<IContentBox> candidates = new LinkedList<IContentBox>();
+ final int[] minVerticalDistance = new int[1];
+ minVerticalDistance[0] = Integer.MAX_VALUE;
+ parent.accept(new DepthFirstBoxTraversal<Object>() {
+ @Override
+ public Object visit(final StructuralNodeReference box) {
+ if (box == parent) {
+ super.visit(box);
+ } else {
+ final int distance = verticalDistance(box, y);
+ if (box.isBelow(y)) {
+ candidates.add(box);
+ minVerticalDistance[0] = Math.min(distance, minVerticalDistance[0]);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Object visit(final TextContent box) {
+ final int distance = verticalDistance(box, y);
+ if (box.isBelow(y)) {
+ candidates.add(box);
+ minVerticalDistance[0] = Math.min(distance, minVerticalDistance[0]);
+ }
+ return null;
+ }
+
+ @Override
+ public Object visit(final NodeEndOffsetPlaceholder box) {
+ final int distance = verticalDistance(box, y);
+ if (box.isBelow(y)) {
+ candidates.add(box);
+ minVerticalDistance[0] = Math.min(distance, minVerticalDistance[0]);
+ }
+ return null;
+ }
+ });
+
+ removeVerticallyDistantBoxes(candidates, y, minVerticalDistance[0]);
+ return candidates;
+ }
+
+ public static IContentBox findClosestContentBoxChildAbove(final IContentBox parent, final int x, final int y) {
+ final Iterable<IContentBox> candidates = findVerticallyClosestContentBoxChildrenAbove(parent, y);
+ return findHorizontallyClosestContentBox(candidates, x);
+ }
+
+ public static Iterable<IContentBox> findVerticallyClosestContentBoxChildrenAbove(final IContentBox parent, final int y) {
+ final LinkedList<IContentBox> candidates = new LinkedList<IContentBox>();
+ final int[] minVerticalDistance = new int[1];
+ minVerticalDistance[0] = Integer.MAX_VALUE;
+ parent.accept(new DepthFirstBoxTraversal<Object>() {
+ @Override
+ public Object visit(final StructuralNodeReference box) {
+ final int distance = verticalDistance(box, y);
+ if (box != parent && !box.isAbove(y)) {
+ return box;
+ }
+
+ if (box == parent) {
+ super.visit(box);
+ }
+
+ if (box != parent) {
+ candidates.add(box);
+ minVerticalDistance[0] = Math.min(distance, minVerticalDistance[0]);
+ }
+
+ return null;
+ }
+
+ @Override
+ public Object visit(final TextContent box) {
+ final int distance = verticalDistance(box, y);
+ if (box.isAbove(y) && distance <= minVerticalDistance[0]) {
+ candidates.add(box);
+ minVerticalDistance[0] = Math.min(distance, minVerticalDistance[0]);
+ }
+ return null;
+ }
+
+ @Override
+ public Object visit(final NodeEndOffsetPlaceholder box) {
+ final int distance = verticalDistance(box, y);
+ if (box.isAbove(y) && distance <= minVerticalDistance[0]) {
+ candidates.add(box);
+ minVerticalDistance[0] = Math.min(distance, minVerticalDistance[0]);
+ }
+ return null;
+ }
+ });
+
+ removeVerticallyDistantBoxes(candidates, y, minVerticalDistance[0]);
+ return candidates;
+ }
+
+ public static void removeVerticallyDistantBoxes(final List<? extends IContentBox> boxes, final int y, final int minVerticalDistance) {
+ for (final Iterator<? extends IContentBox> iter = boxes.iterator(); iter.hasNext();) {
+ final IContentBox candidate = iter.next();
+ if (verticalDistance(candidate, y) > minVerticalDistance) {
+ iter.remove();
+ }
+ }
+ }
+
public static int verticalDistance(final IContentBox box, final int y) {
return box.accept(new BaseBoxVisitorWithResult<Integer>(0) {
@Override
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveDown.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveDown.java
index f4360e3..ad84e93 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveDown.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveDown.java
@@ -10,12 +10,8 @@
*******************************************************************************/
package org.eclipse.vex.core.internal.cursor;
-import static org.eclipse.vex.core.internal.cursor.ContentTopology.findHorizontallyClosestContentBox;
+import static org.eclipse.vex.core.internal.cursor.ContentTopology.findClosestContentBoxChildBelow;
import static org.eclipse.vex.core.internal.cursor.ContentTopology.getParentContentBox;
-import static org.eclipse.vex.core.internal.cursor.ContentTopology.verticalDistance;
-
-import java.util.Iterator;
-import java.util.LinkedList;
import org.eclipse.vex.core.internal.boxes.BaseBoxVisitorWithResult;
import org.eclipse.vex.core.internal.boxes.DepthFirstBoxTraversal;
@@ -210,7 +206,7 @@
return currentBox;
}
- final IContentBox childBelow = findClosestContentBoxChildBelow(parent, x, y);
+ final IContentBox childBelow = handleSpecialCaseMovingIntoLastLineOfParagraph(findClosestContentBoxChildBelow(parent, x, y), x, y);
if (childBelow == null) {
if (containsInlineContent(parent)) {
return findNextContentBoxBelow(parent, x, y);
@@ -221,62 +217,6 @@
return childBelow;
}
- private static IContentBox findClosestContentBoxChildBelow(final IContentBox parent, final int x, final int y) {
- final Iterable<IContentBox> candidates = findVerticallyClosestContentBoxChildrenBelow(parent, y);
- final IContentBox finalCandidate = findHorizontallyClosestContentBox(candidates, x);
- final IContentBox realFinalCandidate = handleSpecialCaseMovingIntoLastLineOfParagraph(finalCandidate, x, y);
- return realFinalCandidate;
- }
-
- private static Iterable<IContentBox> findVerticallyClosestContentBoxChildrenBelow(final IContentBox parent, final int y) {
- final LinkedList<IContentBox> candidates = new LinkedList<IContentBox>();
- final int[] minVerticalDistance = new int[1];
- minVerticalDistance[0] = Integer.MAX_VALUE;
- parent.accept(new DepthFirstBoxTraversal<Object>() {
- @Override
- public Object visit(final StructuralNodeReference box) {
- if (box == parent) {
- super.visit(box);
- } else {
- final int distance = verticalDistance(box, y);
- if (box.isBelow(y)) {
- candidates.add(box);
- minVerticalDistance[0] = Math.min(distance, minVerticalDistance[0]);
- }
- }
- return null;
- }
-
- @Override
- public Object visit(final TextContent box) {
- final int distance = verticalDistance(box, y);
- if (box.isBelow(y)) {
- candidates.add(box);
- minVerticalDistance[0] = Math.min(distance, minVerticalDistance[0]);
- }
- return null;
- }
-
- @Override
- public Object visit(final NodeEndOffsetPlaceholder box) {
- final int distance = verticalDistance(box, y);
- if (box.isBelow(y)) {
- candidates.add(box);
- minVerticalDistance[0] = Math.min(distance, minVerticalDistance[0]);
- }
- return null;
- }
- });
-
- for (final Iterator<IContentBox> iter = candidates.iterator(); iter.hasNext();) {
- final IContentBox candidate = iter.next();
- if (verticalDistance(candidate, y) > minVerticalDistance[0]) {
- iter.remove();
- }
- }
- return candidates;
- }
-
private static IContentBox handleSpecialCaseMovingIntoLastLineOfParagraph(final IContentBox candidate, final int x, final int y) {
if (candidate == null) {
return null;
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToNextPage.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToNextPage.java
index 8211031..f0bcddf 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToNextPage.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToNextPage.java
@@ -10,9 +10,15 @@
*******************************************************************************/
package org.eclipse.vex.core.internal.cursor;
+import static org.eclipse.vex.core.internal.cursor.ContentTopology.findClosestContentBoxChildBelow;
import static org.eclipse.vex.core.internal.cursor.ContentTopology.getParentContentBox;
+import org.eclipse.vex.core.internal.boxes.BaseBoxVisitorWithResult;
import org.eclipse.vex.core.internal.boxes.IContentBox;
+import org.eclipse.vex.core.internal.boxes.InlineNodeReference;
+import org.eclipse.vex.core.internal.boxes.NodeEndOffsetPlaceholder;
+import org.eclipse.vex.core.internal.boxes.StructuralNodeReference;
+import org.eclipse.vex.core.internal.boxes.TextContent;
import org.eclipse.vex.core.internal.core.Graphics;
import org.eclipse.vex.core.internal.core.Rectangle;
import org.eclipse.vex.core.internal.widget.IViewPort;
@@ -32,9 +38,8 @@
final IContentBox box = contentTopology.findClosestBoxByCoordinates(x, y);
if (box == null) {
return currentOffset;
- }
- if (box.containsCoordinates(x, y)) {
- return box.getOffsetForCoordinates(graphics, x - box.getAbsoluteLeft(), y - box.getAbsoluteTop());
+ } else if (box.containsCoordinates(x, y)) {
+ return findBestOffsetWithin(graphics, box, x, y);
} else if (box.isLeftOf(x)) {
if (isLastEnclosedBox(box)) {
return box.getEndOffset() + 1;
@@ -48,6 +53,34 @@
}
}
+ private static int findBestOffsetWithin(final Graphics graphics, final IContentBox closestBoxByCoordinates, final int x, final int y) {
+ return closestBoxByCoordinates.accept(new BaseBoxVisitorWithResult<Integer>() {
+ @Override
+ public Integer visit(final StructuralNodeReference box) {
+ final IContentBox closestChild = findClosestContentBoxChildBelow(box, x, y);
+ if (closestChild == null) {
+ return box.getEndOffset();
+ }
+ return closestChild.accept(this);
+ }
+
+ @Override
+ public Integer visit(final InlineNodeReference box) {
+ return box.getOffsetForCoordinates(graphics, x - box.getAbsoluteLeft(), y - box.getAbsoluteTop());
+ }
+
+ @Override
+ public Integer visit(final NodeEndOffsetPlaceholder box) {
+ return box.getOffsetForCoordinates(graphics, x - box.getAbsoluteLeft(), y - box.getAbsoluteTop());
+ }
+
+ @Override
+ public Integer visit(final TextContent box) {
+ return box.getOffsetForCoordinates(graphics, x - box.getAbsoluteLeft(), y - box.getAbsoluteTop());
+ }
+ });
+ }
+
private static boolean isLastEnclosedBox(final IContentBox enclosedBox) {
final IContentBox parent = getParentContentBox(enclosedBox);
if (parent == null) {
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToPreviousPage.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToPreviousPage.java
index 2cc2d36..8bb52fc 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToPreviousPage.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveToPreviousPage.java
@@ -10,9 +10,15 @@
*******************************************************************************/
package org.eclipse.vex.core.internal.cursor;
+import static org.eclipse.vex.core.internal.cursor.ContentTopology.findClosestContentBoxChildAbove;
import static org.eclipse.vex.core.internal.cursor.ContentTopology.getParentContentBox;
+import org.eclipse.vex.core.internal.boxes.BaseBoxVisitorWithResult;
import org.eclipse.vex.core.internal.boxes.IContentBox;
+import org.eclipse.vex.core.internal.boxes.InlineNodeReference;
+import org.eclipse.vex.core.internal.boxes.NodeEndOffsetPlaceholder;
+import org.eclipse.vex.core.internal.boxes.StructuralNodeReference;
+import org.eclipse.vex.core.internal.boxes.TextContent;
import org.eclipse.vex.core.internal.core.Graphics;
import org.eclipse.vex.core.internal.core.Rectangle;
import org.eclipse.vex.core.internal.widget.IViewPort;
@@ -32,9 +38,8 @@
final IContentBox box = contentTopology.findClosestBoxByCoordinates(x, y);
if (box == null) {
return currentOffset;
- }
- if (box.containsCoordinates(x, y)) {
- return box.getOffsetForCoordinates(graphics, x - box.getAbsoluteLeft(), y - box.getAbsoluteTop());
+ } else if (box.containsCoordinates(x, y)) {
+ return findBestOffsetWithin(graphics, box, x, y);
} else if (box.isLeftOf(x)) {
if (isLastEnclosedBox(box)) {
return box.getEndOffset() + 1;
@@ -48,6 +53,34 @@
}
}
+ private static int findBestOffsetWithin(final Graphics graphics, final IContentBox closestBoxByCoordinates, final int x, final int y) {
+ return closestBoxByCoordinates.accept(new BaseBoxVisitorWithResult<Integer>() {
+ @Override
+ public Integer visit(final StructuralNodeReference box) {
+ final IContentBox closestChild = findClosestContentBoxChildAbove(box, x, y);
+ if (closestChild == null) {
+ return box.getStartOffset();
+ }
+ return closestChild.accept(this);
+ }
+
+ @Override
+ public Integer visit(final InlineNodeReference box) {
+ return box.getOffsetForCoordinates(graphics, x - box.getAbsoluteLeft(), y - box.getAbsoluteTop());
+ }
+
+ @Override
+ public Integer visit(final NodeEndOffsetPlaceholder box) {
+ return box.getOffsetForCoordinates(graphics, x - box.getAbsoluteLeft(), y - box.getAbsoluteTop());
+ }
+
+ @Override
+ public Integer visit(final TextContent box) {
+ return box.getOffsetForCoordinates(graphics, x - box.getAbsoluteLeft(), y - box.getAbsoluteTop());
+ }
+ });
+ }
+
private static boolean isLastEnclosedBox(final IContentBox enclosedBox) {
final IContentBox parent = getParentContentBox(enclosedBox);
if (parent == null) {
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveUp.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveUp.java
index b774630..fbf94b2 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveUp.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/cursor/MoveUp.java
@@ -10,13 +10,14 @@
*******************************************************************************/
package org.eclipse.vex.core.internal.cursor;
+import static org.eclipse.vex.core.internal.cursor.ContentTopology.findClosestContentBoxChildAbove;
import static org.eclipse.vex.core.internal.cursor.ContentTopology.findHorizontallyClosestContentBox;
import static org.eclipse.vex.core.internal.cursor.ContentTopology.getParentContentBox;
import static org.eclipse.vex.core.internal.cursor.ContentTopology.horizontalDistance;
+import static org.eclipse.vex.core.internal.cursor.ContentTopology.removeVerticallyDistantBoxes;
import static org.eclipse.vex.core.internal.cursor.ContentTopology.verticalDistance;
import java.util.Collections;
-import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -208,7 +209,7 @@
return currentBox;
}
- final IContentBox childAbove = findClosestContentBoxChildAbove(parent, x, y);
+ final IContentBox childAbove = handleSpecialCaseMovingIntoLastLineOfParagraph(findClosestContentBoxChildAbove(parent, x, y), x, y);
if (childAbove == null) {
return parent.accept(new BaseBoxVisitorWithResult<IContentBox>() {
@Override
@@ -236,61 +237,6 @@
return childAbove;
}
- private static IContentBox findClosestContentBoxChildAbove(final IContentBox parent, final int x, final int y) {
- final Iterable<IContentBox> candidates = findVerticallyClosestContentBoxChildrenAbove(parent, y);
- final IContentBox finalCandidate = findHorizontallyClosestContentBox(candidates, x);
- return handleSpecialCaseMovingIntoLastLineOfParagraph(finalCandidate, x, y);
- }
-
- private static Iterable<IContentBox> findVerticallyClosestContentBoxChildrenAbove(final IContentBox parent, final int y) {
- final LinkedList<IContentBox> candidates = new LinkedList<IContentBox>();
- final int[] minVerticalDistance = new int[1];
- minVerticalDistance[0] = Integer.MAX_VALUE;
- parent.accept(new DepthFirstBoxTraversal<Object>() {
- @Override
- public Object visit(final StructuralNodeReference box) {
- final int distance = verticalDistance(box, y);
- if (box != parent && !box.isAbove(y)) {
- return box;
- }
-
- if (box == parent) {
- super.visit(box);
- }
-
- if (box != parent) {
- candidates.add(box);
- minVerticalDistance[0] = Math.min(distance, minVerticalDistance[0]);
- }
-
- return null;
- }
-
- @Override
- public Object visit(final TextContent box) {
- final int distance = verticalDistance(box, y);
- if (box.isAbove(y) && distance <= minVerticalDistance[0]) {
- candidates.add(box);
- minVerticalDistance[0] = Math.min(distance, minVerticalDistance[0]);
- }
- return null;
- }
-
- @Override
- public Object visit(final NodeEndOffsetPlaceholder box) {
- final int distance = verticalDistance(box, y);
- if (box.isAbove(y) && distance <= minVerticalDistance[0]) {
- candidates.add(box);
- minVerticalDistance[0] = Math.min(distance, minVerticalDistance[0]);
- }
- return null;
- }
- });
-
- removeVerticallyDistantBoxes(candidates, y, minVerticalDistance[0]);
- return candidates;
- }
-
private static IContentBox handleSpecialCaseMovingIntoLastLineOfParagraph(final IContentBox candidate, final int x, final int y) {
if (candidate == null) {
return null;
@@ -358,12 +304,4 @@
return candidates;
}
- private static void removeVerticallyDistantBoxes(final List<? extends IContentBox> boxes, final int y, final int minVerticalDistance) {
- for (final Iterator<? extends IContentBox> iter = boxes.iterator(); iter.hasNext();) {
- final IContentBox candidate = iter.next();
- if (verticalDistance(candidate, y) > minVerticalDistance) {
- iter.remove();
- }
- }
- }
}