blob: 63287a45c15de6878d146d433c077a7b45188990 [file] [log] [blame]
/*******************************************************************************
* 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.BaseBoxVisitorWithResult;
import org.eclipse.vex.core.internal.boxes.DepthFirstBoxTraversal;
import org.eclipse.vex.core.internal.boxes.IBox;
import org.eclipse.vex.core.internal.boxes.IContentBox;
import org.eclipse.vex.core.internal.boxes.IInlineBox;
import org.eclipse.vex.core.internal.boxes.IParentBox;
import org.eclipse.vex.core.internal.boxes.InlineNodeReference;
import org.eclipse.vex.core.internal.boxes.NodeEndOffsetPlaceholder;
import org.eclipse.vex.core.internal.boxes.Paragraph;
import org.eclipse.vex.core.internal.boxes.ParentTraversal;
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;
public class MoveToLineStart implements ICursorMove {
@Override
public int calculateNewOffset(final Graphics graphics, final IViewPort viewPort, final ContentTopology contentTopology, final int currentOffset, final IContentBox currentBox, final Rectangle hotArea, final int preferredX) {
return currentBox.accept(new BaseBoxVisitorWithResult<Integer>(currentOffset) {
@Override
public Integer visit(final StructuralNodeReference box) {
final Paragraph containedParagraph = getContainedParagraph(box);
if (containedParagraph != null) {
if (currentOffset == box.getEndOffset()) {
return getFirstOffsetInLastLine(containedParagraph, currentOffset);
}
}
return super.visit(box);
}
@Override
public Integer visit(final InlineNodeReference box) {
return getFirstOffsetInLine(box, currentOffset);
}
@Override
public Integer visit(final NodeEndOffsetPlaceholder box) {
return getFirstOffsetInLine(box, currentOffset);
}
@Override
public Integer visit(final TextContent box) {
return getFirstOffsetInLine(box, currentOffset);
}
});
}
private static Paragraph getContainedParagraph(final StructuralNodeReference nodeReference) {
return nodeReference.accept(new DepthFirstBoxTraversal<Paragraph>() {
@Override
public Paragraph visit(final StructuralNodeReference box) {
if (box == nodeReference) {
return super.visit(box);
}
return null;
}
@Override
public Paragraph visit(final Paragraph box) {
return box;
}
});
}
private static int getFirstOffsetInLastLine(final Paragraph box, final int defaultValue) {
int currentBaseline = 0;
IContentBox firstChildOnLine = null;
for (final IInlineBox child : box.getChildren()) {
final int baseline = getAbsoluteBaseline(child);
if (baseline > currentBaseline && child instanceof IContentBox) {
currentBaseline = baseline;
firstChildOnLine = (IContentBox) child;
}
}
if (firstChildOnLine == null) {
return defaultValue;
}
return firstChildOnLine.getStartOffset();
}
private static int getFirstOffsetInLine(final IInlineBox box, final int defaultValue) {
final int targetBaseline = getAbsoluteBaseline(box);
final IParentBox<IInlineBox> structuralParent = getStructuralParent(box);
final IContentBox firstBoxInLine = findFirstContentBoxOnTargetBaseline(structuralParent, targetBaseline);
if (firstBoxInLine == null) {
return defaultValue;
}
return firstBoxInLine.getStartOffset();
}
private static int getAbsoluteBaseline(final IInlineBox box) {
return box.getAbsoluteTop() + box.getBaseline();
}
private static IContentBox findFirstContentBoxOnTargetBaseline(final IParentBox<IInlineBox> structuralParent, final int targetBaseline) {
for (final IInlineBox child : structuralParent.getChildren()) {
if (targetBaseline == getAbsoluteBaseline(child)) {
final IContentBox contentBox = findFirstContentBox(child);
if (contentBox != null) {
return contentBox;
}
}
}
return null;
}
private static IContentBox findFirstContentBox(final IInlineBox box) {
return box.accept(new DepthFirstBoxTraversal<IContentBox>() {
@Override
public IContentBox visit(final InlineNodeReference box) {
return box;
}
@Override
public IContentBox visit(final NodeEndOffsetPlaceholder box) {
return box;
}
@Override
public IContentBox visit(final TextContent box) {
return box;
}
});
}
private static IParentBox<IInlineBox> getStructuralParent(final IBox box) {
return box.accept(new ParentTraversal<IParentBox<IInlineBox>>(null) {
@Override
public IParentBox<IInlineBox> visit(final Paragraph box) {
return box;
}
});
}
@Override
public boolean preferX() {
return true;
}
@Override
public boolean isAbsolute() {
return true;
}
}