/**
* 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.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.stream.Collectors;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Rectangle;
import org.whole.lang.ui.editparts.IGraphicalEntityPart;
import org.whole.lang.ui.viewers.IEntityPartViewer;
/**
* @author Enrico Persiani
*/
public class FigureUtils {
public static int getPositionOf(IFigure figure, IFigure relativeTo) {
return getApproximatePositionOf(figure, relativeTo, 0.0);
}
public static int getApproximatePositionOf(IFigure figure, IFigure relativeTo, double approx) {
Rectangle bounds = figure.getBounds();
Rectangle relativeToBounds = relativeTo.getBounds();
int hApprox = (int) Math.round(bounds.width * approx);
int vApprox = (int) Math.round(bounds.height * approx);
if (bounds.x + hApprox > relativeToBounds.right())
return PositionConstants.EAST;
else if (bounds.right() - hApprox < relativeToBounds.x)
return PositionConstants.WEST;
else if (bounds.y + vApprox > relativeToBounds.bottom())
return PositionConstants.SOUTH;
else if (bounds.bottom() - vApprox < relativeToBounds.y)
return PositionConstants.NORTH;
else
return PositionConstants.NONE;
}
@SuppressWarnings("unchecked")
public static LinkedList<IGraphicalEntityPart> extractContainerEntityParts(LinkedList<IGraphicalEntityPart> entityParts) {
ListIterator<IGraphicalEntityPart> iterator = entityParts.listIterator();
// replace containers
while (iterator.hasNext()) {
IGraphicalEntityPart entityPart = iterator.next();
if (!entityPart.getChildren().isEmpty()) {
iterator.remove();
LinkedList<IGraphicalEntityPart> children = new LinkedList<IGraphicalEntityPart>(entityPart.getChildren());
for (IGraphicalEntityPart descendantPart : extractContainerEntityParts(children))
iterator.add(descendantPart);
}
}
return entityParts;
}
@SuppressWarnings("unchecked")
public static LinkedList<IGraphicalEntityPart> filterClosestVertically(List<IGraphicalEntityPart> entityParts, boolean above, Rectangle bounds) {
int targetLimit = above ? bounds.y : bounds.bottom();
int closestY = above ? 0 : Integer.MAX_VALUE;
LinkedList<IGraphicalEntityPart> closestVertically = new LinkedList<>();
for (IGraphicalEntityPart entityPart : entityParts) {
if (!entityPart.getFigure().isVisible())
continue;
Rectangle partBounds = entityPart.getFigure().getBounds();
if (partBounds.contains(bounds) && !entityPart.getChildren().isEmpty()) {
LinkedList<IGraphicalEntityPart> filtered = filterClosestVertically(entityPart.getChildren(), above, bounds);
if (!filtered.isEmpty())
return filtered;
} else {
int limit = above ? partBounds.bottom() : partBounds.y;
if ((above && limit >= closestY && limit < targetLimit) ||
(!above && limit <= closestY && limit > targetLimit)) {
if (limit > closestY)
closestVertically.clear();
closestVertically.add(entityPart);
closestY = limit;
}
}
}
return extractContainerEntityParts(closestVertically);
}
@SuppressWarnings("unchecked")
public static LinkedList<IGraphicalEntityPart> filterClosestHorizontally(List<IGraphicalEntityPart> entityParts, boolean before, Rectangle bounds) {
int targetLimit = bounds.x * (before ? 1 : -1);
int closestX = before ? 0 : -Integer.MAX_VALUE;
LinkedList<IGraphicalEntityPart> closestHorizontally = new LinkedList<>();
for (IGraphicalEntityPart entityPart : entityParts) {
Rectangle partBounds = entityPart.getFigure().getBounds();
if (partBounds.contains(bounds) && !entityPart.getChildren().isEmpty()) {
LinkedList<IGraphicalEntityPart> filtered = filterClosestVertically(entityPart.getChildren(), before, bounds);
if (!filtered.isEmpty())
return filtered;
} else {
int limit = before ? partBounds.right() : partBounds.x;
if (limit >= closestX && limit < targetLimit) {
if (limit > closestX)
closestHorizontally.clear();
closestHorizontally.add(entityPart);
closestX = limit;
}
}
}
return extractContainerEntityParts(closestHorizontally);
}
public static IGraphicalEntityPart getClosestWrappingVertically(List<IGraphicalEntityPart> entityParts, boolean above, Rectangle bounds) {
throw new UnsupportedOperationException("not implemented yet");
}
public static IGraphicalEntityPart getClosestWrappingHorizontally(LinkedList<IGraphicalEntityPart> entityParts, boolean above, Rectangle bounds) {
LinkedList<IGraphicalEntityPart> candidates = entityParts.stream()
.filter((p) ->
p.getFigure().getBounds().x < bounds.x && bounds.x <= p.getFigure().getBounds().right())
.collect(Collectors.toCollection(() -> new LinkedList<>()));
candidates.sort((f, s) -> {
Rectangle fb = f.getFigure().getBounds();
Rectangle sb = s.getFigure().getBounds();
return above ? fb.y - sb.y : sb.bottom() - fb.bottom();
});
if (!candidates.isEmpty())
return candidates.getLast();
return entityParts.stream().reduce((f, s) -> {
Rectangle fb = f.getFigure().getBounds();
Rectangle sb = s.getFigure().getBounds();
int df = Math.min(Math.abs(fb.x - bounds.x), Math.abs(fb.right() - bounds.x));
int ds = Math.min(Math.abs(sb.x - bounds.x), Math.abs(sb.right() - bounds.x));
return df < ds ? f : s;
}).orElse(null);
}
public static IGraphicalEntityPart getClosestAbove(IEntityPartViewer viewer, IGraphicalEntityPart entityPart) {
return getClosestAbove(viewer, entityPart.getFigure());
}
public static IGraphicalEntityPart getClosestAbove(IEntityPartViewer viewer, IFigure figure) {
return getClosestAbove(viewer, figure.getBounds());
}
@SuppressWarnings("unchecked")
public static IGraphicalEntityPart getClosestAbove(IEntityPartViewer viewer, Rectangle bounds) {
return getClosestAbove(viewer.getContents().getChildren(), bounds);
}
public static IGraphicalEntityPart getClosestAbove(List<IGraphicalEntityPart> entityParts, Rectangle bounds) {
if (entityParts.isEmpty())
throw new IllegalArgumentException("empty list");
return getClosestWrappingHorizontally(filterClosestVertically(entityParts, true, bounds), true, bounds);
}
public static IGraphicalEntityPart getClosestBelow(IEntityPartViewer viewer, IGraphicalEntityPart entityPart) {
return getClosestBelow(viewer, entityPart.getFigure());
}
public static IGraphicalEntityPart getClosestBelow(IEntityPartViewer viewer, IFigure figure) {
return getClosestBelow(viewer, figure.getBounds());
}
@SuppressWarnings("unchecked")
public static IGraphicalEntityPart getClosestBelow(IEntityPartViewer viewer, Rectangle bounds) {
return getClosestBelow(viewer.getContents().getChildren(), bounds);
}
public static IGraphicalEntityPart getClosestBelow(List<IGraphicalEntityPart> entityParts, Rectangle bounds) {
if (entityParts.isEmpty())
throw new IllegalArgumentException("empty list");
return getClosestWrappingHorizontally(filterClosestVertically(entityParts, false, bounds), false, bounds);
}
public static IGraphicalEntityPart getClosest(int inDirection, IEntityPartViewer viewer, IGraphicalEntityPart entityPart) {
return getClosest(inDirection, viewer, entityPart.getFigure());
}
public static IGraphicalEntityPart getClosest(int inDirection, IEntityPartViewer viewer, IFigure figure) {
return getClosest(inDirection, viewer, figure.getBounds());
}
@SuppressWarnings("unchecked")
public static IGraphicalEntityPart getClosest(int inDirection, IEntityPartViewer viewer, Rectangle bounds) {
return getClosest(inDirection, viewer.getRootEditPart().getChildren(), bounds);
}
public static IGraphicalEntityPart getClosest(int inDirection, List<IGraphicalEntityPart> entityParts, Rectangle bounds) {
throw new UnsupportedOperationException();
}
}