/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.fge;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.antar.binding.BindingDefinition;
import org.openflexo.antar.binding.BindingDefinition.BindingDefinitionType;
import org.openflexo.antar.binding.TypeUtils;
import org.openflexo.fge.controller.DrawingController;
import org.openflexo.fge.controller.MouseClickControl;
import org.openflexo.fge.controller.MouseClickControlAction.MouseClickControlActionType;
import org.openflexo.fge.controller.MouseControl.MouseButton;
import org.openflexo.fge.controller.MouseDragControl;
import org.openflexo.fge.controller.MouseDragControlAction.MouseDragControlActionType;
import org.openflexo.fge.cp.ControlArea;
import org.openflexo.fge.cp.ControlPoint;
import org.openflexo.fge.geom.FGEDimension;
import org.openflexo.fge.geom.FGEGeometricObject;
import org.openflexo.fge.geom.FGEGeometricObject.Filling;
import org.openflexo.fge.geom.FGEGeometricObject.SimplifiedCardinalDirection;
import org.openflexo.fge.geom.FGEPoint;
import org.openflexo.fge.geom.FGERectangle;
import org.openflexo.fge.geom.FGESegment;
import org.openflexo.fge.geom.FGESteppedDimensionConstraint;
import org.openflexo.fge.geom.area.FGEArea;
import org.openflexo.fge.geom.area.FGEIntersectionArea;
import org.openflexo.fge.graphics.BackgroundStyle;
import org.openflexo.fge.graphics.BackgroundStyle.BackgroundStyleType;
import org.openflexo.fge.graphics.DecorationPainter;
import org.openflexo.fge.graphics.FGEShapeDecorationGraphics;
import org.openflexo.fge.graphics.FGEShapeGraphics;
import org.openflexo.fge.graphics.ForegroundStyle;
import org.openflexo.fge.graphics.ShadowStyle;
import org.openflexo.fge.graphics.ShapePainter;
import org.openflexo.fge.graphics.TextStyle;
import org.openflexo.fge.notifications.FGENotification;
import org.openflexo.fge.notifications.ObjectHasMoved;
import org.openflexo.fge.notifications.ObjectHasResized;
import org.openflexo.fge.notifications.ObjectMove;
import org.openflexo.fge.notifications.ObjectResized;
import org.openflexo.fge.notifications.ObjectWillMove;
import org.openflexo.fge.notifications.ObjectWillResize;
import org.openflexo.fge.notifications.ShapeChanged;
import org.openflexo.fge.notifications.ShapeNeedsToBeRedrawn;
import org.openflexo.fge.shapes.Shape;
import org.openflexo.fge.shapes.Shape.ShapeType;
import org.openflexo.fge.view.ShapeView;
import org.openflexo.kvc.KVCObject;
import org.openflexo.toolbox.ToolBox;
import org.openflexo.xmlcode.XMLSerializable;
public class ShapeGraphicalRepresentation<O> extends GraphicalRepresentation<O> implements Observer {
private static final Logger logger = Logger.getLogger(ShapeGraphicalRepresentation.class.getPackage().getName());
// *******************************************************************************
// * Parameters *
// *******************************************************************************
public static enum Parameters implements GRParameter {
x,
y,
width,
height,
minimalWidth,
minimalHeight,
maximalWidth,
maximalHeight,
dimensionConstraintStep,
locationConstraints,
locationConstrainedArea,
dimensionConstraints,
adjustMinimalWidthToLabelWidth,
adjustMinimalHeightToLabelHeight,
adjustMaximalWidthToLabelWidth,
adjustMaximalHeightToLabelHeight,
foreground,
background,
selectedForeground,
selectedBackground,
focusedForeground,
focusedBackground,
hasSelectedForeground,
hasSelectedBackground,
hasFocusedForeground,
hasFocusedBackground,
border,
shapeType,
shape,
shadowStyle,
isFloatingLabel,
relativeTextX,
relativeTextY,
decorationPainter,
shapePainter,
specificStroke,
allowToLeaveBounds,
adaptBoundsToContents,
xConstraints,
yConstraints,
widthConstraints,
heightConstraints;
}
private double x = 0;
private double y = 0;
private double width = 1;
private double height = 1;
private double minimalWidth = 0;
private double minimalHeight = 0;
private double maximalWidth = Double.POSITIVE_INFINITY;
private double maximalHeight = Double.POSITIVE_INFINITY;
private DimensionConstraints dimensionConstraints = DimensionConstraints.FREELY_RESIZABLE;
private FGESteppedDimensionConstraint dimensionConstraintStep = null;
private boolean adjustMinimalWidthToLabelWidth = true;
private boolean adjustMinimalHeightToLabelHeight = true;
private boolean adjustMaximalWidthToLabelWidth = false;
private boolean adjustMaximalHeightToLabelHeight = false;
private LocationConstraints locationConstraints = LocationConstraints.FREELY_MOVABLE;
private FGEArea locationConstrainedArea = null;
private ForegroundStyle foreground;
private BackgroundStyle background;
private ForegroundStyle selectedForeground = null;
private BackgroundStyle selectedBackground = null;
private ForegroundStyle focusedForeground = null;
private BackgroundStyle focusedBackground = null;
private boolean hasSelectedForeground = false;
private boolean hasSelectedBackground = false;
private boolean hasFocusedForeground = false;
private boolean hasFocusedBackground = false;
private ShapeBorder border = new ShapeBorder();
private Shape shape = null;
private ShadowStyle shadowStyle = ShadowStyle.makeDefault();
private boolean allowToLeaveBounds = true;
private boolean adaptBoundsToContents = false;
/*private boolean drawShadow = true;
private int shadowDarkness = FGEConstants.DEFAULT_SHADOW_DARKNESS;
private int shadowDeep = FGEConstants.DEFAULT_SHADOW_DEEP;
private int shadowBlur = FGEConstants.DEFAULT_SHADOW_BLUR;*/
private boolean isFloatingLabel = true;
private double relativeTextX = 0.5;
private double relativeTextY = 0.5;
private boolean isResizing = false;
private boolean isMoving = false;
// *******************************************************************************
// * Geometry constraints *
// *******************************************************************************
public static enum DimensionConstraints {
FREELY_RESIZABLE, UNRESIZABLE, CONSTRAINED_DIMENSIONS, STEP_CONSTRAINED, WIDTH_FIXED, HEIGHT_FIXED, CONTAINER
}
public static enum LocationConstraints {
/**
* Shape is freely relocatable in parent global bounds (rectangular bounds, don't care about borders nor shape of parent)
*/
FREELY_MOVABLE,
/**
* Shape is freely relocatable in parent exact bounds (shape of this GR must be fully located INSIDE parent GR outline)
*/
CONTAINED_IN_SHAPE,
/**
* Shape is not movable
*/
UNMOVABLE, RELATIVE_TO_PARENT, X_FIXED, Y_FIXED, AREA_CONSTRAINED;
}
// *******************************************************************************
// * Border *
// *******************************************************************************
public static class ShapeBorder extends KVCObject implements XMLSerializable, Cloneable {
public int top;
public int bottom;
public int left;
public int right;
public ShapeBorder() {
super();
this.top = FGEConstants.DEFAULT_BORDER_SIZE;
this.bottom = FGEConstants.DEFAULT_BORDER_SIZE;
this.left = FGEConstants.DEFAULT_BORDER_SIZE;
this.right = FGEConstants.DEFAULT_BORDER_SIZE;
}
public ShapeBorder(int top, int bottom, int left, int right) {
super();
this.top = top;
this.bottom = bottom;
this.left = left;
this.right = right;
}
public ShapeBorder(ShapeBorder border) {
super();
this.top = border.top;
this.bottom = border.bottom;
this.left = border.left;
this.right = border.right;
}
@Override
public ShapeBorder clone() {
try {
return (ShapeBorder) super.clone();
} catch (CloneNotSupportedException e) {
// cannot happen, we are clonable
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "ShapeBorder [" + left + "," + top + "," + right + "," + bottom + "]";
}
}
private FGEShapeGraphics graphics;
private FGEShapeDecorationGraphics decorationGraphics;
private DecorationPainter decorationPainter;
private ShapePainter shapePainter;
// *******************************************************************************
// * Constructor *
// *******************************************************************************
// Never use this constructor (used during deserialization only)
public ShapeGraphicalRepresentation() {
this(null, null);
}
private ShapeGraphicalRepresentation(O aDrawable, Drawing<?> aDrawing) {
super(aDrawable, aDrawing);
}
public ShapeGraphicalRepresentation(ShapeType shapeType, O aDrawable, Drawing<?> aDrawing) {
this(aDrawable, aDrawing);
setShapeType(shapeType);
layer = FGEConstants.DEFAULT_SHAPE_LAYER;
foreground = ForegroundStyle.makeDefault();
// foreground.setGraphicalRepresentation(this);
foreground.addObserver(this);
background = BackgroundStyle.makeColoredBackground(Color.WHITE);
// background.setGraphicalRepresentation(this);
background.addObserver(this);
graphics = new FGEShapeGraphics(this);
addToMouseClickControls(MouseClickControl.makeMouseClickControl("Selection", MouseButton.LEFT, 1,
MouseClickControlActionType.SELECTION));
if (ToolBox.getPLATFORM() == ToolBox.MACOS) {
addToMouseClickControls(MouseClickControl.makeMouseMetaClickControl("Multiple selection", MouseButton.LEFT, 1,
MouseClickControlActionType.MULTIPLE_SELECTION));
} else {
addToMouseClickControls(MouseClickControl.makeMouseControlClickControl("Multiple selection", MouseButton.LEFT, 1,
MouseClickControlActionType.MULTIPLE_SELECTION));
}
addToMouseDragControls(MouseDragControl.makeMouseDragControl("Move", MouseButton.LEFT, MouseDragControlActionType.MOVE));
addToMouseDragControls(MouseDragControl.makeMouseShiftDragControl("Rectangle selection", MouseButton.LEFT,
MouseDragControlActionType.RECTANGLE_SELECTING));
}
public ShapeGraphicalRepresentation(ShapeGraphicalRepresentation<?> aGR, O aDrawable, Drawing<?> aDrawing) {
this(aDrawable, aDrawing);
setsWith(aGR);
// setShape(aGR.getShape().clone());
// getShape().setGraphicalRepresentation(this);
// getShape().rebuildControlPoints();
graphics = new FGEShapeGraphics(this);
addToMouseClickControls(MouseClickControl.makeMouseClickControl("Selection", MouseButton.LEFT, 1,
MouseClickControlActionType.SELECTION));
if (ToolBox.getPLATFORM() == ToolBox.MACOS) {
addToMouseClickControls(MouseClickControl.makeMouseMetaClickControl("Multiple selection", MouseButton.LEFT, 1,
MouseClickControlActionType.MULTIPLE_SELECTION));
} else {
addToMouseClickControls(MouseClickControl.makeMouseControlClickControl("Multiple selection", MouseButton.LEFT, 1,
MouseClickControlActionType.MULTIPLE_SELECTION));
}
addToMouseDragControls(MouseDragControl.makeMouseDragControl("Move", MouseButton.LEFT, MouseDragControlActionType.MOVE));
addToMouseDragControls(MouseDragControl.makeMouseDragControl("Zoom", MouseButton.RIGHT, MouseDragControlActionType.ZOOM));
addToMouseDragControls(MouseDragControl.makeMouseShiftDragControl("Rectangle selection", MouseButton.LEFT,
MouseDragControlActionType.RECTANGLE_SELECTING));
}
@Override
public ShapeGraphicalRepresentation<O> clone() {
// logger.info("La GR "+this+" se fait cloner la");
try {
return (ShapeGraphicalRepresentation<O>) super.clone();
} catch (CloneNotSupportedException e) {
// cannot happen since we are clonable
e.printStackTrace();
return null;
}
}
@Override
public Vector<GRParameter> getAllParameters() {
Vector<GRParameter> returned = super.getAllParameters();
Parameters[] allParams = Parameters.values();
for (int i = 0; i < allParams.length; i++) {
returned.add(allParams[i]);
}
return returned;
}
// ***************************************************************************
// * Deletion *
// ***************************************************************************
@Override
public void delete() {
if (background != null) {
background.deleteObserver(this);
}
if (foreground != null) {
foreground.deleteObserver(this);
}
if (selectedBackground != null) {
selectedBackground.deleteObserver(this);
}
if (selectedForeground != null) {
selectedForeground.deleteObserver(this);
}
if (focusedBackground != null) {
focusedBackground.deleteObserver(this);
}
if (focusedForeground != null) {
focusedForeground.deleteObserver(this);
}
if (shadowStyle != null) {
shadowStyle.deleteObserver(this);
}
super.delete();
}
// ***************************************************************************
// * Cloning *
// ***************************************************************************
@Override
public final void setsWith(GraphicalRepresentation<?> gr) {
super.setsWith(gr);
if (gr instanceof ShapeGraphicalRepresentation) {
for (Parameters p : Parameters.values()) {
if (p != Parameters.shape && p != Parameters.shapeType) {
_setParameterValueWith(p, gr);
}
}
Shape shapeToCopy = ((ShapeGraphicalRepresentation<?>) gr).getShape();
Shape clone = shapeToCopy.clone();
setShape(clone);
}
}
@Override
public final void setsWith(GraphicalRepresentation<?> gr, GRParameter... exceptedParameters) {
super.setsWith(gr, exceptedParameters);
if (gr instanceof ShapeGraphicalRepresentation) {
for (Parameters p : Parameters.values()) {
boolean excepted = false;
for (GRParameter ep : exceptedParameters) {
if (p == ep) {
excepted = true;
}
}
if (p != Parameters.shape && p != Parameters.shapeType && !excepted) {
_setParameterValueWith(p, gr);
}
}
Shape shapeToCopy = ((ShapeGraphicalRepresentation<?>) gr).getShape();
Shape clone = shapeToCopy.clone();
setShape(clone);
}
}
// *******************************************************************************
// * Observer implementation *
// *******************************************************************************
// This might be very dangerous to override this (this has been done in the past)
// SGU: Developer really need to think about what he's doing while overriding this
@Override
public void update(Observable observable, Object notification) {
// System.out.println("Shape received "+notification+" from "+observable);
super.update(observable, notification);
if (observeParentGRBecauseMyLocationReferToIt && observable == getContainerGraphicalRepresentation()) {
if (notification instanceof ObjectWillMove || notification instanceof ObjectWillResize
|| notification instanceof ObjectHasMoved || notification instanceof ObjectHasResized
|| notification instanceof ObjectMove || notification instanceof ObjectResized || notification instanceof ShapeChanged) {
checkAndUpdateLocationIfRequired();
}
}
if (observable instanceof BackgroundStyle) {
notifyAttributeChange(Parameters.background);
}
if (observable instanceof ForegroundStyle) {
notifyAttributeChange(Parameters.foreground);
}
if (observable instanceof ShadowStyle) {
notifyAttributeChange(Parameters.shadowStyle);
}
}
// *******************************************************************************
// * Location management *
// *******************************************************************************
public void extendParentBoundsToHostThisShape() {
if (getParentGraphicalRepresentation() instanceof ShapeGraphicalRepresentation) {
ShapeGraphicalRepresentation<?> parent = (ShapeGraphicalRepresentation) getParentGraphicalRepresentation();
parent.extendBoundsToHostContents();
}
}
public void extendParentBoundsToHostThisShape2() {
// System.out.println("parent=" + getParentGraphicalRepresentation());
if (getParentGraphicalRepresentation() instanceof ShapeGraphicalRepresentation) {
ShapeGraphicalRepresentation<?> parent = (ShapeGraphicalRepresentation) getParentGraphicalRepresentation();
// parent.updateRequiredBoundsForChildGRLocation(this, new FGEPoint(x, y));
boolean parentNeedsResize = false;
FGEDimension newDimension = new FGEDimension(parent.getWidth(), parent.getHeight());
boolean parentNeedsRelocate = false;
FGEPoint newPosition = new FGEPoint(parent.getX(), parent.getY());
double deltaX = 0;
double deltaY = 0;
if (x < 0) {
newPosition.x = newPosition.x + x;
parentNeedsRelocate = true;
deltaX = -x;
}
if (y < 0) {
newPosition.y = newPosition.y + y;
parentNeedsRelocate = true;
deltaY = -y;
}
if (parentNeedsRelocate) {
parent.setLocation(newPosition);
setLocation(new FGEPoint(getX() - deltaX, getY() - deltaY));
parentNeedsResize = true;
newDimension = new FGEDimension(parent.getWidth() + deltaX, parent.getHeight() + deltaY);
}
if (x + getWidth() > parent.getWidth()) {
newDimension.width = x + getWidth();
parentNeedsResize = true;
}
if (y + getHeight() > parent.getHeight()) {
newDimension.height = y + getHeight();
parentNeedsResize = true;
}
if (parentNeedsResize) {
parent.setSize(newDimension);
}
if (parentNeedsRelocate) {
for (GraphicalRepresentation<?> child : parent.getContainedGraphicalRepresentations()) {
if (child instanceof ShapeGraphicalRepresentation && child != this) {
ShapeGraphicalRepresentation<?> c = (ShapeGraphicalRepresentation<?>) child;
c.setLocation(new FGEPoint(c.getX() + deltaX, c.getY() + deltaY));
}
}
}
if (parentNeedsRelocate || parentNeedsResize) {
FGEPoint oldLocation = new FGEPoint(x, y);
notifyObjectMoved(oldLocation);
notifyChange(Parameters.x, oldLocation.x, getX());
notifyChange(Parameters.y, oldLocation.y, getY());
}
}
}
/**
* Check and eventually relocate and resize current graphical representation in order to all all contained shape graphical
* representations. Contained graphical representations may substantically be relocated.
*/
public void extendBoundsToHostContents() {
// logger.info("Hop: extendBoundsToHostContents");
boolean needsResize = false;
FGEDimension newDimension = new FGEDimension(getWidth(), getHeight());
boolean needsRelocate = false;
FGEPoint newPosition = new FGEPoint(getX(), getY());
double deltaX = 0;
double deltaY = 0;
// First compute the delta to be applied (max of all required delta)
for (GraphicalRepresentation<?> child : getContainedGraphicalRepresentations()) {
if (child instanceof ShapeGraphicalRepresentation) {
ShapeGraphicalRepresentation<?> gr = (ShapeGraphicalRepresentation<?>) child;
if (gr.getX() < -deltaX) {
deltaX = -gr.getX();
needsRelocate = true;
}
if (gr.getY() < -deltaY) {
deltaY = -gr.getY();
needsRelocate = true;
}
}
}
// Relocate
if (needsRelocate) {
System.out.println("Relocate with deltaX=" + deltaX + " deltaY=" + deltaY);
newPosition.x = newPosition.x - deltaX;
newPosition.y = newPosition.y - deltaY;
setLocation(newPosition);
needsResize = true;
newDimension = new FGEDimension(getWidth() + deltaX, getHeight() + deltaY);
for (GraphicalRepresentation<?> child : getContainedGraphicalRepresentations()) {
if (child instanceof ShapeGraphicalRepresentation && child != this) {
ShapeGraphicalRepresentation<?> c = (ShapeGraphicalRepresentation<?>) child;
c.setLocation(new FGEPoint(c.getX() + deltaX, c.getY() + deltaY));
}
}
}
// First compute the resize to be applied
for (GraphicalRepresentation<?> child : getContainedGraphicalRepresentations()) {
if (child instanceof ShapeGraphicalRepresentation) {
ShapeGraphicalRepresentation<?> gr = (ShapeGraphicalRepresentation<?>) child;
if (gr.getX() + gr.getWidth() > getWidth()) {
newDimension.width = gr.x + gr.getWidth();
needsResize = true;
}
if (gr.y + gr.getHeight() > getHeight()) {
newDimension.height = gr.y + gr.getHeight();
needsResize = true;
}
}
}
if (needsResize) {
System.out.println("Resize to " + newDimension);
setSize(newDimension);
}
/*if (needsRelocate || needsResize) {
for (GraphicalRepresentation<?> child : getContainedGraphicalRepresentations()) {
if (child instanceof ShapeGraphicalRepresentation) {
ShapeGraphicalRepresentation<?> c = (ShapeGraphicalRepresentation<?>) child;
FGEPoint oldLocation = new FGEPoint(c.getX() - deltaX, c.getY() - deltaY);
c.notifyObjectMoved(oldLocation);
c.notifyChange(Parameters.x, oldLocation.x, c.getX());
c.notifyChange(Parameters.y, oldLocation.y, c.getY());
}
}
}*/
}
public double getX() {
// SGU: in general case, this is NOT forbidden
if (!getAllowToLeaveBounds()) {
if (x < 0) {
return 0;
}
double maxX = 0;
if (getContainerGraphicalRepresentation() instanceof DrawingGraphicalRepresentation) {
maxX = ((DrawingGraphicalRepresentation<?>) getContainerGraphicalRepresentation()).getWidth();
} else if (getContainerGraphicalRepresentation() instanceof ShapeGraphicalRepresentation) {
maxX = ((ShapeGraphicalRepresentation<?>) getContainerGraphicalRepresentation()).getWidth();
}
if (maxX > 0 && x > maxX - getWidth()) {
// logger.info("Relocate x from "+x+" to "+(maxX-getWidth())+" maxX="+maxX+" width="+getWidth());
return maxX - getWidth();
}
}
return x;
}
public final void setX(double aValue) {
FGENotification notification = requireChange(Parameters.x, aValue);
if (notification != null) {
FGEPoint oldLocation = getLocation();
setXNoNotification(aValue);
hasChanged(notification);
notifyObjectMoved(oldLocation);
}
}
public void setXNoNotification(double aValue) {
x = aValue;
}
public double getY() {
// SGU: in general case, this is NOT forbidden
if (!getAllowToLeaveBounds()) {
if (y < 0) {
return 0;
}
double maxY = 0;
if (getContainerGraphicalRepresentation() instanceof DrawingGraphicalRepresentation) {
maxY = ((DrawingGraphicalRepresentation<?>) getContainerGraphicalRepresentation()).getHeight();
} else if (getContainerGraphicalRepresentation() instanceof ShapeGraphicalRepresentation) {
maxY = ((ShapeGraphicalRepresentation<?>) getContainerGraphicalRepresentation()).getHeight();
}
if (maxY > 0 && y > maxY - getHeight()) {
// logger.info("Relocate y from " + y + " to " + (maxY - getHeight()) + " maxY=" + maxY + " height=" + getHeight());
return maxY - getHeight();
}
}
return y;
}
public final void setY(double aValue) {
FGENotification notification = requireChange(Parameters.y, aValue);
if (notification != null) {
FGEPoint oldLocation = getLocation();
setYNoNotification(aValue);
hasChanged(notification);
notifyObjectMoved(oldLocation);
}
}
public void setYNoNotification(double aValue) {
y = aValue;
}
public FGEPoint getLocation() {
return new FGEPoint(getX(), getY());
}
public void setLocation(FGEPoint newLocation) {
if (newLocation == null) {
return;
}
newLocation = computeConstrainedLocation(newLocation);
FGEPoint oldLocation = getLocation();
if (!newLocation.equals(oldLocation)) {
double oldX = getX();
double oldY = getY();
if (isParentLayoutedAsContainer()) {
setLocationForContainerLayout(newLocation);
} else {
setXNoNotification(newLocation.x);
setYNoNotification(newLocation.y);
}
notifyObjectMoved(oldLocation);
notifyChange(Parameters.x, oldX, getX());
notifyChange(Parameters.y, oldY, getY());
if (!isFullyContainedInContainer()) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("setLocation() lead shape going outside it's parent view");
}
}
}
}
public FGEPoint getLocationInDrawing() {
return GraphicalRepresentation.convertNormalizedPoint(this, new FGEPoint(0, 0), getDrawingGraphicalRepresentation());
}
/*@Override
public void setAbsoluteTextX(double absoluteTextX)
{
super.setAbsoluteTextX(absoluteTextX);
if (isParentLayoutedAsContainer()) {
ShapeGraphicalRepresentation<?> container = (ShapeGraphicalRepresentation<?>)getContainerGraphicalRepresentation();
FGEPoint oldLocation = container.getLocation();
FGERectangle oldBounds = container.getBounds();
FGERectangle newBounds = container.getRequiredBoundsForChildGRLocation(this,getLocation());
System.out.println("oldBounds= "+oldBounds+" newBounds="+newBounds);
container.updateRequiredBoundsForChildGRLocation(this,getLocation());
if (container.getLocation().x != oldLocation.x) {
System.out.println("Trying "+absoluteTextX+" use finally "+(absoluteTextX-container.getLocation().x+oldLocation.x)+" but container moved from "+oldLocation.x+" to "+container.getLocation().x);
super.setAbsoluteTextX(absoluteTextX-container.getLocation().x+oldLocation.x);
}
}
}*/
private void setLocationNoCheckNorNotification(FGEPoint newLocation) {
setXNoNotification(newLocation.x);
setYNoNotification(newLocation.y);
}
private void setLocationForContainerLayout(FGEPoint newLocation) {
ShapeGraphicalRepresentation<?> container = (ShapeGraphicalRepresentation<?>) getContainerGraphicalRepresentation();
container.updateRequiredBoundsForChildGRLocation(this, newLocation);
}
private void updateRequiredBoundsForChildGRLocation(ShapeGraphicalRepresentation<?> child, FGEPoint newChildLocation) {
FGERectangle oldBounds = getBounds();
FGERectangle newBounds = getRequiredBoundsForChildGRLocation(child, newChildLocation);
// System.out.println("oldBounds= "+oldBounds+" newBounds="+newBounds);
double deltaX = -newBounds.x + oldBounds.x;
double deltaY = -newBounds.y + oldBounds.y;
AffineTransform translation = AffineTransform.getTranslateInstance(deltaX, deltaY);
for (GraphicalRepresentation<?> gr : getContainedGraphicalRepresentations()) {
if (gr instanceof ShapeGraphicalRepresentation) {
ShapeGraphicalRepresentation<?> shapeGR = (ShapeGraphicalRepresentation<?>) gr;
if (shapeGR == child) {
shapeGR.setLocationNoCheckNorNotification(newChildLocation.transform(translation));
} else {
// FGEPoint oldLocationInParent = new FGEPoint(oldBounds.x+shapeGR.getLocation().x,oldBounds.y+shapeGR.getLocation().y);
// FGEPoint newLocationInParent = new
// FGEPoint(newBounds.x+shapeGR.getLocation().transform(translation).x,newBounds.y+shapeGR.getLocation().transform(translation).y);
// logger.info("Opposite was: "+shapeGR.getLocation()+" now "+shapeGR.getLocation().transform(translation)+" parent was: "+oldLocationInParent+" now "+newLocationInParent);
shapeGR.setLocationNoCheckNorNotification(shapeGR.getLocation().transform(translation));
}
shapeGR.notifyObjectMoved();
}
}
setLocation(new FGEPoint(newBounds.x, newBounds.y));
setSize(new FGEDimension(newBounds.width, newBounds.height));
}
public Dimension getNormalizedLabelSize() {
if (labelMetricsProvider != null) {
return labelMetricsProvider.getScaledPreferredDimension(1.0);
} else {
return new Dimension(0, 0);
}
}
public Rectangle getNormalizedLabelBounds() {
Dimension normalizedLabelSize = getNormalizedLabelSize();
Rectangle r = new Rectangle(getLabelLocation(1.0), normalizedLabelSize);
return r;
}
public FGERectangle getRequiredBoundsForContents() {
FGERectangle requiredBounds = null;
if (getContainedGraphicalRepresentations() == null) {
return new FGERectangle(getMinimalWidth() / 2, getMinimalHeight() / 2, getMinimalWidth(), getMinimalHeight());
}
for (GraphicalRepresentation<?> gr : getContainedGraphicalRepresentations()) {
if (gr instanceof ShapeGraphicalRepresentation) {
ShapeGraphicalRepresentation<?> shapeGR = (ShapeGraphicalRepresentation<?>) gr;
FGERectangle bounds = shapeGR.getBoundsNoBorder();
if (shapeGR.hasText()) {
Rectangle labelBounds = shapeGR.getNormalizedLabelBounds(); // getLabelBounds((new JLabel()), 1.0);
FGERectangle labelBounds2 = new FGERectangle(labelBounds.x, labelBounds.y, labelBounds.width, labelBounds.height);
bounds = bounds.rectangleUnion(labelBounds2);
}
if (requiredBounds == null) {
requiredBounds = bounds;
} else {
requiredBounds = requiredBounds.rectangleUnion(bounds);
}
}
}
if (requiredBounds == null) {
requiredBounds = new FGERectangle(getMinimalWidth() / 2, getMinimalHeight() / 2, getMinimalWidth(), getMinimalHeight());
} else {
if (requiredBounds.width < getMinimalWidth()) {
requiredBounds.x = requiredBounds.x - (int) ((getMinimalWidth() - requiredBounds.width) / 2.0);
requiredBounds.width = getMinimalWidth();
}
if (requiredBounds.height < getMinimalHeight()) {
requiredBounds.y = requiredBounds.y - (int) ((getMinimalHeight() - requiredBounds.height) / 2.0);
requiredBounds.height = getMinimalHeight();
}
}
requiredBounds.x = requiredBounds.x - getBorder().left;
requiredBounds.y = requiredBounds.y - getBorder().top;
return requiredBounds;
}
private FGERectangle getRequiredBoundsForChildGRLocation(GraphicalRepresentation<?> child, FGEPoint newChildLocation) {
FGERectangle requiredBounds = null;
for (GraphicalRepresentation<?> gr : getContainedGraphicalRepresentations()) {
if (gr instanceof ShapeGraphicalRepresentation) {
ShapeGraphicalRepresentation<?> shapeGR = (ShapeGraphicalRepresentation<?>) gr;
FGERectangle bounds = shapeGR.getBounds();
if (shapeGR == child) {
bounds.x = newChildLocation.x;
bounds.y = newChildLocation.y;
}
if (shapeGR.hasText()) {
Rectangle labelBounds = shapeGR.getNormalizedLabelBounds(); // getLabelBounds((new JLabel()), 1.0);
FGERectangle labelBounds2 = new FGERectangle(labelBounds.x, labelBounds.y, labelBounds.width, labelBounds.height);
bounds = bounds.rectangleUnion(labelBounds2);
}
if (requiredBounds == null) {
requiredBounds = bounds;
} else {
requiredBounds = requiredBounds.rectangleUnion(bounds);
}
}
}
if (requiredBounds == null) {
requiredBounds = new FGERectangle(getX(), getY(), getMinimalWidth(), getMinimalHeight());
} else {
requiredBounds.x = requiredBounds.x + getX();
requiredBounds.y = requiredBounds.y + getY();
if (requiredBounds.width < getMinimalWidth()) {
requiredBounds.x = requiredBounds.x - (int) ((getMinimalWidth() - requiredBounds.width) / 2.0);
requiredBounds.width = getMinimalWidth();
}
if (requiredBounds.height < getMinimalHeight()) {
requiredBounds.y = requiredBounds.y - (int) ((getMinimalHeight() - requiredBounds.height) / 2.0);
requiredBounds.height = getMinimalHeight();
}
}
return requiredBounds;
}
public LocationConstraints getLocationConstraints() {
return locationConstraints;
}
public void setLocationConstraints(LocationConstraints locationConstraints) {
FGENotification notification = requireChange(Parameters.locationConstraints, locationConstraints);
if (notification != null && getShape() != null) {
this.locationConstraints = locationConstraints;
if (isRegistered()) {
if (APPLY_CONSTRAINTS_IMMEDIATELY) {
checkAndUpdateLocationIfRequired();
}
getShape().rebuildControlPoints();
hasChanged(notification);
}
}
}
public FGEArea getLocationConstrainedArea() {
return locationConstrainedArea;
}
public void setLocationConstrainedArea(FGEArea locationConstrainedArea) {
FGENotification notification = requireChange(Parameters.locationConstrainedArea, locationConstrainedArea);
if (notification != null) {
this.locationConstrainedArea = locationConstrainedArea;
if (isRegistered()) {
if (APPLY_CONSTRAINTS_IMMEDIATELY) {
checkAndUpdateLocationIfRequired();
}
getShape().rebuildControlPoints();
hasChanged(notification);
}
}
}
private boolean observeParentGRBecauseMyLocationReferToIt = false;
/**
* Calling this method forces FGE to check (and eventually update) location of current graphical representation according defined
* location constraints
*/
protected void checkAndUpdateLocationIfRequired() {
try {
setLocation(getLocation());
} catch (IllegalArgumentException e) {
// May happen if object hierarchy inconsistent (or not consistent yet)
logger.fine("Ignore IllegalArgumentException: " + e.getMessage());
}
if (!observeParentGRBecauseMyLocationReferToIt) {
if (locationConstraints == LocationConstraints.AREA_CONSTRAINED && getContainerGraphicalRepresentation() != null) {
getContainerGraphicalRepresentation().addObserver(this);
observeParentGRBecauseMyLocationReferToIt = true;
// logger.info("Start observe my father");
}
}
}
protected FGEPoint computeConstrainedLocation(FGEPoint newLocation) {
if (isParentLayoutedAsContainer()) {
return newLocation;
}
if (getLocationConstraints() == LocationConstraints.FREELY_MOVABLE) {
return newLocation.clone();
}
if (getLocationConstraints() == LocationConstraints.CONTAINED_IN_SHAPE) {
GraphicalRepresentation<?> parent = getContainerGraphicalRepresentation();
if (parent instanceof ShapeGraphicalRepresentation) {
ShapeGraphicalRepresentation<?> container = (ShapeGraphicalRepresentation<?>) parent;
FGEPoint center = new FGEPoint(container.getWidth() / 2, container.getHeight() / 2);
double authorizedRatio = getMoveAuthorizedRatio(newLocation, center);
return new FGEPoint(center.x + (newLocation.x - center.x) * authorizedRatio, center.y + (newLocation.y - center.y)
* authorizedRatio);
}
}
if (getLocationConstraints() == LocationConstraints.AREA_CONSTRAINED) {
if (getLocationConstrainedArea() == null) {
// logger.warning("No location constrained are defined");
return newLocation;
} else {
return getLocationConstrainedArea().getNearestPoint(newLocation);
}
}
return newLocation;
}
public boolean isFullyContainedInContainer() {
if (getContainerGraphicalRepresentation() == null || getDrawing() == null) {
return true;
}
boolean isFullyContained = true;
FGERectangle containerViewBounds = new FGERectangle(0, 0, getContainerGraphicalRepresentation().getViewWidth(1),
getContainerGraphicalRepresentation().getViewHeight(1), Filling.FILLED);
for (ControlPoint cp : getShape().getControlPoints()) {
Point cpInContainerView = convertLocalNormalizedPointToRemoteViewCoordinates(cp.getPoint(),
getContainerGraphicalRepresentation(), 1);
FGEPoint preciseCPInContainerView = new FGEPoint(cpInContainerView.x, cpInContainerView.y);
if (!containerViewBounds.containsPoint(preciseCPInContainerView)) {
// System.out.println("Going outside: point="+preciseCPInContainerView+" bounds="+containerViewBounds);
isFullyContained = false;
}
}
return isFullyContained;
}
@Override
public boolean isContainedInSelection(Rectangle drawingViewSelection, double scale) {
FGERectangle drawingViewBounds = new FGERectangle(drawingViewSelection.getX(), drawingViewSelection.getY(),
drawingViewSelection.getWidth(), drawingViewSelection.getHeight(), Filling.FILLED);
boolean isFullyContained = true;
for (ControlPoint cp : getShape().getControlPoints()) {
Point cpInContainerView = convertLocalNormalizedPointToRemoteViewCoordinates(cp.getPoint(),
getDrawingGraphicalRepresentation(), scale);
FGEPoint preciseCPInContainerView = new FGEPoint(cpInContainerView.x, cpInContainerView.y);
if (!drawingViewBounds.containsPoint(preciseCPInContainerView)) {
// System.out.println("Going outside: point="+preciseCPInContainerView+" bounds="+containerViewBounds);
isFullyContained = false;
}
}
return isFullyContained;
}
public boolean isParentLayoutedAsContainer() {
return getContainerGraphicalRepresentation() != null
&& getContainerGraphicalRepresentation() instanceof ShapeGraphicalRepresentation
&& ((ShapeGraphicalRepresentation<?>) getContainerGraphicalRepresentation()).getDimensionConstraints() == DimensionConstraints.CONTAINER;
}
public double getMoveAuthorizedRatio(FGEPoint desiredLocation, FGEPoint initialLocation) {
if (isParentLayoutedAsContainer()) {
// This object is contained in a Shape acting as container: all locations are valid thus,
// container will adapt
return 1;
}
double returnedAuthorizedRatio = 1;
FGERectangle containerViewArea = new FGERectangle(0, 0, getContainerGraphicalRepresentation().getViewWidth(1),
getContainerGraphicalRepresentation().getViewHeight(1), Filling.FILLED);
FGERectangle containerViewBounds = new FGERectangle(0, 0, getContainerGraphicalRepresentation().getViewWidth(1),
getContainerGraphicalRepresentation().getViewHeight(1), Filling.NOT_FILLED);
/*boolean wasInside = true;
FGERectangle containerViewBounds = new FGERectangle(0,0,
getContainerGraphicalRepresentation().getViewWidth(1),
getContainerGraphicalRepresentation().getViewHeight(1));
for (ControlPoint cp : getShape().getControlPoints()) {
Point cpInContainerView = convertLocalNormalizedPointToRemoteViewCoordinates(
cp.getPoint(),
getContainerGraphicalRepresentation(),
1);
if (!containerViewBounds.contains(cpInContainerView)) wasInside = false;
}
if (!wasInside) {
logger.warning("getMoveAuthorizedRatio() called for a shape whose initial location wasn't in container shape");
return 1;
}*/
for (ControlPoint cp : getShape().getControlPoints()) {
Point currentCPInContainerView = convertLocalNormalizedPointToRemoteViewCoordinates(cp.getPoint(),
getContainerGraphicalRepresentation(), 1);
FGEPoint initialCPInContainerView = new FGEPoint((int) (currentCPInContainerView.x + initialLocation.x - getX()),
(int) (currentCPInContainerView.y + initialLocation.y - getY()));
FGEPoint desiredCPInContainerView = new FGEPoint((int) (currentCPInContainerView.x + desiredLocation.x - getX()),
(int) (currentCPInContainerView.y + desiredLocation.y - getY()));
if (!containerViewArea.containsPoint(initialCPInContainerView)) {
logger.warning("getMoveAuthorizedRatio() called for a shape whose initial location wasn't in container shape");
return 1;
}
if (!containerViewArea.containsPoint(desiredCPInContainerView)) {
// We are now sure that desired move will make the shape
// go outside parent bounds
FGESegment segment = new FGESegment(initialCPInContainerView, desiredCPInContainerView);
FGEArea intersection = FGEIntersectionArea.makeIntersection(segment, containerViewBounds);
if (intersection instanceof FGEPoint) {
// Intersection is normally a point
FGEPoint intersect = (FGEPoint) intersection;
double currentRatio = 1;
if (Math.abs(desiredCPInContainerView.x - initialCPInContainerView.x) > FGEGeometricObject.EPSILON) {
currentRatio = (intersect.x - initialCPInContainerView.x)
/ (desiredCPInContainerView.x - initialCPInContainerView.x) - FGEGeometricObject.EPSILON;
} else if (Math.abs(desiredCPInContainerView.y - initialCPInContainerView.y) > FGEGeometricObject.EPSILON) {
currentRatio = (intersect.y - initialCPInContainerView.y)
/ (desiredCPInContainerView.y - initialCPInContainerView.y) - FGEGeometricObject.EPSILON;
} else {
logger.warning("Unexpected unsignifiant move from " + initialCPInContainerView + " to " + desiredCPInContainerView);
}
if (currentRatio < returnedAuthorizedRatio) {
returnedAuthorizedRatio = currentRatio;
}
} else {
logger.warning("Unexpected intersection: " + intersection);
}
}
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("getMoveAuthorizedRatio() initial=" + initialLocation + " desired=" + desiredLocation + " return "
+ returnedAuthorizedRatio);
}
if (returnedAuthorizedRatio < 0) {
returnedAuthorizedRatio = 0;
}
return returnedAuthorizedRatio;
}
// *******************************************************************************
// * Size management *
// *******************************************************************************
@Override
public final void setText(String text) {
super.setText(text);
checkAndUpdateDimensionBoundsIfRequired();
}
@Override
public void setTextStyle(TextStyle textStyle) {
super.setTextStyle(textStyle);
checkAndUpdateDimensionBoundsIfRequired();
}
public boolean getAdjustMinimalWidthToLabelWidth() {
return adjustMinimalWidthToLabelWidth;
}
public void setAdjustMinimalWidthToLabelWidth(boolean adjustMinimalWidthToLabelWidth) {
FGENotification notification = requireChange(Parameters.adjustMinimalWidthToLabelWidth, adjustMinimalWidthToLabelWidth);
if (notification != null) {
this.adjustMinimalWidthToLabelWidth = adjustMinimalWidthToLabelWidth;
checkAndUpdateDimensionBoundsIfRequired();
hasChanged(notification);
}
}
public boolean getAdjustMinimalHeightToLabelHeight() {
return adjustMinimalHeightToLabelHeight;
}
public void setAdjustMinimalHeightToLabelHeight(boolean adjustMinimalHeightToLabelHeight) {
FGENotification notification = requireChange(Parameters.adjustMinimalHeightToLabelHeight, adjustMinimalHeightToLabelHeight);
if (notification != null) {
this.adjustMinimalHeightToLabelHeight = adjustMinimalHeightToLabelHeight;
checkAndUpdateDimensionBoundsIfRequired();
hasChanged(notification);
}
}
public boolean getAdjustMaximalWidthToLabelWidth() {
return adjustMaximalWidthToLabelWidth;
}
public void setAdjustMaximalWidthToLabelWidth(boolean adjustMaximalWidthToLabelWidth) {
FGENotification notification = requireChange(Parameters.adjustMaximalWidthToLabelWidth, adjustMaximalWidthToLabelWidth);
if (notification != null) {
this.adjustMaximalWidthToLabelWidth = adjustMaximalWidthToLabelWidth;
checkAndUpdateDimensionBoundsIfRequired();
hasChanged(notification);
}
}
public boolean getAdjustMaximalHeightToLabelHeight() {
return adjustMaximalHeightToLabelHeight;
}
public void setAdjustMaximalHeightToLabelHeight(boolean adjustMaximalHeightToLabelHeight) {
FGENotification notification = requireChange(Parameters.adjustMaximalHeightToLabelHeight, adjustMaximalHeightToLabelHeight);
if (notification != null) {
this.adjustMaximalHeightToLabelHeight = adjustMaximalHeightToLabelHeight;
checkAndUpdateDimensionBoundsIfRequired();
hasChanged(notification);
}
}
public double getWidth() {
return width;
}
public final void setWidth(double aValue) {
FGENotification notification = requireChange(Parameters.width, aValue);
if (notification != null) {
FGEDimension oldSize = getSize();
setWidthNoNotification(aValue);
checkAndUpdateDimensionBoundsIfRequired();
hasChanged(notification);
notifyObjectResized(oldSize);
}
}
public void setWidthNoNotification(double aValue) {
width = aValue;
}
public double getHeight() {
return height;
}
public final void setHeight(double aValue) {
FGENotification notification = requireChange(Parameters.height, aValue);
if (notification != null) {
FGEDimension oldSize = getSize();
setHeightNoNotification(aValue);
checkAndUpdateDimensionBoundsIfRequired();
hasChanged(notification);
notifyObjectResized(oldSize);
}
}
public void setHeightNoNotification(double aValue) {
height = aValue;
}
public FGEDimension getSize() {
return new FGEDimension(getWidth(), getHeight());
}
public void setSize(FGEDimension newSize) {
if (newSize == null) {
return;
}
// Preventing size from being negative or equals to 0
if (newSize.width <= 0) {
newSize.width = FGEGeometricObject.EPSILON;
}
if (newSize.height <= 0) {
newSize.height = FGEGeometricObject.EPSILON;
}
FGEDimension oldSize = getSize();
if (!newSize.equals(oldSize)) {
double oldWidth = getWidth();
double oldHeight = getHeight();
setWidthNoNotification(newSize.width);
setHeightNoNotification(newSize.height);
if (hasFloatingLabel()) {
if (getAbsoluteTextX() >= 0) {
if (getAbsoluteTextX() < getWidth()) {
setAbsoluteTextX(getAbsoluteTextX() / oldSize.width * getWidth());
} else {
setAbsoluteTextX(getAbsoluteTextX() + getWidth() - oldSize.width);
}
}
if (getAbsoluteTextY() >= 0) {
if (getAbsoluteTextY() < getHeight()) {
setAbsoluteTextY(getAbsoluteTextY() / oldSize.height * getHeight());
} else {
setAbsoluteTextY(getAbsoluteTextY() + getHeight() - oldSize.height);
}
}
}
checkAndUpdateDimensionBoundsIfRequired();
if (isParentLayoutedAsContainer()) {
((ShapeGraphicalRepresentation<?>) getContainerGraphicalRepresentation()).checkAndUpdateDimensionIfRequired();
}
notifyObjectResized(oldSize);
notifyChange(Parameters.width, oldWidth, getWidth());
notifyChange(Parameters.height, oldHeight, getHeight());
getShape().notifyObjectResized();
}
}
public double getMinimalWidth() {
return minimalWidth;
}
public final void setMinimalWidth(double minimalWidth) {
FGENotification notification = requireChange(Parameters.minimalWidth, minimalWidth);
if (notification != null) {
this.minimalWidth = minimalWidth;
checkAndUpdateDimensionBoundsIfRequired();
hasChanged(notification);
}
}
public double getMinimalHeight() {
return minimalHeight;
}
public final void setMinimalHeight(double minimalHeight) {
FGENotification notification = requireChange(Parameters.minimalHeight, minimalHeight);
if (notification != null) {
this.minimalHeight = minimalHeight;
checkAndUpdateDimensionBoundsIfRequired();
hasChanged(notification);
}
}
public double getMaximalHeight() {
return maximalHeight;
}
public final void setMaximalHeight(double maximalHeight) {
FGENotification notification = requireChange(Parameters.maximalHeight, maximalHeight);
if (notification != null) {
this.maximalHeight = maximalHeight;
checkAndUpdateDimensionBoundsIfRequired();
hasChanged(notification);
}
}
public double getMaximalWidth() {
return maximalWidth;
}
public final void setMaximalWidth(double maximalWidth) {
FGENotification notification = requireChange(Parameters.maximalWidth, maximalWidth);
if (notification != null) {
this.maximalWidth = maximalWidth;
checkAndUpdateDimensionBoundsIfRequired();
hasChanged(notification);
}
}
public final boolean getAllowToLeaveBounds() {
return allowToLeaveBounds;
}
public void setAllowToLeaveBounds(boolean allowToLeaveBounds) {
FGENotification notification = requireChange(Parameters.allowToLeaveBounds, allowToLeaveBounds);
if (notification != null) {
this.allowToLeaveBounds = allowToLeaveBounds;
hasChanged(notification);
}
}
public final boolean getAdaptBoundsToContents() {
return adaptBoundsToContents;
}
public void setAdaptBoundsToContents(boolean adaptBoundsToContents) {
FGENotification notification = requireChange(Parameters.adaptBoundsToContents, adaptBoundsToContents);
if (notification != null) {
this.adaptBoundsToContents = adaptBoundsToContents;
hasChanged(notification);
if (adaptBoundsToContents) {
extendBoundsToHostContents();
}
}
}
public DimensionConstraints getDimensionConstraints() {
// Shape dimension constraints may override defaults
if (shape != null && shape.areDimensionConstrained()) {
if (dimensionConstraints == DimensionConstraints.CONSTRAINED_DIMENSIONS) {
return DimensionConstraints.CONSTRAINED_DIMENSIONS;
}
if (dimensionConstraints == DimensionConstraints.FREELY_RESIZABLE) {
return DimensionConstraints.CONSTRAINED_DIMENSIONS;
}
return DimensionConstraints.UNRESIZABLE;
}
return dimensionConstraints;
}
public void setDimensionConstraints(DimensionConstraints dimensionConstraints) {
FGENotification notification = requireChange(Parameters.dimensionConstraints, dimensionConstraints);
if (notification != null && getShape() != null) {
this.dimensionConstraints = dimensionConstraints;
getShape().rebuildControlPoints();
hasChanged(notification);
}
}
public FGESteppedDimensionConstraint getDimensionConstraintStep() {
return dimensionConstraintStep;
}
public void setDimensionConstraintStep(FGESteppedDimensionConstraint dimensionConstraintStep) {
FGENotification notification = requireChange(Parameters.dimensionConstraintStep, dimensionConstraintStep);
if (notification != null) {
this.dimensionConstraintStep = dimensionConstraintStep;
if (isRegistered()) {
if (APPLY_CONSTRAINTS_IMMEDIATELY) {
checkAndUpdateDimensionIfRequired();
}
getShape().rebuildControlPoints();
hasChanged(notification);
}
}
}
/**
* Calling this method forces FGE to check (and eventually update) dimension of current graphical representation according defined
* dimension constraints
*/
protected void checkAndUpdateDimensionIfRequired() {
if (getDimensionConstraints() == DimensionConstraints.CONTAINER) {
List<? extends GraphicalRepresentation<?>> childs = getContainedGraphicalRepresentations();
if (childs != null && childs.size() > 0) {
ShapeGraphicalRepresentation<?> first = (ShapeGraphicalRepresentation<?>) childs.get(0);
updateRequiredBoundsForChildGRLocation(first, first.getLocation());
}
} else {
checkAndUpdateDimensionBoundsIfRequired();
}
}
/**
* Return required width of shape, giving computed width of current label (useful for auto-layout, when
*
* <pre>
* adjustMinimalWidthToLabelWidth
* </pre>
*
* is set to true). Override this method to implement custom layout
*
* @param labelWidth
* @return
*/
public double getRequiredWidth(double labelWidth) {
return labelWidth;
}
/**
* Return required height of shape, giving computed height of current label (usefull for auto-layout, when
*
* <pre>
* adjustMinimalHeightToLabelHeight
* </pre>
*
* is set to true). Override this method to implement custom layout
*
* @param labelHeight
* @return
*/
public double getRequiredHeight(double labelHeight) {
return labelHeight;
}
private boolean isCheckingDimensionConstraints = false;
private void checkAndUpdateDimensionBoundsIfRequired() {
if (isCheckingDimensionConstraints || labelMetricsProvider == null) {
return;
}
try {
// if (getAdaptBoundsToContents()) {
// extendBoundsToHostContents();
// }
isCheckingDimensionConstraints = true;
// FGERectangle requiredBounds = getRequiredBoundsForContents();
boolean changed = false;
FGEDimension newDimension = getSize();
// double minWidth = (getAdaptBoundsToContents() ? Math.max(getMinimalWidth(), requiredBounds.width) : getMinimalWidth());
// double minHeight = (getAdaptBoundsToContents() ? Math.max(getMinimalHeight(), requiredBounds.height) : getMinimalHeight());
double minWidth = getMinimalWidth();
double minHeight = getMinimalHeight();
double maxWidth = getMaximalWidth();
double maxHeight = getMaximalHeight();
if (hasText() && !getIsFloatingLabel()) {
Dimension normalizedLabelSize = getNormalizedLabelSize();
int labelWidth = normalizedLabelSize.width;
int labelHeight = normalizedLabelSize.height;
double requiredWidth = getRequiredWidth(labelWidth);
double requiredHeight = getRequiredHeight(labelHeight);
double rh = 0, rw = 0;
FGEPoint rp = new FGEPoint(getRelativeTextX(), getRelativeTextY());
switch (getVerticalTextAlignment()) {
case BOTTOM:
if (FGEUtils.doubleEquals(rp.y, 0.0)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Impossible to handle BOTTOM alignement with relative y position set to 0!");
}
} else {
rh = labelHeight / rp.y;
}
break;
case MIDDLE:
if (FGEUtils.doubleEquals(rp.y, 0.0)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Impossible to handle MIDDLE alignement with relative y position set to 0");
}
} else if (FGEUtils.doubleEquals(rp.y, 1.0)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Impossible to handle MIDDLE alignement with relative y position set to 1");
}
} else {
if (rp.y > 0.5) {
rh = labelHeight / (2 * (1 - rp.y));
} else {
rh = labelHeight / (2 * rp.y);
}
}
break;
case TOP:
if (FGEUtils.doubleEquals(rp.x, 1.0)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Impossible to handle TOP alignement with relative y position set to 1!");
}
} else {
rh = labelHeight / (1 - rp.y);
}
break;
}
switch (getHorizontalTextAlignment()) {
case RIGHT:
if (FGEUtils.doubleEquals(rp.x, 0.0)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Impossible to handle RIGHT alignement with relative x position set to 0!");
}
} else {
rw = labelWidth / rp.x;
}
case CENTER:
if (FGEUtils.doubleEquals(rp.x, 0.0)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Impossible to handle CENTER alignement with relative x position set to 0");
}
} else if (FGEUtils.doubleEquals(rp.x, 1.0)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Impossible to handle CENTER alignement with relative x position set to 1");
}
} else {
if (rp.x > 0.5) {
rw = labelWidth / (2 * (1 - rp.x));
} else {
rw = labelWidth / (2 * rp.x);
}
}
break;
case LEFT:
if (FGEUtils.doubleEquals(rp.x, 1.0)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Impossible to handle LEFT alignement with relative x position set to 1!");
}
} else {
rw = labelWidth / (1 - rp.x);
}
break;
}
requiredWidth = Math.max(rw, requiredWidth);
requiredHeight = Math.max(rh, requiredHeight);
if (getAdjustMinimalWidthToLabelWidth()) {
minWidth = Math.max(requiredWidth, minWidth);
if (getWidth() < minWidth) {
newDimension.width = minWidth;
changed = true;
}
}
if (getAdjustMinimalHeightToLabelHeight()) {
minHeight = Math.max(requiredHeight, minHeight);
if (getHeight() < minHeight) {
newDimension.height = minHeight;
changed = true;
}
}
if (getAdjustMaximalWidthToLabelWidth()) {
maxWidth = Math.min(requiredWidth, maxWidth);
if (getWidth() > maxWidth) {
newDimension.width = maxWidth;
changed = true;
}
}
if (getAdjustMaximalHeightToLabelHeight()) {
maxHeight = Math.min(requiredHeight, maxHeight);
if (getHeight() > maxHeight) {
newDimension.height = maxHeight;
changed = true;
}
}
}
if (getMinimalWidth() > getMaximalWidth()) {
logger.warning("Minimal width > maximal width, cannot proceed");
} else {
if (getWidth() < minWidth) {
newDimension.width = minWidth;
changed = true;
}
if (getWidth() > maxWidth) {
newDimension.width = maxWidth;
changed = true;
}
}
if (getMinimalHeight() > getMaximalHeight()) {
logger.warning("Minimal height > maximal height, cannot proceed");
} else {
if (getHeight() < minHeight) {
newDimension.height = minHeight;
changed = true;
}
if (getHeight() > maxHeight) {
newDimension.height = maxHeight;
changed = true;
}
}
boolean useStepDimensionConstraints = getDimensionConstraints() == DimensionConstraints.STEP_CONSTRAINED
&& getDimensionConstraintStep() != null;
if (useStepDimensionConstraints && hasText() && !isFloatingLabel) {
if (getAdjustMinimalWidthToLabelWidth() && getAdjustMaximalWidthToLabelWidth()) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Too many constraints on width! Cannot proceed.");
}
useStepDimensionConstraints = false;
}
if (getAdjustMinimalHeightToLabelHeight() && getAdjustMaximalHeightToLabelHeight()) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Too many constraints on height! Cannot proceed.");
}
useStepDimensionConstraints = false;
}
}
if (useStepDimensionConstraints) {
FGEDimension d = getDimensionConstraintStep().getNearestDimension(newDimension, minWidth, maxWidth, minHeight, maxHeight);
if (!d.equals(newDimension)) {
newDimension = d;
changed = true;
}
}
if (changed) {
setSize(newDimension);
checkAndUpdateLocationIfRequired();
}
} finally {
isCheckingDimensionConstraints = false;
}
}
// *******************************************************************************
// * Geometry constraints *
// *******************************************************************************
public static BindingDefinition X_CONSTRAINTS = new BindingDefinition("xConstraints", Double.class, BindingDefinitionType.GET, false);
public static BindingDefinition Y_CONSTRAINTS = new BindingDefinition("yConstraints", Double.class, BindingDefinitionType.GET, false);
public static BindingDefinition WIDTH_CONSTRAINTS = new BindingDefinition("widthConstraints", Double.class, BindingDefinitionType.GET,
false);
public static BindingDefinition HEIGHT_CONSTRAINTS = new BindingDefinition("heightConstraints", Double.class,
BindingDefinitionType.GET, false);
private DataBinding xConstraints;
private DataBinding yConstraints;
private DataBinding widthConstraints;
private DataBinding heightConstraints;
public DataBinding getXConstraints() {
if (xConstraints == null) {
xConstraints = new DataBinding(this, Parameters.xConstraints, X_CONSTRAINTS);
}
return xConstraints;
}
public void setXConstraints(DataBinding xConstraints) {
FGENotification notification = requireChange(Parameters.xConstraints, xConstraints);
if (notification != null) {
xConstraints.setOwner(this);
xConstraints.setBindingAttribute(Parameters.xConstraints);
xConstraints.setBindingDefinition(X_CONSTRAINTS);
this.xConstraints = xConstraints;
hasChanged(notification);
}
}
public DataBinding getYConstraints() {
if (yConstraints == null) {
yConstraints = new DataBinding(this, Parameters.yConstraints, Y_CONSTRAINTS);
}
return yConstraints;
}
public void setYConstraints(DataBinding yConstraints) {
FGENotification notification = requireChange(Parameters.yConstraints, yConstraints);
if (notification != null) {
yConstraints.setOwner(this);
yConstraints.setBindingAttribute(Parameters.yConstraints);
yConstraints.setBindingDefinition(Y_CONSTRAINTS);
this.yConstraints = yConstraints;
hasChanged(notification);
}
}
public DataBinding getWidthConstraints() {
if (widthConstraints == null) {
widthConstraints = new DataBinding(this, Parameters.widthConstraints, WIDTH_CONSTRAINTS);
}
return widthConstraints;
}
public void setWidthConstraints(DataBinding widthConstraints) {
FGENotification notification = requireChange(Parameters.widthConstraints, widthConstraints);
if (notification != null) {
widthConstraints.setOwner(this);
widthConstraints.setBindingAttribute(Parameters.widthConstraints);
widthConstraints.setBindingDefinition(WIDTH_CONSTRAINTS);
this.widthConstraints = widthConstraints;
hasChanged(notification);
}
}
public DataBinding getHeightConstraints() {
if (heightConstraints == null) {
heightConstraints = new DataBinding(this, Parameters.heightConstraints, HEIGHT_CONSTRAINTS);
}
return heightConstraints;
}
public void setHeightConstraints(DataBinding heightConstraints) {
FGENotification notification = requireChange(Parameters.heightConstraints, heightConstraints);
if (notification != null) {
heightConstraints.setOwner(this);
heightConstraints.setBindingAttribute(Parameters.heightConstraints);
heightConstraints.setBindingDefinition(HEIGHT_CONSTRAINTS);
this.heightConstraints = heightConstraints;
hasChanged(notification);
}
}
public void finalizeConstraints() {
if (xConstraints != null && xConstraints.isValid()) {
xConstraints.finalizeDeserialization();
setX((Double) TypeUtils.castTo(xConstraints.getBindingValue(this), Double.class));
setLocationConstraints(LocationConstraints.UNMOVABLE);
}
if (yConstraints != null && yConstraints.isValid()) {
yConstraints.finalizeDeserialization();
setY((Double) TypeUtils.castTo(yConstraints.getBindingValue(this), Double.class));
setLocationConstraints(LocationConstraints.UNMOVABLE);
}
if (widthConstraints != null && widthConstraints.isValid()) {
widthConstraints.finalizeDeserialization();
setWidth((Double) TypeUtils.castTo(widthConstraints.getBindingValue(this), Double.class));
setDimensionConstraints(DimensionConstraints.UNRESIZABLE);
}
if (heightConstraints != null && heightConstraints.isValid()) {
heightConstraints.finalizeDeserialization();
setHeight((Double) TypeUtils.castTo(heightConstraints.getBindingValue(this), Double.class));
setDimensionConstraints(DimensionConstraints.UNRESIZABLE);
}
}
@Override
protected void computeNewConstraint(ConstraintDependency dependancy) {
if (dependancy.requiringParameter == Parameters.xConstraints && xConstraints != null && xConstraints.isValid()) {
updateXPosition();
} else if (dependancy.requiringParameter == Parameters.yConstraints && yConstraints != null && yConstraints.isValid()) {
updateYPosition();
} else if (dependancy.requiringParameter == Parameters.widthConstraints && widthConstraints != null && widthConstraints.isValid()) {
updateWidthPosition();
} else if (dependancy.requiringParameter == Parameters.heightConstraints && heightConstraints != null
&& heightConstraints.isValid()) {
updateHeightPosition();
}
}
public void updateConstraints() {
// System.out.println("updateConstraints() called, valid=" + xConstraints.isValid() + "," + yConstraints.isValid() + ","
// + widthConstraints.isValid() + "," + heightConstraints.isValid());
logger.fine("Called updateConstraints(), drawable=" + getDrawable() + " index=" + getIndex() + " class=" + getClass());
if (xConstraints != null && xConstraints.isValid()) {
// System.out.println("x was " + getX() + " constraint=" + xConstraints);
updateXPosition();
// System.out.println("x is now " + getX());
}
if (yConstraints != null && yConstraints.isValid()) {
// System.out.println("y was " + getY() + " constraint=" + yConstraints);
updateYPosition();
// System.out.println("y is now " + getY());
}
if (widthConstraints != null && widthConstraints.isValid()) {
// System.out.println("width was " + getWidth() + " constraint=" + widthConstraints);
updateWidthPosition();
// System.out.println("width is now " + getWidth());
}
if (heightConstraints != null && heightConstraints.isValid()) {
// System.out.println("height was " + getHeight() + " constraint=" + heightConstraints);
updateHeightPosition();
// System.out.println("height is now " + getHeight());
}
}
private void updateXPosition() {
Number n = (Number) xConstraints.getBindingValue(this);
if (n != null) {
// System.out.println("New value for x is now: " + newValue);
setX(n.doubleValue());
}
}
private void updateYPosition() {
Number n = (Number) yConstraints.getBindingValue(this);
if (n != null) {
// System.out.println("New value for y is now: " + newValue);
setY(n.doubleValue());
}
}
private void updateWidthPosition() {
Number n = (Number) widthConstraints.getBindingValue(this);
if (n != null) {
// System.out.println("New value for width is now: " + newValue);
setWidth(n.doubleValue());
}
}
private void updateHeightPosition() {
Number n = (Number) heightConstraints.getBindingValue(this);
if (n != null) {
// System.out.println("New value for height is now: " + newValue);
setHeight(n.doubleValue());
}
}
public void constraintChanged(DataBinding constraint) {
// logger.info(">>>>>>> OK, constraint "+constraint+" changed for "+constraint.getBindingAttribute());
constraint.updateDependancies();
if (constraint.getBindingAttribute() == Parameters.xConstraints && xConstraints != null && xConstraints.isValid()) {
updateXPosition();
} else if (constraint.getBindingAttribute() == Parameters.yConstraints && yConstraints != null && yConstraints.isValid()) {
updateYPosition();
} else if (constraint.getBindingAttribute() == Parameters.widthConstraints && widthConstraints != null
&& widthConstraints.isValid()) {
updateWidthPosition();
} else if (constraint.getBindingAttribute() == Parameters.heightConstraints && heightConstraints != null
&& heightConstraints.isValid()) {
updateHeightPosition();
}
}
// *******************************************************************************
// * Accessors *
// *******************************************************************************
public ForegroundStyle getForeground() {
return foreground;
}
public void setForeground(ForegroundStyle aForeground) {
FGENotification notification = requireChange(Parameters.foreground, aForeground, false);
if (notification != null) {
if (foreground != null) {
foreground.deleteObserver(this);
}
foreground = aForeground;
if (aForeground != null) {
aForeground.addObserver(this);
}
hasChanged(notification);
}
}
public ForegroundStyle getSelectedForeground() {
if (selectedForeground == null) {
selectedForeground = foreground.clone();
}
return selectedForeground;
}
public void setSelectedForeground(ForegroundStyle aForeground) {
FGENotification notification = requireChange(Parameters.selectedForeground, aForeground, false);
if (notification != null) {
if (selectedForeground != null) {
selectedForeground.deleteObserver(this);
}
selectedForeground = aForeground;
if (aForeground != null) {
aForeground.addObserver(this);
}
hasChanged(notification);
}
}
public boolean getHasSelectedForeground() {
return hasSelectedForeground;
}
public void setHasSelectedForeground(boolean aFlag) {
hasSelectedForeground = aFlag;
}
public ForegroundStyle getFocusedForeground() {
if (focusedForeground == null) {
focusedForeground = foreground.clone();
}
return focusedForeground;
}
public void setFocusedForeground(ForegroundStyle aForeground) {
FGENotification notification = requireChange(Parameters.focusedForeground, aForeground, false);
if (notification != null) {
if (focusedForeground != null) {
focusedForeground.deleteObserver(this);
}
focusedForeground = aForeground;
if (aForeground != null) {
aForeground.addObserver(this);
}
hasChanged(notification);
}
}
public boolean getHasFocusedForeground() {
return hasFocusedForeground;
}
public void setHasFocusedForeground(boolean aFlag) {
hasFocusedForeground = aFlag;
}
public boolean getNoStroke() {
return foreground.getNoStroke();
}
public void setNoStroke(boolean noStroke) {
foreground.setNoStroke(noStroke);
}
public BackgroundStyle getBackground() {
return background;
}
public void setBackground(BackgroundStyle aBackground) {
FGENotification notification = requireChange(Parameters.background, aBackground, false);
if (notification != null) {
// background = aBackground.clone();
if (background != null) {
background.deleteObserver(this);
}
background = aBackground;
// background.setGraphicalRepresentation(this);
if (aBackground != null) {
aBackground.addObserver(this);
}
hasChanged(notification);
}
}
public BackgroundStyleType getBackgroundType() {
return background.getBackgroundStyleType();
}
public void setBackgroundType(BackgroundStyleType backgroundType) {
if (backgroundType != getBackgroundType()) {
setBackground(BackgroundStyle.makeBackground(backgroundType));
}
}
public BackgroundStyle getSelectedBackground() {
if (selectedBackground == null) {
selectedBackground = background.clone();
}
return selectedBackground;
}
public void setSelectedBackground(BackgroundStyle aBackground) {
FGENotification notification = requireChange(Parameters.selectedBackground, aBackground, false);
if (notification != null) {
// background = aBackground.clone();
if (selectedBackground != null) {
selectedBackground.deleteObserver(this);
}
selectedBackground = aBackground;
// background.setGraphicalRepresentation(this);
if (aBackground != null) {
aBackground.addObserver(this);
}
hasChanged(notification);
}
}
public boolean getHasSelectedBackground() {
return hasSelectedBackground;
}
public void setHasSelectedBackground(boolean aFlag) {
hasSelectedBackground = aFlag;
}
public BackgroundStyle getFocusedBackground() {
if (focusedBackground == null) {
focusedBackground = background.clone();
}
return focusedBackground;
}
public void setFocusedBackground(BackgroundStyle aBackground) {
FGENotification notification = requireChange(Parameters.focusedBackground, aBackground, false);
if (notification != null) {
// background = aBackground.clone();
if (focusedBackground != null) {
focusedBackground.deleteObserver(this);
}
focusedBackground = aBackground;
// background.setGraphicalRepresentation(this);
if (aBackground != null) {
aBackground.addObserver(this);
}
hasChanged(notification);
}
}
public boolean getHasFocusedBackground() {
return hasFocusedBackground;
}
public void setHasFocusedBackground(boolean aFlag) {
hasFocusedBackground = aFlag;
}
public ShapeBorder getBorder() {
return border;
}
public void setBorder(ShapeBorder border) {
FGENotification notification = requireChange(Parameters.border, border);
if (notification != null) {
this.border = border;
hasChanged(notification);
notifyObjectResized();
}
}
public ShadowStyle getShadowStyle() {
return shadowStyle;
}
public void setShadowStyle(ShadowStyle aShadowStyle) {
FGENotification notification = requireChange(Parameters.shadowStyle, aShadowStyle);
if (notification != null) {
if (shadowStyle != null) {
shadowStyle.deleteObserver(this);
}
this.shadowStyle = aShadowStyle;
if (aShadowStyle != null) {
aShadowStyle.addObserver(this);
}
hasChanged(notification);
}
}
/*public boolean getDrawShadow()
{
return drawShadow;
}
public void setDrawShadow(boolean drawShadow)
{
FGENotification notification = requireChange(Parameters.drawShadow,
drawShadow);
if (notification != null) {
this.drawShadow = drawShadow;
hasChanged(notification);
}
}*/
/*public int getShadowDarkness()
{
return shadowDarkness;
}
public void setShadowDarkness(int shadowDarkness)
{
FGENotification notification = requireChange(
Parameters.shadowDarkness, shadowDarkness);
if (notification != null) {
this.shadowDarkness = shadowDarkness;
hasChanged(notification);
}
}
public int getShadowBlur()
{
return shadowBlur;
}
public void setShadowBlur(int shadowBlur)
{
FGENotification notification = requireChange(Parameters.shadowBlur,
shadowBlur);
if (notification != null) {
this.shadowBlur = shadowBlur;
hasChanged(notification);
}
}
public int getShadowDeep()
{
return shadowDeep;
}
public void setShadowDeep(int shadowDeep)
{
FGENotification notification = requireChange(Parameters.shadowDeep,
shadowDeep);
if (notification != null) {
this.shadowDeep = shadowDeep;
hasChanged(notification);
}
}*/
public Shape getShape() {
return shape;
}
public void setShape(Shape aShape) {
aShape.setGraphicalRepresentation(this);
FGENotification notification = requireChange(Parameters.shape, aShape);
if (notification != null) {
ShapeType oldType = aShape != null ? aShape.getShapeType() : null;
this.shape = aShape;
shape.rebuildControlPoints();
hasChanged(notification);
setChanged();
notifyObservers(new FGENotification(Parameters.shapeType, oldType, aShape.getShapeType()));
notifyShapeChanged();
}
}
public ShapeType getShapeType() {
if (shape != null) {
return shape.getShapeType();
}
return null;
}
public void setShapeType(ShapeType shapeType) {
if (getShapeType() != shapeType) {
setShape(Shape.makeShape(shapeType, this));
if (getShape().areDimensionConstrained()) {
double newSize = Math.max(getWidth(), getHeight());
setWidth(newSize);
setHeight(newSize);
}
}
}
public void notifyShapeChanged() {
setChanged();
notifyObservers(new ShapeChanged());
}
public void notifyShapeNeedsToBeRedrawn() {
setChanged();
notifyObservers(new ShapeNeedsToBeRedrawn());
}
public void notifyObjectMoved() {
boolean mustNotify = !isMoving();
if (mustNotify) {
notifyObjectWillMove();
}
notifyObjectMoved(null);
if (mustNotify) {
notifyObjectHasMoved();
}
}
public void notifyObjectMoved(FGEPoint oldLocation) {
setChanged();
notifyObservers(new ObjectMove(oldLocation, getLocation()));
}
public void notifyObjectWillMove() {
isMoving = true;
setChanged();
notifyObservers(new ObjectWillMove());
}
public void notifyObjectHasMoved() {
isMoving = false;
setChanged();
notifyObservers(new ObjectHasMoved());
}
public boolean isMoving() {
return isMoving;
}
/**
* Notify that the object just resized
*/
public void notifyObjectResized() {
notifyObjectResized(null);
}
/**
* Notify that the object just resized
*/
public void notifyObjectResized(FGEDimension oldSize) {
if (getShape() != null) {
getShape().updateShape();
}
setChanged();
notifyObservers(new ObjectResized(oldSize, getSize()));
}
/**
* Notify that the object will be resized
*/
public void notifyObjectWillResize() {
isResizing = true;
setChanged();
notifyObservers(new ObjectWillResize());
}
/**
* Notify that the object resizing has finished (take care that this just notify END of resize, this should NOT be used to notify a
* resizing: use notifyObjectResize() instead)
*/
public void notifyObjectHasResized() {
isResizing = false;
for (GraphicalRepresentation<?> gr : getContainedGraphicalRepresentations()) {
if (gr instanceof ShapeGraphicalRepresentation) {
((ShapeGraphicalRepresentation<?>) gr).checkAndUpdateLocationIfRequired();
}
}
setChanged();
notifyObservers(new ObjectHasResized());
}
public boolean isResizing() {
return isResizing;
}
@Override
public final boolean shouldBeDisplayed() {
return super.shouldBeDisplayed();
}
public boolean getIsFloatingLabel() {
return isFloatingLabel;
}
public void setIsFloatingLabel(boolean isFloatingLabel) {
FGENotification notification = requireChange(Parameters.isFloatingLabel, isFloatingLabel);
if (notification != null) {
this.isFloatingLabel = isFloatingLabel;
hasChanged(notification);
}
}
@Override
public boolean hasFloatingLabel() {
return hasText() && getIsFloatingLabel();
}
public double getRelativeTextX() {
return relativeTextX;
}
public void setRelativeTextX(double textX) {
FGENotification notification = requireChange(Parameters.relativeTextX, textX);
if (notification != null) {
this.relativeTextX = textX;
hasChanged(notification);
}
}
public double getRelativeTextY() {
return relativeTextY;
}
public void setRelativeTextY(double textY) {
FGENotification notification = requireChange(Parameters.relativeTextY, textY);
if (notification != null) {
this.relativeTextY = textY;
hasChanged(notification);
}
}
// *******************************************************************************
// * Methods *
// *******************************************************************************
@Override
public int getViewX(double scale) {
return (int) (getX() * scale/*-(border!=null?border.left:0)*/);
}
@Override
public int getViewY(double scale) {
return (int) (getY() * scale/*-(border!=null?border.top:0)*/);
}
@Override
public int getViewWidth(double scale) {
return (int) (getUnscaledViewWidth() * scale) + 1;
}
public double getUnscaledViewWidth() {
return getWidth() + (getBorder() != null ? getBorder().left + getBorder().right : 0);
}
@Override
public int getViewHeight(double scale) {
return (int) (getUnscaledViewHeight() * scale) + 1;
}
public double getUnscaledViewHeight() {
return getHeight() + (getBorder() != null ? getBorder().top + getBorder().bottom : 0);
}
/**
* Return bounds (including border) relative to parent container
*
* @return
*/
public FGERectangle getBounds() {
return new FGERectangle(getX(), getY(), getUnscaledViewWidth(), getUnscaledViewHeight());
}
/**
* Return bounds (including border) relative to parent container
*
* @return
*/
public FGERectangle getBoundsNoBorder() {
return new FGERectangle(getX(), getY(), getWidth(), getHeight());
}
/**
* Return view bounds (excluding border) relative to parent container
*
* @param scale
* @return
*/
public Rectangle getBounds(double scale) {
Rectangle bounds = new Rectangle();
bounds.x = (int) ((getX() + (getBorder() != null ? getBorder().left : 0)) * scale);
bounds.y = (int) ((getY() + (getBorder() != null ? getBorder().top : 0)) * scale);
bounds.width = (int) (getWidth() * scale);
bounds.height = (int) (getHeight() * scale);
return bounds;
}
/**
* Return view bounds (excluding border) relative to given container
*
* @param scale
* @return
*/
public Rectangle getBounds(GraphicalRepresentation<?> container, double scale) {
Rectangle bounds = getBounds(scale);
bounds = convertRectangle(getContainerGraphicalRepresentation(), bounds, container, scale);
return bounds;
}
/**
* Return logical bounds (including border) relative to given container
*
* @param scale
* @return
*/
public Rectangle getViewBounds(GraphicalRepresentation<?> container, double scale) {
Rectangle bounds = getViewBounds(scale);
if (getContainerGraphicalRepresentation() == null) {
logger.warning("Container is null for " + this + " validated=" + isValidated());
}
if (container == null) {
logger.warning("Container is null for " + this + " validated=" + isValidated());
}
bounds = convertRectangle(getContainerGraphicalRepresentation(), bounds, container, scale);
return bounds;
}
@Override
public AffineTransform convertNormalizedPointToViewCoordinatesAT(double scale) {
AffineTransform returned = AffineTransform.getScaleInstance(getWidth(), getHeight());
if (getBorder() != null) {
returned.preConcatenate(AffineTransform.getTranslateInstance(getBorder().left, getBorder().top));
}
if (scale != 1) {
returned.preConcatenate(AffineTransform.getScaleInstance(scale, scale));
}
return returned;
/*
double x2 = x*getWidth();
double y2 = y*getHeight();
if (getBorder() != null) {
x2 += getBorder().left;
y2 += getBorder().top;
}
if (scale != 1) {
x2 = x2*scale;
y2 = y2*scale;
}
return new Point((int)x2,(int)y2);*/
}
@Override
public AffineTransform convertViewCoordinatesToNormalizedPointAT(double scale) {
AffineTransform returned = new AffineTransform();
if (scale != 1) {
returned = AffineTransform.getScaleInstance(1 / scale, 1 / scale);
}
if (getBorder() != null) {
returned.preConcatenate(AffineTransform.getTranslateInstance(-getBorder().left, -getBorder().top));
}
returned.preConcatenate(AffineTransform.getScaleInstance(1 / getWidth(), 1 / getHeight()));
return returned;
/*
double x2= (double)x;
double y2= (double)y;
if (scale != 1) {
x2 = x2/scale;
y2 = y2/scale;
}
if (getBorder() != null) {
x2 -= getBorder().left;
y2 -= getBorder().top;
}
x2 = x2/getWidth();
y2 = y2/getHeight();
return new FGEPoint(x2,y2);*/
}
public boolean isPointInsideShape(FGEPoint aPoint) {
return shape.isPointInsideShape(aPoint);
}
// *******************************************************************************
// * Methods *
// *******************************************************************************
public DecorationPainter getDecorationPainter() {
return decorationPainter;
}
public void setDecorationPainter(DecorationPainter aPainter) {
decorationGraphics = new FGEShapeDecorationGraphics(this);
decorationPainter = aPainter;
}
public ShapePainter getShapePainter() {
return shapePainter;
}
public void setShapePainter(ShapePainter aPainter) {
shapePainter = aPainter;
}
@Override
public void paint(Graphics g, DrawingController<?> controller) {
if (!isRegistered()) {
setRegistered(true);
}
super.paint(g, controller);
Graphics2D g2 = (Graphics2D) g;
graphics.createGraphics(g2, controller);
// If there is a decoration painter init its graphics
if (decorationPainter != null) {
decorationGraphics.createGraphics(g2, controller);
}
// If there is a decoration painter and decoration should be painted BEFORE shape, fo it now
if (decorationPainter != null && decorationPainter.paintBeforeShape()) {
decorationPainter.paintDecoration(decorationGraphics);
}
if (FGEConstants.DEBUG) {
if (getBorder() != null) {
g2.setColor(Color.RED);
g2.drawRect(0, 0, getViewWidth(controller.getScale()) - 1, getViewHeight(controller.getScale()) - 1);
g2.setColor(Color.BLUE);
g2.drawRect((int) (getBorder().left * controller.getScale()), (int) (getBorder().top * controller.getScale()),
(int) (getWidth() * controller.getScale()) - 1, (int) (getHeight() * controller.getScale()) - 1);
} else {
g2.setColor(Color.BLUE);
g2.drawRect(0, 0, getViewWidth(controller.getScale()) - 1, getViewHeight(controller.getScale()) - 1);
}
}
if (shape != null && getShadowStyle() != null) {
if (getShadowStyle().getDrawShadow()) {
shape.paintShadow(graphics);
}
shape.paintShape(graphics);
}
if (shapePainter != null) {
shapePainter.paintShape(graphics);
}
// If there is a decoration painter and decoration should be painted AFTER shape, do it now
if (decorationPainter != null && !decorationPainter.paintBeforeShape()) {
decorationPainter.paintDecoration(decorationGraphics);
}
graphics.releaseGraphics();
if (decorationPainter != null) {
decorationGraphics.releaseGraphics();
}
}
@Override
public Point getLabelLocation(double scale) {
Point point;
if (getIsFloatingLabel()) {
point = new Point((int) (getAbsoluteTextX() * scale + getViewX(scale)), (int) (getAbsoluteTextY() * scale + getViewY(scale)));
} else {
FGEPoint relativePosition = new FGEPoint(getRelativeTextX(), getRelativeTextY());
point = convertLocalNormalizedPointToRemoteViewCoordinates(relativePosition, getContainerGraphicalRepresentation(), scale);
}
Dimension d = getLabelDimension(scale);
switch (getHorizontalTextAlignment()) {
case CENTER:
point.x -= d.width / 2;
break;
case LEFT:
break;
case RIGHT:
point.x -= d.width;
break;
}
switch (getVerticalTextAlignment()) {
case BOTTOM:
point.y -= d.height;
break;
case MIDDLE:
point.y -= d.height / 2;
break;
case TOP:
break;
}
return point;
}
@Override
public void setLabelLocation(Point point, double scale) {
if (getIsFloatingLabel()) {
Dimension d = getLabelDimension(scale);
switch (getHorizontalTextAlignment()) {
case CENTER:
point.x += d.width / 2;
break;
case LEFT:
break;
case RIGHT:
point.x += d.width;
break;
}
switch (getVerticalTextAlignment()) {
case BOTTOM:
point.y += d.height;
break;
case MIDDLE:
point.y += d.height / 2;
break;
case TOP:
break;
}
FGEPoint p = new FGEPoint((point.x - getViewX(scale)) / scale, (point.y - getViewY(scale)) / scale);
setAbsoluteTextX(p.x);
setAbsoluteTextY(p.y);
}
}
@Override
public int getAvailableLabelWidth(double scale) {
if (getLineWrap()) {
double rpx = getRelativeTextX();
switch (getHorizontalTextAlignment()) {
case RIGHT:
if (FGEUtils.doubleEquals(rpx, 0.0)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Impossible to handle RIGHT alignement with relative x position set to 0!");
}
} else {
return (int) (getWidth() * rpx * scale);
}
case CENTER:
if (FGEUtils.doubleEquals(rpx, 0.0)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Impossible to handle CENTER alignement with relative x position set to 0");
}
} else if (FGEUtils.doubleEquals(rpx, 1.0)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Impossible to handle CENTER alignement with relative x position set to 1");
}
} else {
if (rpx > 0.5) {
return (int) (getWidth() * 2 * (1 - rpx) * scale);
} else {
return (int) (getWidth() * 2 * rpx * scale);
}
}
break;
case LEFT:
if (FGEUtils.doubleEquals(rpx, 1.0)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Impossible to handle LEFT alignement with relative x position set to 1");
}
} else {
return (int) (getWidth() * (1 - rpx) * scale);
}
break;
}
}
return super.getAvailableLabelWidth(scale);
}
@Override
public void setHorizontalTextAlignment(org.openflexo.fge.GraphicalRepresentation.HorizontalTextAlignment horizontalTextAlignment) {
super.setHorizontalTextAlignment(horizontalTextAlignment);
checkAndUpdateDimensionBoundsIfRequired();
}
@Override
public void setVerticalTextAlignment(org.openflexo.fge.GraphicalRepresentation.VerticalTextAlignment verticalTextAlignment) {
super.setVerticalTextAlignment(verticalTextAlignment);
checkAndUpdateDimensionBoundsIfRequired();
}
@Override
public String getInspectorName() {
return "ShapeGraphicalRepresentation.inspector";
}
// Override for a custom view management
public ShapeView<O> makeShapeView(DrawingController<?> controller) {
return new ShapeView<O>(this, controller);
}
@Override
public String toString() {
if (isRegistered()) {
return getClass().getSimpleName() + "[" + getShapeType() + "," + getBounds(1) + "," + getText() + "]";
} else {
return getClass().getSimpleName() + "[" + getShapeType() + "," + getText() + "]";
}
}
@Override
public void notifyObjectHierarchyHasBeenUpdated() {
super.notifyObjectHierarchyHasBeenUpdated();
// The two calls below seem to be very important to ensure proper layout of
// containers with constraints CONTAINER!
checkAndUpdateLocationIfRequired();
checkAndUpdateDimensionIfRequired();
}
public List<? extends ControlArea<?>> getControlAreas() {
return getShape().getControlPoints();
}
public FGEShapeGraphics getGraphics() {
return graphics;
}
/**
* Override this if you want to use such a feature
*
* @return
*/
public boolean isAllowedToBeDraggedOutsideParentContainer() {
return false;
}
/**
* Override this if you want to use this feature Default implementation return always false
*
* @return
*/
public boolean isAllowedToBeDraggedOutsideParentContainerInsideContainer(GraphicalRepresentation<?> container) {
return false;
}
/**
* Override this if you want to use this feature Default implementation does nothing return boolean indicating if drag was successfully
* performed
*/
public boolean dragOutsideParentContainerInsideContainer(GraphicalRepresentation<?> container, FGEPoint location) {
return false;
}
// *******************************************************************************
// * Layout *
// *******************************************************************************
public void performRandomLayout() {
performRandomLayout(getWidth(), getHeight());
}
public void performAutoLayout() {
performAutoLayout(getWidth(), getHeight());
}
@Override
public void notifyDrawableAdded(GraphicalRepresentation<?> addedGR) {
super.notifyDrawableAdded(addedGR);
if (getAdaptBoundsToContents()) {
extendBoundsToHostContents();
}
}
/**
* Returns the area on which the given connector can start. The area is expressed in this normalized coordinates
*
* @param connectorGR
* the connector asking where to start
* @return the area on which the given connector can start
*/
public FGEArea getAllowedStartAreaForConnector(ConnectorGraphicalRepresentation<?> connectorGR) {
return getShape().getOutline();
}
/**
* Returns the area on which the given connector can end. The area is expressed in this normalized coordinates
*
* @param connectorGR
* the connector asking where to end
* @return the area on which the given connector can end
*/
public FGEArea getAllowedEndAreaForConnector(ConnectorGraphicalRepresentation<?> connectorGR) {
return getShape().getOutline();
}
/**
* Returns the area on which the given connector can start. The area is expressed in this normalized coordinates
*
* @param connectorGR
* the connector asking where to start
*
* @return the area on which the given connector can start
*/
public FGEArea getAllowedStartAreaForConnectorForDirection(ConnectorGraphicalRepresentation<?> connectorGR, FGEArea area,
SimplifiedCardinalDirection direction) {
return area;
}
/**
* Returns the area on which the given connector can end. The area is expressed in this normalized coordinates
*
* @param connectorGR
* the connector asking where to end
* @return the area on which the given connector can end
*/
public FGEArea getAllowedEndAreaForConnectorForDirection(ConnectorGraphicalRepresentation<?> connectorGR, FGEArea area,
SimplifiedCardinalDirection direction) {
return area;
}
}