blob: 84d5f16f10130952e1a20d6f1e4770188976fb9f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014 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.DepthFirstTraversal;
import org.eclipse.vex.core.internal.boxes.IContentBox;
import org.eclipse.vex.core.internal.boxes.NodeReference;
import org.eclipse.vex.core.internal.boxes.Paragraph;
import org.eclipse.vex.core.internal.boxes.RootBox;
import org.eclipse.vex.core.internal.boxes.TextContent;
/**
* @author Florian Thienel
*/
public class ContentMap {
private RootBox rootBox;
private IContentBox outmostContentBox;
public void setRootBox(final RootBox rootBox) {
this.rootBox = rootBox;
outmostContentBox = findOutmostContentBox();
}
private IContentBox findOutmostContentBox() {
return rootBox.accept(new DepthFirstTraversal<IContentBox>(null) {
@Override
public IContentBox visit(final NodeReference box) {
return box;
}
@Override
public IContentBox visit(final TextContent box) {
return box;
}
});
}
public int getLastPosition() {
if (outmostContentBox == null) {
return 0;
}
return outmostContentBox.getEndOffset();
}
public IContentBox getOutmostContentBox() {
return outmostContentBox;
}
public IContentBox findBoxForPosition(final int offset) {
return rootBox.accept(new DepthFirstTraversal<IContentBox>() {
@Override
public IContentBox visit(final NodeReference box) {
if (box.getStartOffset() == offset || box.getEndOffset() == offset) {
return box;
}
if (box.getStartOffset() < offset && box.getEndOffset() > offset) {
return box.getComponent().accept(this);
}
return null;
}
@Override
public IContentBox visit(final Paragraph box) {
return traverseChildren(box);
}
@Override
public IContentBox visit(final TextContent box) {
if (box.getStartOffset() <= offset && box.getEndOffset() >= offset) {
return box;
}
return null;
}
});
}
public Environment findEnvironmentForCoordinates(final int x, final int y, final boolean preferClosest) {
final IContentBox[] deepestContainer = new IContentBox[1];
final Neighbourhood neighbours = new Neighbourhood();
rootBox.accept(new DepthFirstTraversal<IContentBox>() {
@Override
public IContentBox visit(final NodeReference box) {
super.visit(box);
if (box.isAbove(y)) {
neighbours.setAbove(box, y - box.getAbsoluteTop() - box.getHeight(), preferClosest);
return null;
} else if (box.isBelow(y)) {
neighbours.setBelow(box, box.getAbsoluteTop() - y, preferClosest);
return null;
} else if (box.isRightOf(x)) {
neighbours.setRight(box, box.getAbsoluteLeft() - x, preferClosest);
return null;
} else if (box.isLeftOf(x)) {
neighbours.setLeft(box, x - box.getAbsoluteLeft() - box.getWidth(), preferClosest);
return null;
} else {
if (deepestContainer[0] == null) {
deepestContainer[0] = box;
}
if (neighbours.getBelow().box != null) {
return deepestContainer[0];
} else {
return null;
}
}
}
@Override
public IContentBox visit(final TextContent box) {
if (box.isAbove(y)) {
neighbours.setAbove(box, y - box.getAbsoluteTop() - box.getHeight(), true);
return null;
} else if (box.isBelow(y)) {
neighbours.setBelow(box, box.getAbsoluteTop() - y, true);
return null;
} else if (box.isRightOf(x)) {
neighbours.setRight(box, box.getAbsoluteLeft() - x, true);
return null;
} else if (box.isLeftOf(x)) {
neighbours.setLeft(box, x - box.getAbsoluteLeft() - box.getWidth(), true);
return null;
} else {
deepestContainer[0] = box;
return null;
}
}
});
return new Environment(neighbours, deepestContainer[0]);
}
public static class Neighbour {
public static final Neighbour NULL = new Neighbour(null, Integer.MAX_VALUE);
public final IContentBox box;
public final int distance;
public Neighbour(final IContentBox box, final int distance) {
this.box = box;
this.distance = distance;
}
}
public static class Neighbourhood {
private Neighbour above = Neighbour.NULL;
private Neighbour below = Neighbour.NULL;
private Neighbour left = Neighbour.NULL;
private Neighbour right = Neighbour.NULL;
public void setAbove(final IContentBox box, final int distance, final boolean overwrite) {
if (above == Neighbour.NULL || overwrite && above.distance >= distance) {
above = new Neighbour(box, distance);
}
}
public Neighbour getAbove() {
return above;
}
public void setBelow(final IContentBox box, final int distance, final boolean overwrite) {
if (below == Neighbour.NULL || overwrite && below.distance >= distance) {
below = new Neighbour(box, distance);
}
}
public Neighbour getBelow() {
return below;
}
public void setLeft(final IContentBox box, final int distance, final boolean overwrite) {
if (left == Neighbour.NULL || overwrite && left.distance >= distance) {
left = new Neighbour(box, distance);
}
}
public void setRight(final IContentBox box, final int distance, final boolean overwrite) {
if (right == Neighbour.NULL || overwrite && right.distance >= distance) {
right = new Neighbour(box, distance);
}
}
public Neighbour getClosestOnLine() {
return closest(left, right);
}
private Neighbour closest(final Neighbour neighbour1, final Neighbour neighbour2) {
if (neighbour1.distance < neighbour2.distance) {
return neighbour1;
} else {
return neighbour2;
}
}
}
public static class Environment {
public final Neighbourhood neighbours;
public final IContentBox deepestContainer;
public Environment(final Neighbourhood neighbours, final IContentBox deepestContainer) {
this.neighbours = neighbours;
this.deepestContainer = deepestContainer;
}
}
}