/**
* Copyright 2004-2016 Riccardo Solmi. All rights reserved.
* This file is part of the Whole Platform.
*
* The Whole Platform is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Whole Platform is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whole.lang.ui.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.draw2d.FigureCanvas;
import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.Viewport;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Caret;
import org.whole.lang.model.IEntity;
import org.whole.lang.ui.editparts.IEntityPart;
import org.whole.lang.ui.editparts.ITextualEntityPart;
import org.whole.lang.ui.figures.ITextualFigure;
import org.whole.lang.ui.viewers.IEntityPartViewer;
/**
* @author Enrico Persiani
*/
public class CaretUtils {
private static final int REAVEAL_SIZE = 50;
private static final int CARET_WIDTH = 2;
public static Caret getCaret(EditPartViewer viewer) {
Caret caret = null;
if (viewer != null) {
Canvas canvas = (Canvas)viewer.getControl();
if (canvas != null) {
caret = canvas.getCaret();
if (caret == null)
caret = new Caret(canvas, 0);
}
}
return caret;
}
public static org.eclipse.draw2d.geometry.Rectangle getAbsoluteCaretBounds(EditPartViewer viewer) {
return new org.eclipse.draw2d.geometry.Rectangle(CaretUtils.getCaret(viewer).getBounds());
}
public static Rectangle getAbsoluteCaretBounds(IFigure figure, org.eclipse.draw2d.geometry.Rectangle draw2dRect) {
Rectangle rect = new Rectangle(0, 0, 0, 0);
figure.translateToAbsolute(draw2dRect);
rect.x = draw2dRect.x;
rect.y = draw2dRect.y;
rect.width = draw2dRect.width;
rect.height = draw2dRect.height;
return rect;
}
public static org.eclipse.draw2d.geometry.Rectangle getRelativeCaretBounds(IFigure figure, Rectangle rect) {
org.eclipse.draw2d.geometry.Rectangle draw2dRect = new org.eclipse.draw2d.geometry.Rectangle();
draw2dRect.x = rect.x;
draw2dRect.y = rect.y;
draw2dRect.width = rect.width;
draw2dRect.height = rect.height;
figure.translateToRelative(draw2dRect);
return draw2dRect;
}
public static void revealCaret(Caret caret) {
if (caret == null || !caret.isVisible() || !(caret.getParent() instanceof FigureCanvas))
return;
FigureCanvas figureCanvas = (FigureCanvas) caret.getParent();
Viewport viewport = figureCanvas.getViewport();
org.eclipse.draw2d.geometry.Rectangle viewableArea =
viewport.getClientArea().getCopy().setLocation(viewport.getViewLocation());
org.eclipse.draw2d.geometry.Rectangle caretBounds =
new org.eclipse.draw2d.geometry.Rectangle(caret.getBounds());
viewport.getContents().translateToRelative(caretBounds);
if (!viewableArea.contains(caretBounds)) {
Point scrollTo = viewableArea.getLocation();
if (caretBounds.x <= viewableArea.x)
scrollTo.x = caretBounds.x - REAVEAL_SIZE;
else if (caretBounds.right() >= viewableArea.right())
scrollTo.x = viewableArea.x + caretBounds.right() - viewableArea.right() + REAVEAL_SIZE;
if (caretBounds.y < viewableArea.y)
scrollTo.y = caretBounds.y - REAVEAL_SIZE;
else if (caretBounds.bottom() > viewableArea.bottom())
scrollTo.y = viewableArea.y + caretBounds.bottom() - viewableArea.bottom() + REAVEAL_SIZE;
figureCanvas.scrollTo(scrollTo.x, scrollTo.y);
}
}
public static Dimension getCaretSize(Font font) {
FontMetrics fontMetrics = FigureUtilities.getFontMetrics(font);
return new Dimension(CARET_WIDTH, fontMetrics.getHeight());
}
// begin of multiline positioning methods
private static final Pattern lt = Pattern.compile("[\\n\\r]|\\r\\n");
public static int getCaretLines(String text) {
Matcher matcher = lt.matcher(text);
int lines = 1;
while (matcher.find()) {
lines++;
}
return lines;
}
public static int getCaretPositions(IEntityPartViewer viewer, IEntity entity) {
IEntityPart entityPart = viewer.getEditPartRegistry().get(entity);
if (!(entityPart instanceof ITextualEntityPart))
throw new IllegalArgumentException("the entity part is not textual");
ITextualEntityPart targetPart = (ITextualEntityPart) entityPart;
return targetPart.getCaretPositions();
}
public static int getCaretLine(Point proximityPoint, org.eclipse.draw2d.geometry.Rectangle labelBounds, Dimension caretSize) {
return (proximityPoint.y - labelBounds.y) / caretSize.height;
}
public static int getStartingLinePosition(String text, int line) {
return line == 0 ? 0 : getEndingLinePosition(text, line-1)+1;
}
public static int getEndingLinePosition(String text, int line) {
int labelLines = getCaretLines(text);
if (line >= labelLines)
return -1;
if (line == labelLines-1)
return text.length();
Matcher matcher = lt.matcher(text);
int index = -1;
while (matcher.find() && line >= 0) {
index = matcher.start();
line--;
}
return index;
}
public static String getLine(String text, int line) {
try {
return text.substring(getStartingLinePosition(text, line), getEndingLinePosition(text, line));
} catch (StringIndexOutOfBoundsException e) {
return text;//FIXME workaround
}
}
public static int getLineFromPosition(String text, int position) {
int lines = getCaretLines(text);
int index = -1;
for (int i=0; i<lines; i++) {
index = getEndingLinePosition(text, i);
if (position <= index)
return i;
}
return -1;
}
// end of multiline positioning methods
//FIXME alternative methods added for backward compatibility
public static org.eclipse.draw2d.geometry.Rectangle getAbsoluteCaretBounds(IEntityPartViewer viewer, ITextualEntityPart targetPart) {
ITextualFigure textualFigure = targetPart.getTextualFigure();
Viewport viewport = ((FigureCanvas) viewer.getControl()).getViewport();
org.eclipse.draw2d.geometry.Rectangle caretBounds = textualFigure.getCaretBounds().getCopy();
viewport.getContents().translateToRelative(caretBounds);
return caretBounds;
}
public static int calculateCaretPosition(ITextualEntityPart targetPart, int horizontalLocation) {
ITextualFigure textualFigure = targetPart.getTextualFigure();
Point.SINGLETON.x = horizontalLocation;
textualFigure.translateToRelative(Point.SINGLETON);
horizontalLocation = Point.SINGLETON.x;
Label label = textualFigure.getEmbeddedLabel();
String text = textualFigure.getText();
Font font = textualFigure.getEmbeddedLabelFont();
int availableWidth = horizontalLocation - textualFigure.getTextBounds().x;
int length = label.getTextUtilities().getLargestSubstringConfinedTo(text, font, availableWidth+3);
return CaretUtils.getStartingLinePosition(text, getCaretLines(text)-1)+length;
}
}