/* * (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.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.beans.PropertyChangeSupport; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Observable; import java.util.Observer; import java.util.Random; import java.util.StringTokenizer; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import org.openflexo.antar.binding.AbstractBinding.BindingEvaluationContext; import org.openflexo.antar.binding.Bindable; import org.openflexo.antar.binding.BindingFactory; import org.openflexo.antar.binding.BindingModel; import org.openflexo.antar.binding.BindingVariable; import org.openflexo.fge.GRBindingFactory.ComponentPathElement; import org.openflexo.fge.GRBindingFactory.ComponentsBindingVariable; import org.openflexo.fge.GRVariable.GRVariableType; import org.openflexo.fge.controller.DrawingController; import org.openflexo.fge.controller.MouseClickControl; import org.openflexo.fge.controller.MouseControl.MouseButton; import org.openflexo.fge.controller.MouseDragControl; import org.openflexo.fge.geom.FGEGeometricObject; import org.openflexo.fge.geom.FGEGeometricObject.Filling; import org.openflexo.fge.geom.FGEPoint; import org.openflexo.fge.geom.FGERectPolylin; import org.openflexo.fge.geom.FGERectangle; import org.openflexo.fge.graphics.DrawUtils; import org.openflexo.fge.graphics.TextStyle; import org.openflexo.fge.notifications.FGENotification; import org.openflexo.fge.notifications.GraphicalRepresentationAdded; import org.openflexo.fge.notifications.GraphicalRepresentationDeleted; import org.openflexo.fge.notifications.GraphicalRepresentationRemoved; import org.openflexo.fge.notifications.LabelHasEdited; import org.openflexo.fge.notifications.LabelHasMoved; import org.openflexo.fge.notifications.LabelWillEdit; import org.openflexo.fge.notifications.LabelWillMove; import org.openflexo.fib.utils.LocalizedDelegateGUIImpl; import org.openflexo.inspector.DefaultInspectableObject; import org.openflexo.toolbox.FileResource; import org.openflexo.toolbox.HasPropertyChangeSupport; import org.openflexo.xmlcode.StringEncoder; import org.openflexo.xmlcode.XMLSerializable; public abstract class GraphicalRepresentation<O> extends DefaultInspectableObject implements XMLSerializable, Bindable, BindingEvaluationContext, Cloneable, FGEConstants, Observer, HasPropertyChangeSupport { private static final Logger logger = Logger.getLogger(GraphicalRepresentation.class.getPackage().getName()); // Instantiate a new localizer in directory src/dev/resources/FGELocalized // Little hack to be removed: linked to parent localizer (which is Openflexo main localizer) public static LocalizedDelegateGUIImpl LOCALIZATION = new LocalizedDelegateGUIImpl(new FileResource("FGELocalized"), new LocalizedDelegateGUIImpl(new FileResource("Localized"), null, false), true); private Stroke specificStroke = null; private static BindingFactory BINDING_FACTORY = new GRBindingFactory(); private static final List<Object> EMPTY_VECTOR = Collections.emptyList(); private static final List<GraphicalRepresentation<?>> EMPTY_GR_VECTOR = Collections.emptyList(); // ******************************************************************************* // * Parameters * // ******************************************************************************* public static interface LabelMetricsProvider { public Dimension getScaledPreferredDimension(double scale); } public static interface GRParameter { public String name(); } public static enum Parameters implements GRParameter { identifier, layer, hasText, text, isMultilineAllowed, lineWrap, continuousTextEditing, textStyle, absoluteTextX, absoluteTextY, horizontalTextAlignment, verticalTextAlignment, paragraphAlignment, isSelectable, isFocusable, isSelected, isFocused, drawControlPointsWhenFocused, drawControlPointsWhenSelected, isReadOnly, isLabelEditable, isVisible, mouseClickControls, mouseDragControls, toolTipText, variables } protected int layer; private TextStyle textStyle = TextStyle.makeDefault(); private String text; private boolean multilineAllowed = false; private boolean lineWrap = false; private boolean continuousTextEditing = true; private double absoluteTextX = 0; private double absoluteTextY = 0; private HorizontalTextAlignment horizontalTextAlignment = HorizontalTextAlignment.CENTER; private VerticalTextAlignment verticalTextAlignment = VerticalTextAlignment.MIDDLE; private ParagraphAlignment paragraphAlignment = ParagraphAlignment.CENTER; private boolean isSelectable = true; private boolean isFocusable = true; private boolean isSelected = false; private boolean isFocused = false; private boolean drawControlPointsWhenFocused = true; private boolean drawControlPointsWhenSelected = true; protected boolean isVisible = true; private boolean readOnly = false; private boolean labelEditable = true; private Vector<MouseClickControl> mouseClickControls; private Vector<MouseDragControl> mouseDragControls; private PropertyChangeSupport pcSupport; private String toolTipText = null; public static enum ParagraphAlignment { LEFT, CENTER, RIGHT, JUSTIFY; } public static enum HorizontalTextAlignment { LEFT, CENTER, RIGHT } public static enum VerticalTextAlignment { TOP, MIDDLE, BOTTOM; } protected static class ConstraintDependency { GraphicalRepresentation<?> requiringGR; GRParameter requiringParameter; GraphicalRepresentation<?> requiredGR; GRParameter requiredParameter; public ConstraintDependency(GraphicalRepresentation<?> requiringGR, GRParameter requiringParameter, GraphicalRepresentation<?> requiredGR, GRParameter requiredParameter) { super(); this.requiringGR = requiringGR; this.requiringParameter = requiringParameter; this.requiredGR = requiredGR; this.requiredParameter = requiredParameter; } @Override public boolean equals(Object obj) { if (obj instanceof ConstraintDependency) { ConstraintDependency opposite = (ConstraintDependency) obj; return requiredGR == opposite.requiredGR && requiringGR == opposite.requiringGR && requiringParameter == opposite.requiringParameter && requiredParameter == opposite.requiredParameter; } return super.equals(obj); } @Override public int hashCode() { return super.hashCode(); } } private Vector<ConstraintDependency> dependancies; private Vector<ConstraintDependency> alterings; // ******************************************************************************* // * Static code * // ******************************************************************************* public static StringEncoder.Converter<FGEPoint> POINT_CONVERTER = new StringEncoder.Converter<FGEPoint>(FGEPoint.class) { @Override public FGEPoint convertFromString(String value) { try { FGEPoint returned = new FGEPoint(); StringTokenizer st = new StringTokenizer(value, ","); if (st.hasMoreTokens()) { returned.x = Double.parseDouble(st.nextToken()); } if (st.hasMoreTokens()) { returned.y = Double.parseDouble(st.nextToken()); } return returned; } catch (NumberFormatException e) { // Warns about the exception System.err.println("Supplied value is not parsable as a FGEPoint:" + value); return null; } } @Override public String convertToString(FGEPoint aPoint) { if (aPoint != null) { return aPoint.x + "," + aPoint.y; } else { return null; } } }; public static StringEncoder.Converter<FGERectPolylin> RECT_POLYLIN_CONVERTER = new StringEncoder.Converter<FGERectPolylin>( FGERectPolylin.class) { @Override public FGERectPolylin convertFromString(String value) { try { Vector<FGEPoint> points = new Vector<FGEPoint>(); StringTokenizer st = new StringTokenizer(value, ";"); while (st.hasMoreTokens()) { String nextPoint = st.nextToken(); points.add(POINT_CONVERTER.convertFromString(nextPoint)); } return new FGERectPolylin(points); } catch (NumberFormatException e) { // Warns about the exception System.err.println("Supplied value is not parsable as a FGEPoint:" + value); return null; } } @Override public String convertToString(FGERectPolylin aPolylin) { if (aPolylin != null) { StringBuffer sb = new StringBuffer(); boolean isFirst = true; for (FGEPoint pt : aPolylin.getPoints()) { if (!isFirst) { sb.append(";"); } sb.append(POINT_CONVERTER.convertToString(pt)); isFirst = false; } return sb.toString(); } else { return null; } } }; static { StringEncoder.getDefaultInstance()._addConverter(POINT_CONVERTER); StringEncoder.getDefaultInstance()._addConverter(RECT_POLYLIN_CONVERTER); } // ******************************************************************************* // * Inner classes * // ******************************************************************************* // ******************************************************************************* // * Fields * // ******************************************************************************* private O drawable; private Drawing<?> drawing; private Vector<Object> ancestors; private boolean isRegistered = false; private boolean isDeleted = false; private boolean hasText = true; // ******************************************************************************* // * Constructor * // ******************************************************************************* protected GraphicalRepresentation(O aDrawable, Drawing<?> aDrawing) { super(); drawable = aDrawable; drawing = aDrawing; textStyle = TextStyle.makeDefault(); // textStyle.setGraphicalRepresentation(this); if (textStyle != null) { textStyle.addObserver(this); } mouseClickControls = new Vector<MouseClickControl>(); mouseDragControls = new Vector<MouseDragControl>(); dependancies = new Vector<ConstraintDependency>(); alterings = new Vector<ConstraintDependency>(); } // *************************************************************************** // * Deletion * // *************************************************************************** public void delete() { if (!isDeleted) { isDeleted = true; if (textStyle != null) { textStyle.deleteObserver(this); } _bindingModel = null; setChanged(); notifyObservers(new GraphicalRepresentationDeleted(this)); deleteObservers(); if (getPropertyChangeSupport() != null) { // Property change support can be null if noone is listening. I noone is listening, // it is not needed to fire a property change. getPropertyChangeSupport().firePropertyChange(getDeletedProperty(), false, true); // Fixed huge bug with graphical representation (which are in the model) deleted when the diagram view was closed // TODO: Now we can really set the pcSupport to null here // Until now, it still create big issues // pcSupport = null; } } } @Override public boolean isDeleted() { return isDeleted; } public GRParameter parameterWithName(String parameterName) { if (parameterName == null) { return null; } for (GRParameter param : getAllParameters()) { if (param.name().equals(parameterName)) { return param; } } return null; } public Vector<GRParameter> getAllParameters() { Vector<GRParameter> returned = new Vector<GRParameter>(); Parameters[] allParams = Parameters.values(); for (int i = 0; i < allParams.length; i++) { returned.add(allParams[i]); } return returned; } // *************************************************************************** // * Cloning * // *************************************************************************** public void setsWith(GraphicalRepresentation<?> gr) { if (gr instanceof GraphicalRepresentation) { for (Parameters p : Parameters.values()) { if (p != Parameters.identifier && p != Parameters.mouseClickControls && p != Parameters.mouseDragControls) { _setParameterValueWith(p, gr); } } } } public void setsWith(GraphicalRepresentation<?> gr, GRParameter... exceptedParameters) { if (gr instanceof GraphicalRepresentation) { for (Parameters p : Parameters.values()) { boolean excepted = false; for (GRParameter ep : exceptedParameters) { if (p == ep) { excepted = true; } } if (p != Parameters.mouseClickControls && p != Parameters.mouseDragControls && p != Parameters.identifier && !excepted) { _setParameterValueWith(p, gr); } } } } protected void _setParameterValueWith(Enum<?> parameterKey, GraphicalRepresentation<?> gr) { Class<?> type = getTypeForKey(parameterKey.name()); if (type.isPrimitive()) { if (logger.isLoggable(Level.FINE)) { logger.fine("Primitive: " + parameterKey.name() + " of " + type + " values " + gr.valueForKey(parameterKey.name())); } if (type == Boolean.TYPE) { setBooleanValueForKey(gr.booleanValueForKey(parameterKey.name()), parameterKey.name()); } if (type == Integer.TYPE) { setIntegerValueForKey(gr.integerValueForKey(parameterKey.name()), parameterKey.name()); } if (type == Short.TYPE) { setShortValueForKey(gr.shortValueForKey(parameterKey.name()), parameterKey.name()); } if (type == Long.TYPE) { setLongValueForKey(gr.longValueForKey(parameterKey.name()), parameterKey.name()); } if (type == Float.TYPE) { setFloatValueForKey(gr.floatValueForKey(parameterKey.name()), parameterKey.name()); } if (type == Double.TYPE) { setDoubleValueForKey(gr.doubleValueForKey(parameterKey.name()), parameterKey.name()); } if (type == Byte.TYPE) { setByteValueForKey(gr.byteValueForKey(parameterKey.name()), parameterKey.name()); } if (type == Character.TYPE) { setCharacterForKey(gr.characterForKey(parameterKey.name()), parameterKey.name()); } } else { Object value = gr.objectForKey(parameterKey.name()); if (logger.isLoggable(Level.FINE)) { logger.fine("Object: " + parameterKey.name() + " of " + type); } if (value instanceof Cloneable) { // Try to clone... try { if (logger.isLoggable(Level.FINE)) { logger.fine("Cloning object: " + parameterKey.name() + " of " + type); } Method cloneMethod = value.getClass().getMethod("clone", (Class[]) null); value = cloneMethod.invoke(value, (Object[]) null); } catch (Exception e) { logger.warning("Cannot clone parameter: " + value); e.printStackTrace(); } } // IMPORTANT !!!!!! // Special case for DataBinding, just copy unparsed string, and let framework recompute the binding if (type.equals(DataBinding.class)) { value = new DataBinding(((DataBinding) value).toString()); } Object currentValue = objectForKey(parameterKey.name()); if (value != currentValue) { setObjectForKey(value, parameterKey.name()); } } } // ************************************************************************* // * Serialization * // ************************************************************************* private boolean isDeserializing = false; public void initializeDeserialization() { isDeserializing = true; } public void finalizeDeserialization() { // logger.info("Hop: finalizeDeserialization for "+this+" root is "+getRootGraphicalRepresentation()); if (getRootGraphicalRepresentation().getBindingModel() == null) { getRootGraphicalRepresentation().createBindingModel(); } isDeserializing = false; } public boolean isDeserializing() { return isDeserializing; } // ******************************************************************************* // * Instance methods * // ******************************************************************************* private String identifier = null; public void resetToDefaultIdentifier() { identifier = retrieveDefaultIdentifier(); } private String retrieveDefaultIdentifier() { if (getParentGraphicalRepresentation() == null) { return "root"; } else { // int index = getParentGraphicalRepresentation().getContainedGraphicalRepresentations().indexOf(this); int index = getIndex(); if (getParentGraphicalRepresentation().getParentGraphicalRepresentation() == null) { // logger.info("retrieveDefaultIdentifier return "+index); return "object_" + index; } else { return getParentGraphicalRepresentation().retrieveDefaultIdentifier() + "_" + index; } } } public String getIdentifier() { if (identifier == null) { return retrieveDefaultIdentifier(); } return identifier; } public void setIdentifier(String identifier) { FGENotification notification = requireChange(Parameters.identifier, identifier); if (notification != null) { this.identifier = identifier; hasChanged(notification); updateBindingModel(); } } public int getLayer() { return layer; } public void setLayer(int layer) { /* * Vector<GraphicalRepresentation> allGRInSameLayer = null; GraphicalRepresentation<?> parent = getParentGraphicalRepresentation(); * if (parent != null) { for (GraphicalRepresentation<?> child : parent.getContainedGraphicalRepresentations()) { if * (child.getLayer() == layer) { System.out.println("Il faudrait changer la layer de "+child +" en meme temps"); if * (allGRInSameLayer == null) allGRInSameLayer = new Vector<GraphicalRepresentation>(); allGRInSameLayer.add(child); } } } * * if (allGRInSameLayer == null || allGRInSameLayer.size() == 1) { FGENotification notification = requireChange(Parameters.layer, * layer); if (notification != null) { this.layer = layer; hasChanged(notification); } } else { for (GraphicalRepresentation<?> * child : allGRInSameLayer) { child.proceedSetLayer(layer); } } */ FGENotification notification = requireChange(Parameters.layer, layer); if (notification != null) { this.layer = layer; hasChanged(notification); } } /* * private void proceedSetLayer(int layer) { int oldLayer = layer; this.layer = layer; hasChanged(new * FGENotification("layer",oldLayer,layer)); } */ public final O getDrawable() { return drawable; } public final void setDrawable(O aDrawable) { drawable = aDrawable; } public Drawing<?> getDrawing() { return drawing; } public void setDrawing(Drawing<?> drawing) { this.drawing = drawing; } public DrawingGraphicalRepresentation<?> getDrawingGraphicalRepresentation() { return getDrawing().getDrawingGraphicalRepresentation(); } public <O2> GraphicalRepresentation<O2> getGraphicalRepresentation(O2 drawable) { return getDrawing().getGraphicalRepresentation(drawable); } public List<? extends Object> getContainedObjects(Object drawable) { return getDrawing().getContainedObjects(drawable); } public Object getContainer(Object drawable) { if (getDrawing() == null) { return null; } return getDrawing().getContainer(drawable); } public List<? extends Object> getContainedObjects() { if (getDrawable() == null) { return null; } if (getDrawing() == null) { return null; } return getDrawing().getContainedObjects(getDrawable()); } public List<GraphicalRepresentation<?>> getContainedGraphicalRepresentations() { // Indirection added to separate callers that require an ordered list of contained GR and those who do not care. Wa may then later // reimplement these methods to optimizer perfs. return getOrderedContainedGraphicalRepresentations(); } public List<GraphicalRepresentation<?>> getOrderedContainedGraphicalRepresentations() { if (!isValidated()) { return EMPTY_GR_VECTOR; } if (getContainedObjects() == null) { return null; } List<GraphicalRepresentation<?>> toRemove = new ArrayList<GraphicalRepresentation<?>>(getOrderedContainedGR()); for (Object o : getContainedObjects()) { GraphicalRepresentation<Object> gr = getDrawing().getGraphicalRepresentation(o); if (gr != null) { if (orderedContainedGR.contains(gr)) { // OK, fine toRemove.remove(gr); } else { orderedContainedGR.add(gr); } } else { if (logger.isLoggable(Level.WARNING)) { logger.warning("Could not find any gr for " + o); } } } for (GraphicalRepresentation<?> c : toRemove) { orderedContainedGR.remove(c); } return orderedContainedGR; } private List<GraphicalRepresentation<?>> orderedContainedGR = null; private List<GraphicalRepresentation<?>> getOrderedContainedGR() { if (!isValidated()) { logger.warning("GR " + this + " is not validated"); return EMPTY_GR_VECTOR; } if (orderedContainedGR == null) { orderedContainedGR = new ArrayList<GraphicalRepresentation<?>>(); for (GraphicalRepresentation<?> c : getOrderedContainedGraphicalRepresentations()) { if (!orderedContainedGR.contains(c)) { orderedContainedGR.add(c); } } } return orderedContainedGR; } public void moveToTop(GraphicalRepresentation<?> gr) { // TODO: something to do here if (logger.isLoggable(Level.FINE)) { logger.fine("moveToTop temporarily desactivated"); } /*if (!gr.isValidated()) { logger.warning("GR " + gr + " is not validated"); } if (getOrderedContainedGR().contains(gr)) { getOrderedContainedGR().remove(gr); } getOrderedContainedGR().add(gr);*/ } public int getOrder(GraphicalRepresentation<?> child1, GraphicalRepresentation<?> child2) { List<GraphicalRepresentation<?>> orderedGRList = getOrderedContainedGraphicalRepresentations(); // logger.info("getOrder: "+orderedGRList); if (!orderedGRList.contains(child1)) { return 0; } if (!orderedGRList.contains(child2)) { return 0; } /* * if (orderedGRList.indexOf(child1)<orderedGRList.indexOf(child2)) logger * .info("Ordering: "+child1+" on top of "+child2+" child1:"+orderedGRList * .indexOf(child1)+" child2:"+orderedGRList.indexOf(child2)); else logger * .info("Ordering: "+child2+" on top of "+child1+" child1:"+orderedGRList * .indexOf(child1)+" child2:"+orderedGRList.indexOf(child2)); */ return orderedGRList.indexOf(child1) - orderedGRList.indexOf(child2); } public int getLayerOrder() { if (!isValidated()) { return -1; } if (getParentGraphicalRepresentation() == null) { return -1; } List<GraphicalRepresentation<?>> orderedGRList = getParentGraphicalRepresentation().getOrderedContainedGraphicalRepresentations(); /*System.out.println("Index of " + this + " inside parent " + getParentGraphicalRepresentation() + " is " + orderedGRList.indexOf(this)); for (GraphicalRepresentation gr : orderedGRList) { System.out.println("> " + gr + " : is this=" + gr.equals(this)); }*/ return orderedGRList.indexOf(this); } public int getIndex() { return getLayerOrder(); } public Object getContainer() { if (drawing == null) { return null; } if (drawable == null) { return null; } return drawing.getContainer(drawable); } public GraphicalRepresentation<?> getContainerGraphicalRepresentation() { if (!isValidated()) { return null; } Object container = getContainer(); if (container == null) { // logger.warning("No container for "+this); return null; } return getDrawing().getGraphicalRepresentation(getContainer()); } public GraphicalRepresentation<?> getParentGraphicalRepresentation() { return getContainerGraphicalRepresentation(); } public boolean contains(GraphicalRepresentation<?> gr) { if (!isValidated()) { return false; } return getContainedGraphicalRepresentations().contains(gr); } public boolean contains(Object drawable) { if (!isValidated()) { return false; } return getContainedGraphicalRepresentations().contains(getGraphicalRepresentation(drawable)); } public List<Object> getAncestors() { if (!isValidated()) { return EMPTY_VECTOR; } return getAncestors(false); } public List<Object> getAncestors(boolean forceRecompute) { if (!isValidated()) { return EMPTY_VECTOR; } if (getDrawing() == null) { return EMPTY_VECTOR; } if (ancestors == null || forceRecompute) { ancestors = new Vector<Object>(); Object current = getDrawable(); while (current != getDrawing().getModel()) { Object container = drawing.getContainer(current); if (container == null) { // throw new // IllegalArgumentException("Drawable "+current+" has no container"); return ancestors; } ancestors.add(container); current = container; } } return ancestors; } public boolean isConnectedToDrawing() { if (!isValidated()) { return false; } Object current = getDrawable(); while (current != getDrawing().getModel()) { Object container = drawing.getContainer(current); if (container == null) { return false; } current = container; } return true; } public boolean isAncestorOf(GraphicalRepresentation<?> child) { if (!isValidated()) { return false; } GraphicalRepresentation<?> father = child.getContainerGraphicalRepresentation(); while (father != null) { if (father == this) { return true; } father = father.getContainerGraphicalRepresentation(); } return false; } public static GraphicalRepresentation<?> getFirstCommonAncestor(GraphicalRepresentation<?> child1, GraphicalRepresentation<?> child2) { if (!child1.isValidated()) { return null; } if (!child2.isValidated()) { return null; } return getFirstCommonAncestor(child1, child2, false); } public static GraphicalRepresentation<?> getFirstCommonAncestor(GraphicalRepresentation<?> child1, GraphicalRepresentation<?> child2, boolean includeCurrent) { if (!child1.isValidated()) { return null; } if (!child2.isValidated()) { return null; } List<Object> ancestors1 = child1.getAncestors(true); if (includeCurrent) { ancestors1.add(0, child1); } List<Object> ancestors2 = child2.getAncestors(true); if (includeCurrent) { ancestors2.add(0, child2); } for (int i = 0; i < ancestors1.size(); i++) { Object o1 = ancestors1.get(i); if (ancestors2.contains(o1)) { return child1.getGraphicalRepresentation(o1); } } return null; } public static boolean areElementsConnectedInGraphicalHierarchy(GraphicalRepresentation<?> element1, GraphicalRepresentation<?> element2) { if (!element1.isValidated()) { return false; } if (!element2.isValidated()) { return false; } return getFirstCommonAncestor(element1, element2) != null; } /* * public boolean isLayerVisibleAccross(GraphicalRepresentation<?> gr) { if (getLayer() > gr.getLayer()) return true; * * //if (debug) logger.info("this="+this); //if (debug) logger.info("gr="+gr); * * // But may be i have one parent whose layer is bigger than opposite gr GraphicalRepresentation<?> commonAncestor = * getFirstCommonAncestor(this, gr, true); * * //if (debug) logger.info("commonAncestor="+commonAncestor+" of "+commonAncestor .getClass().getName()); * * if (commonAncestor == null) return false; * * GraphicalRepresentation<?> lastAncestor1 = null; GraphicalRepresentation<?> lastAncestor2 = null; if (commonAncestor == this) * lastAncestor1 = this; if (commonAncestor == gr) lastAncestor2 = gr; * * for (GraphicalRepresentation<?> child : commonAncestor.getContainedGraphicalRepresentations()) { //logger.info("Child:"+child); if * (lastAncestor1 == null && (child == this || child.isAncestorOf(this))) lastAncestor1 = child; if (lastAncestor2 == null && (child == * gr || child.isAncestorOf(gr))) lastAncestor2 = child; } * * //if (debug) logger.info("Ancestor1="+lastAncestor1+" layer="+lastAncestor1 .getLayer()); //if (debug) * logger.info("Ancestor2="+lastAncestor2+" layer=" +lastAncestor2.getLayer()); * * if (lastAncestor1 == null) return false; if (lastAncestor2 == null) return false; * * return lastAncestor1.getLayer() >= lastAncestor2.getLayer(); } */ /* * public boolean isPointVisible(FGEPoint p) { if (!getIsVisible()) return false; * * GraphicalRepresentation<?> initialGR = this; GraphicalRepresentation<?> currentGR = this; * * while (currentGR != null) { GraphicalRepresentation<?> parentGR = currentGR.getContainerGraphicalRepresentation(); if (parentGR == * null) return true; if (!parentGR.getIsVisible()) return false; for (GraphicalRepresentation<?> child : * parentGR.getContainedGraphicalRepresentations()) { // Only ShapeGR can hide other GR, ignore ConnectorGR here if (child instanceof * ShapeGraphicalRepresentation) { ShapeGraphicalRepresentation<?> shapedChild = (ShapeGraphicalRepresentation<?>)child; if * (shapedChild.getShape().getShape().containsPoint( convertNormalizedPoint(initialGR, p, child))) { * logger.info("GR "+child+" contains point "+p+" on "+initialGR); if(child.getLayer() > currentGR.getLayer()) { * logger.info("GR "+child+" hides point "+p+" on "+initialGR); } } } } currentGR = parentGR; } * * return true; } */ public boolean isPointVisible(FGEPoint p) { if (!getIsVisible()) { return false; } /* * if (this instanceof ShapeGraphicalRepresentation) { // Be careful, maybe this point is just on outline // So translate it to the * center to be sure FGEPoint center = ((ShapeGraphicalRepresentation)this).getShape ().getShape().getCenter(); p.x = * p.x+FGEGeometricObject.EPSILON*(center.x-p.x); p.y = p.y+FGEGeometricObject.EPSILON*(center.y-p.y); } * * DrawingGraphicalRepresentation<?> drawingGR = getDrawingGraphicalRepresentation(); ShapeGraphicalRepresentation<?> topLevelShape * = drawingGR.getTopLevelShapeGraphicalRepresentation( convertNormalizedPoint(this, p, drawingGR)); */ GraphicalRepresentation<?> topLevelShape = shapeHiding(p); return topLevelShape == null; } public ShapeGraphicalRepresentation<?> shapeHiding(FGEPoint p) { if (!getIsVisible()) { return null; } if (this instanceof ShapeGraphicalRepresentation) { // Be careful, maybe this point is just on outline // So translate it to the center to be sure FGEPoint center = ((ShapeGraphicalRepresentation<?>) this).getShape().getShape().getCenter(); p.x = p.x + FGEGeometricObject.EPSILON * (center.x - p.x); p.y = p.y + FGEGeometricObject.EPSILON * (center.y - p.y); } DrawingGraphicalRepresentation<?> drawingGR = getDrawingGraphicalRepresentation(); ShapeGraphicalRepresentation<?> topLevelShape = drawingGR.getTopLevelShapeGraphicalRepresentation(convertNormalizedPoint(this, p, drawingGR)); if (topLevelShape == this || topLevelShape == getParentGraphicalRepresentation()) { return null; } return topLevelShape; } // ******************************************************************************* // * Accessors * // ******************************************************************************* public TextStyle getTextStyle() { return textStyle; } public void setTextStyle(TextStyle aTextStyle) { FGENotification notification = requireChange(Parameters.textStyle, aTextStyle, false); if (notification != null) { if (textStyle != null) { textStyle.deleteObserver(this); } this.textStyle = aTextStyle; if (aTextStyle != null) { aTextStyle.addObserver(this); } hasChanged(notification); } } public double getAbsoluteTextX() { return absoluteTextX; } public final void setAbsoluteTextX(double absoluteTextX) { /* * if (absoluteTextX < 0) absoluteTextX = 0; if (getContainerGraphicalRepresentation() != null && * getContainerGraphicalRepresentation() instanceof ShapeGraphicalRepresentation && absoluteTextX > ((ShapeGraphicalRepresentation * <?>)getContainerGraphicalRepresentation()).getWidth()) { absoluteTextX = * ((ShapeGraphicalRepresentation<?>)getContainerGraphicalRepresentation ()).getWidth(); } */ FGENotification notification = requireChange(Parameters.absoluteTextX, absoluteTextX); if (notification != null) { setAbsoluteTextXNoNotification(absoluteTextX); hasChanged(notification); } } public void setAbsoluteTextXNoNotification(double absoluteTextX) { this.absoluteTextX = absoluteTextX; } public double getAbsoluteTextY() { return absoluteTextY; } public final void setAbsoluteTextY(double absoluteTextY) { FGENotification notification = requireChange(Parameters.absoluteTextY, absoluteTextY); if (notification != null) { setAbsoluteTextYNoNotification(absoluteTextY); hasChanged(notification); } } public void setAbsoluteTextYNoNotification(double absoluteTextY) { this.absoluteTextY = absoluteTextY; } public boolean getIsFocusable() { return isFocusable; } public void setIsFocusable(boolean isFocusable) { FGENotification notification = requireChange(Parameters.isFocusable, isFocusable); if (notification != null) { this.isFocusable = isFocusable; hasChanged(notification); } } public boolean getDrawControlPointsWhenFocused() { return drawControlPointsWhenFocused; } public void setDrawControlPointsWhenFocused(boolean aFlag) { FGENotification notification = requireChange(Parameters.drawControlPointsWhenFocused, aFlag); if (notification != null) { drawControlPointsWhenFocused = aFlag; hasChanged(notification); } } public boolean getIsSelectable() { return isSelectable; } public void setIsSelectable(boolean isSelectable) { FGENotification notification = requireChange(Parameters.isSelectable, isSelectable); if (notification != null) { this.isSelectable = isSelectable; hasChanged(notification); } } public boolean getDrawControlPointsWhenSelected() { return drawControlPointsWhenSelected; } public void setDrawControlPointsWhenSelected(boolean aFlag) { FGENotification notification = requireChange(Parameters.drawControlPointsWhenSelected, aFlag); if (notification != null) { drawControlPointsWhenSelected = aFlag; hasChanged(notification); } } public String getText() { return text; } public void setText(String text) { FGENotification notification = requireChange(Parameters.text, text); if (notification != null) { setTextNoNotification(text); hasChanged(notification); } } public void setTextNoNotification(String text) { this.text = text; } public String getMultilineText() { return getText(); } public void setMultilineText(String text) { setText(text); } public boolean getHasText() { return hasText; } public void setHasText(boolean hasText) { FGENotification notification = requireChange(Parameters.hasText, hasText); if (notification != null) { this.hasText = hasText; hasChanged(notification); } } public boolean getIsMultilineAllowed() { return multilineAllowed; } public void setIsMultilineAllowed(boolean multilineAllowed) { FGENotification notification = requireChange(Parameters.isMultilineAllowed, multilineAllowed); if (notification != null) { this.multilineAllowed = multilineAllowed; hasChanged(notification); if (!multilineAllowed && getText() != null) { setText(getText().replaceAll("\r?\n", " ")); } } } public boolean getLineWrap() { return lineWrap; } public void setLineWrap(boolean lineWrap) { FGENotification notification = requireChange(Parameters.lineWrap, lineWrap); if (notification != null) { this.lineWrap = lineWrap; hasChanged(notification); } } public boolean getContinuousTextEditing() { return continuousTextEditing; } public void setContinuousTextEditing(boolean continuousTextEditing) { FGENotification notification = requireChange(Parameters.continuousTextEditing, continuousTextEditing); if (notification != null) { this.continuousTextEditing = continuousTextEditing; hasChanged(notification); } } public abstract boolean hasFloatingLabel(); public boolean getIsFocused() { return isFocused; } public void setIsFocused(boolean aFlag) { FGENotification notification = requireChange(Parameters.isFocused, aFlag); if (notification != null) { isFocused = aFlag; hasChanged(notification); } } public boolean getIsSelected() { return isSelected; } public void setIsSelected(boolean aFlag) { if (getParentGraphicalRepresentation() != null && aFlag) { getParentGraphicalRepresentation().moveToTop(this); } FGENotification notification = requireChange(Parameters.isSelected, aFlag); if (notification != null) { isSelected = aFlag; hasChanged(notification); } } public boolean getIsReadOnly() { return readOnly; } public void setIsReadOnly(boolean readOnly) { FGENotification notification = requireChange(Parameters.isReadOnly, readOnly); if (notification != null) { this.readOnly = readOnly; hasChanged(notification); } } public boolean getIsLabelEditable() { return labelEditable; } public void setIsLabelEditable(boolean labelEditable) { FGENotification notification = requireChange(Parameters.isLabelEditable, labelEditable); if (notification != null) { this.labelEditable = labelEditable; hasChanged(notification); } } public boolean shouldBeDisplayed() { // logger.info("For "+this+" getIsVisible()="+getIsVisible()+" getContainerGraphicalRepresentation()="+getContainerGraphicalRepresentation()); return getIsVisible() && getContainerGraphicalRepresentation() != null && getContainerGraphicalRepresentation().shouldBeDisplayed(); } public boolean getIsVisible() { if (isDeserializing()) { return isVisible; } return isVisible; } public void setIsVisible(boolean isVisible) { FGENotification notification = requireChange(Parameters.isVisible, isVisible); if (notification != null) { this.isVisible = isVisible; hasChanged(notification); } } public boolean hasText() { return getText() != null && !getText().trim().equals(""); } // ******************************************************************************* // * Methods * // ******************************************************************************* public abstract int getViewX(double scale); public abstract int getViewY(double scale); public abstract int getViewWidth(double scale); public abstract int getViewHeight(double scale); public Rectangle getViewBounds(double scale) { Rectangle bounds = new Rectangle(); bounds.x = getViewX(scale); bounds.y = getViewY(scale); bounds.width = getViewWidth(scale); bounds.height = getViewHeight(scale); return bounds; } public FGERectangle getNormalizedBounds() { return new FGERectangle(0, 0, 1, 1, Filling.FILLED); } public Point getLabelLocation(double scale) { return new Point((int) (getAbsoluteTextX() * scale + getViewX(scale)), (int) (getAbsoluteTextY() * scale + getViewY(scale))); } public Dimension getLabelDimension(double scale) { Dimension d; if (labelMetricsProvider != null) { d = labelMetricsProvider.getScaledPreferredDimension(scale); } else { d = new Dimension(0, 0); } return d; } public void setLabelLocation(Point point, double scale) { setAbsoluteTextX((point.x - getViewX(scale)) / scale); setAbsoluteTextY((point.y - getViewY(scale)) / scale); } public Rectangle getLabelBounds(double scale) { return new Rectangle(getLabelLocation(scale), getLabelDimension(scale)); } // ******************************************************************************* // * Methods * // ******************************************************************************* public void paint(Graphics g, DrawingController<?> controller) { Graphics2D g2 = (Graphics2D) g; DrawUtils.turnOnAntiAlising(g2); DrawUtils.setRenderQuality(g2); DrawUtils.setColorRenderQuality(g2); } // ******************************************************************************* // * Utils * // ******************************************************************************* protected void notifyChange(GRParameter parameter, Object oldValue, Object newValue) { // Never notify unchanged values if (oldValue != null && newValue != null && oldValue.equals(newValue)) { return; } hasChanged(new FGENotification(parameter, oldValue, newValue)); /*propagateConstraintsAfterModification(parameter); setChanged(); notifyObservers(new FGENotification(parameter, oldValue, newValue));*/ } public void notifyChange(GRParameter parameter) { notifyChange(parameter, null, null); } public void notifyAttributeChange(GRParameter parameter) { notifyChange(parameter); } /** * Build and return a new notification for a potential parameter change, given a new value. Change is required if values are different * considering equals() method * * @param parameter * @param value * @param useEquals * @return */ protected FGENotification requireChange(GRParameter parameter, Object value) { return requireChange(parameter, value, true); } /** * Build and return a new notification for a potential parameter change, given a new value. Change is required if values are different * considering: * <ul> * <li>If useEquals flag set to true, equals() method * <li> * <li>If useEquals flag set to false, same reference for objects, same value for primitives</li> * </ul> * * @param parameter * @param value * @param useEquals * @return */ protected FGENotification requireChange(GRParameter parameter, Object value, boolean useEquals) { Class<?> type = getTypeForKey(parameter.name()); Object oldValue = null; if (type.isPrimitive()) { if (type == Boolean.TYPE) { oldValue = booleanValueForKey(parameter.name()); } if (type == Integer.TYPE) { oldValue = integerValueForKey(parameter.name()); } if (type == Short.TYPE) { oldValue = shortValueForKey(parameter.name()); } if (type == Long.TYPE) { oldValue = longValueForKey(parameter.name()); } if (type == Float.TYPE) { oldValue = floatValueForKey(parameter.name()); } if (type == Double.TYPE) { oldValue = doubleValueForKey(parameter.name()); } if (type == Byte.TYPE) { oldValue = byteValueForKey(parameter.name()); } if (type == Character.TYPE) { oldValue = characterForKey(parameter.name()); } } else { oldValue = objectForKey(parameter.name()); if (value == oldValue && value != null && !value.getClass().isEnum()) { // logger.warning(parameter.name() + ": require change called for same object: aren't you wrong ???"); } } // System.out.println("param: "+parameterKey.name()+" value="+oldValue+" value="+value); if (oldValue == null) { if (value == null) { return null; // No change } else { return new FGENotification(parameter, oldValue, value); } } else { if (useEquals) { if (oldValue.equals(value)) { return null; // No change } else { return new FGENotification(parameter, oldValue, value); } } else { if (oldValue == value) { return null; // No change } else { return new FGENotification(parameter, oldValue, value); } } } } public void notify(FGENotification notification) { hasChanged(notification); } /** * This method is called whenever a notification is triggered from GR model * * @param notification */ protected void hasChanged(FGENotification notification) { if (logger.isLoggable(Level.FINE)) { logger.fine("Change attribute " + notification.parameter + " for object " + this + " was: " + notification.oldValue + " is now: " + notification.newValue); } propagateConstraintsAfterModification(notification.parameter); setChanged(); notifyObservers(notification); getPropertyChangeSupport().firePropertyChange(notification.propertyName(), notification.oldValue, notification.newValue); } @Override public String getInspectorName() { return "GraphicalRepresentation.inspector"; } public boolean isShape() { return this instanceof ShapeGraphicalRepresentation; } public boolean isConnector() { return this instanceof ConnectorGraphicalRepresentation; } public boolean isDrawing() { return this instanceof DrawingGraphicalRepresentation; } public void notifyDrawableAdded(GraphicalRepresentation<?> addedGR) { addedGR.updateBindingModel(); // logger.info(">>>>>>>>>> NEW GraphicalRepresentationAdded"); setChanged(); notifyObservers(new GraphicalRepresentationAdded(addedGR)); } public void notifyDrawableRemoved(GraphicalRepresentation<?> removedGR) { removedGR.updateBindingModel(); setChanged(); notifyObservers(new GraphicalRepresentationRemoved(removedGR)); } // ******************************************************************************* // * Observer implementation * // ******************************************************************************* @Override public void update(Observable observable, Object notification) { if (observable instanceof TextStyle) { notifyAttributeChange(Parameters.textStyle); } } // ******************************************************************************* // * Coordinates manipulation * // ******************************************************************************* /** * Convert a point relative to the view representing source drawable, with supplied scale, in a point relative to the view representing * destination drawable * * @param source * graphical representation of drawable represented in the source view * @param point * point to convert * @param destination * graphical representation of drawable represented in the destination view * @param scale * the scale to be used to perform this conversion * @return */ public static Point convertPoint(GraphicalRepresentation<?> source, Point point, GraphicalRepresentation<?> destination, double scale) { if (source != destination) { AffineTransform at = convertCoordinatesAT(source, destination, scale); return (Point) at.transform(point, new Point()); } else { return new Point(point); } } /** * Convert a rectangle coordinates expressed in the view representing source drawable, with supplied scale, in coordinates expressed in * the view representing destination drawable * * @param source * graphical representation of drawable represented in the source view * @param aRectangle * rectangle to convert * @param destination * graphical representation of drawable represented in the destination view * @param scale * the scale to be used to perform this conversion * @return */ public static Rectangle convertRectangle(GraphicalRepresentation<?> source, Rectangle aRectangle, GraphicalRepresentation<?> destination, double scale) { Point point = new Point(aRectangle.x, aRectangle.y); if (source != destination) { point = convertPoint(source, point, destination, scale); } return new Rectangle(point.x, point.y, aRectangle.width, aRectangle.height); } /** * Build and return a new AffineTransform allowing to perform coordinates conversion from the view representing source drawable, with * supplied scale, to the view representing destination drawable * * @param source * @param destination * @param scale * @return */ public static AffineTransform convertCoordinatesAT(GraphicalRepresentation<?> source, GraphicalRepresentation<?> destination, double scale) { if (source != destination) { AffineTransform returned = convertFromDrawableToDrawingAT(source, scale); returned.preConcatenate(convertFromDrawingToDrawableAT(destination, scale)); return returned; } else { return new AffineTransform(); } } /** * Convert a point defined in coordinates system related to "source" graphical representation to related drawing graphical * representation * * @param destination * @param point * @param scale * @return */ public static Point convertPointFromDrawableToDrawing(GraphicalRepresentation<?> source, Point point, double scale) { AffineTransform at = convertFromDrawableToDrawingAT(source, scale); return (Point) at.transform(point, new Point()); } /** * * Build a new AffineTransform allowing to convert coordinates from coordinate system defined by "source" graphical representation to * related drawing graphical representation * * @param source * @param scale * @return */ public static AffineTransform convertFromDrawableToDrawingAT(GraphicalRepresentation<?> source, double scale) { double tx = 0; double ty = 0; if (source == null) { logger.warning("Called convertFromDrawableToDrawingAT() for null graphical representation (source)"); return new AffineTransform(); } Object current = source.getDrawable(); while (current != source.getDrawing().getModel()) { if (source.getDrawing().getGraphicalRepresentation(current) == null) { throw new IllegalArgumentException( "Drawable " + current + " has no graphical representation.\nDevelopper note: Use GraphicalRepresentation.areElementsConnectedInGraphicalHierarchy(GraphicalRepresentation,GraphicalRepresentation) to prevent such cases."); } if (source.getDrawing().getContainer(current) == null) { throw new IllegalArgumentException( "Drawable " + current + " has no container.\nDevelopper note: Use GraphicalRepresentation.areElementsConnectedInGraphicalHierarchy(GraphicalRepresentation,GraphicalRepresentation) to prevent such cases."); } tx += source.getDrawing().getGraphicalRepresentation(current).getViewX(scale); ty += source.getDrawing().getGraphicalRepresentation(current).getViewY(scale); current = source.getDrawing().getContainer(current); } return AffineTransform.getTranslateInstance(tx, ty); } /** * Convert a point defined in related drawing graphical representation coordinates system to the one defined by "destination" graphical * representation * * @param destination * @param point * @param scale * @return */ public static Point convertPointFromDrawingToDrawable(GraphicalRepresentation<?> destination, Point point, double scale) { AffineTransform at = convertFromDrawingToDrawableAT(destination, scale); return (Point) at.transform(point, new Point()); } /** * * Build a new AffineTransform allowing to convert coordinates from coordinate system defined by related drawing graphical * representation to the one defined by "destination" graphical representation * * @param destination * @param scale * @return */ public static AffineTransform convertFromDrawingToDrawableAT(GraphicalRepresentation<?> destination, double scale) { double tx = 0; double ty = 0; if (destination == null) { logger.warning("Called convertFromDrawingToDrawableAT() for null graphical representation (destination)"); return new AffineTransform(); } Object current = destination.getDrawable(); while (current != destination.getDrawing().getModel()) { if (destination.getDrawing().getContainer(current) == null) { throw new IllegalArgumentException( "Drawable " + current + " has no container.\nDevelopper note: Use GraphicalRepresentation.areElementsConnectedInGraphicalHierarchy(GraphicalRepresentation,GraphicalRepresentation) to prevent such cases."); } tx -= destination.getDrawing().getGraphicalRepresentation(current).getViewX(scale); ty -= destination.getDrawing().getGraphicalRepresentation(current).getViewY(scale); current = destination.getDrawing().getContainer(current); } return AffineTransform.getTranslateInstance(tx, ty); } public final Point convertNormalizedPointToViewCoordinates(double x, double y, double scale) { AffineTransform at = convertNormalizedPointToViewCoordinatesAT(scale); FGEPoint returned = new FGEPoint(); at.transform(new FGEPoint(x, y), returned); return new Point((int) returned.x, (int) returned.y); } public Rectangle convertNormalizedRectangleToViewCoordinates(FGERectangle r, double scale) { FGEPoint p1 = new FGEPoint(r.x, r.y); FGEPoint p2 = new FGEPoint(r.x + r.width, r.y + r.height); Point pp1 = convertNormalizedPointToViewCoordinates(p1, scale); Point pp2 = convertNormalizedPointToViewCoordinates(p2, scale); return new Rectangle(pp1.x, pp1.y, pp2.x - pp1.x, pp2.y - pp1.y); } public abstract AffineTransform convertNormalizedPointToViewCoordinatesAT(double scale); public final FGEPoint convertViewCoordinatesToNormalizedPoint(int x, int y, double scale) { AffineTransform at = convertViewCoordinatesToNormalizedPointAT(scale); FGEPoint returned = new FGEPoint(); at.transform(new FGEPoint(x, y), returned); return returned; } public abstract AffineTransform convertViewCoordinatesToNormalizedPointAT(double scale); public Point convertNormalizedPointToViewCoordinates(FGEPoint p, double scale) { return convertNormalizedPointToViewCoordinates(p.x, p.y, scale); } public FGEPoint convertViewCoordinatesToNormalizedPoint(Point p, double scale) { return convertViewCoordinatesToNormalizedPoint(p.x, p.y, scale); } /** * Convert a point relative to the normalized coordinates system from source drawable to the normalized coordinates system from * destination drawable * * @param source * @param point * @param destination * @return */ public static FGEPoint convertNormalizedPoint(GraphicalRepresentation<?> source, FGEPoint point, GraphicalRepresentation<?> destination) { if (point == null) { return null; } AffineTransform at = convertNormalizedCoordinatesAT(source, destination); return (FGEPoint) at.transform(point, new FGEPoint()); } /** * Build and return an AffineTransform allowing to convert locations relative to the normalized coordinates system from source drawable * to the normalized coordinates system from destination drawable * * @param source * @param point * @param destination * @return */ public static AffineTransform convertNormalizedCoordinatesAT(GraphicalRepresentation<?> source, GraphicalRepresentation<?> destination) { if (source == null) { logger.warning("null source !"); } AffineTransform returned = source.convertNormalizedPointToViewCoordinatesAT(1.0); returned.preConcatenate(convertCoordinatesAT(source, destination, 1.0)); returned.preConcatenate(destination.convertViewCoordinatesToNormalizedPointAT(1.0)); return returned; } public FGEPoint convertRemoteViewCoordinatesToLocalNormalizedPoint(Point p, GraphicalRepresentation<?> source, double scale) { if (!isConnectedToDrawing() || !source.isConnectedToDrawing()) { return new FGEPoint(p.x / scale, p.y / scale); } Point pointRelativeToCurrentView = convertPoint(source, p, this, scale); return convertViewCoordinatesToNormalizedPoint(pointRelativeToCurrentView, scale); } public FGEPoint convertLocalViewCoordinatesToRemoteNormalizedPoint(Point p, GraphicalRepresentation<?> destination, double scale) { if (!isConnectedToDrawing() || !destination.isConnectedToDrawing()) { return new FGEPoint(p.x * scale, p.y * scale); } Point pointRelativeToRemoteView = convertPoint(this, p, destination, scale); return destination.convertViewCoordinatesToNormalizedPoint(pointRelativeToRemoteView, scale); } public Point convertLocalNormalizedPointToRemoteViewCoordinates(FGEPoint p, GraphicalRepresentation<?> destination, double scale) { Point point = convertNormalizedPointToViewCoordinates(p, scale); return convertPoint(this, point, destination, scale); } public Rectangle convertLocalNormalizedRectangleToRemoteViewCoordinates(FGERectangle r, GraphicalRepresentation<?> destination, double scale) { FGEPoint p1 = new FGEPoint(r.x, r.y); FGEPoint p2 = new FGEPoint(r.x + r.width, r.y + r.height); Point pp1 = convertLocalNormalizedPointToRemoteViewCoordinates(p1, destination, scale); Point pp2 = convertLocalNormalizedPointToRemoteViewCoordinates(p2, destination, scale); return new Rectangle(pp1.x, pp1.y, pp2.x - pp1.x, pp2.y - pp1.y); } public Point convertRemoteNormalizedPointToLocalViewCoordinates(FGEPoint p, GraphicalRepresentation<?> source, double scale) { Point point = source.convertNormalizedPointToViewCoordinates(p, scale); return convertPoint(source, point, this, scale); } public boolean isRegistered() { return isRegistered; } public void setRegistered(boolean aFlag) { isRegistered = aFlag; } public Vector<MouseClickControl> getMouseClickControls() { return mouseClickControls; } public void setMouseClickControls(Vector<MouseClickControl> mouseClickControls) { FGENotification notification = requireChange(Parameters.mouseClickControls, mouseClickControls); if (notification != null) { this.mouseClickControls.addAll(mouseClickControls); hasChanged(notification); } } public void addToMouseClickControls(MouseClickControl mouseClickControl) { addToMouseClickControls(mouseClickControl, false); } public void addToMouseClickControls(MouseClickControl mouseClickControl, boolean isPrioritar) { if (isPrioritar) { mouseClickControls.insertElementAt(mouseClickControl, 0); } else { mouseClickControls.add(mouseClickControl); } setChanged(); notifyObservers(new FGENotification(Parameters.mouseClickControls, mouseClickControls, mouseClickControls)); } public void removeFromMouseClickControls(MouseClickControl mouseClickControl) { mouseClickControls.remove(mouseClickControl); setChanged(); notifyObservers(new FGENotification(Parameters.mouseClickControls, mouseClickControls, mouseClickControls)); } public Vector<MouseDragControl> getMouseDragControls() { return mouseDragControls; } public void setMouseDragControls(Vector<MouseDragControl> mouseDragControls) { FGENotification notification = requireChange(Parameters.mouseDragControls, mouseDragControls); if (notification != null) { this.mouseDragControls.addAll(mouseDragControls); hasChanged(notification); } } public void addToMouseDragControls(MouseDragControl mouseDragControl) { addToMouseDragControls(mouseDragControl, false); } public void addToMouseDragControls(MouseDragControl mouseDragControl, boolean isPrioritar) { if (isPrioritar) { mouseDragControls.insertElementAt(mouseDragControl, 0); } else { mouseDragControls.add(mouseDragControl); } setChanged(); notifyObservers(new FGENotification(Parameters.mouseDragControls, mouseDragControls, mouseDragControls)); } public void removeFromMouseDragControls(MouseDragControl mouseDragControl) { mouseDragControls.remove(mouseDragControl); setChanged(); notifyObservers(new FGENotification(Parameters.mouseDragControls, mouseDragControls, mouseDragControls)); } public MouseClickControl createMouseClickControl() { MouseClickControl returned = MouseClickControl.makeMouseClickControl("Noname", MouseButton.LEFT, 1); addToMouseClickControls(returned); return returned; } public void deleteMouseClickControl(MouseClickControl mouseClickControl) { removeFromMouseClickControls(mouseClickControl); } public boolean isMouseClickControlDeletable(MouseClickControl mouseClickControl) { return true; } public MouseDragControl createMouseDragControl() { MouseDragControl returned = MouseDragControl.makeMouseDragControl("Noname", MouseButton.LEFT); addToMouseDragControls(returned); return returned; } public void deleteMouseDragControl(MouseDragControl mouseDragControl) { removeFromMouseDragControls(mouseDragControl); } public boolean isMouseDragControlDeletable(MouseDragControl mouseDragControl) { return true; } public abstract boolean isContainedInSelection(Rectangle drawingViewSelection, double scale); public void notifyLabelWillBeEdited() { setChanged(); notifyObservers(new LabelWillEdit()); } public void notifyLabelHasBeenEdited() { setChanged(); notifyObservers(new LabelHasEdited()); } public void notifyLabelWillMove() { setChanged(); notifyObservers(new LabelWillMove()); } public void notifyLabelHasMoved() { setChanged(); notifyObservers(new LabelHasMoved()); } // Override when required public void notifyObjectHierarchyWillBeUpdated() { setRegistered(false); if (ancestors != null) { ancestors.clear(); } ancestors = null; } // Override when required public void notifyObjectHierarchyHasBeenUpdated() { setRegistered(true); if (ancestors != null) { ancestors.clear(); } ancestors = null; } public String getToolTipText() { return toolTipText; } public void setToolTipText(String tooltipText) { FGENotification notification = requireChange(Parameters.toolTipText, tooltipText); if (notification != null) { this.toolTipText = tooltipText; hasChanged(notification); } } public HorizontalTextAlignment getHorizontalTextAlignment() { return horizontalTextAlignment; } public void setHorizontalTextAlignment(HorizontalTextAlignment horizontalTextAlignment) { FGENotification notification = requireChange(Parameters.horizontalTextAlignment, horizontalTextAlignment); if (notification != null) { this.horizontalTextAlignment = horizontalTextAlignment; hasChanged(notification); } } public VerticalTextAlignment getVerticalTextAlignment() { return verticalTextAlignment; } public void setVerticalTextAlignment(VerticalTextAlignment verticalTextAlignment) { FGENotification notification = requireChange(Parameters.verticalTextAlignment, verticalTextAlignment); if (notification != null) { this.verticalTextAlignment = verticalTextAlignment; hasChanged(notification); } } public ParagraphAlignment getParagraphAlignment() { return paragraphAlignment; } public void setParagraphAlignment(ParagraphAlignment paragraphAlignment) { FGENotification notification = requireChange(Parameters.paragraphAlignment, paragraphAlignment); if (notification != null) { this.paragraphAlignment = paragraphAlignment; hasChanged(notification); } } // ******************************************************************************* // * Layout * // ******************************************************************************* public void performRandomLayout(double width, double height) { Random r = new Random(); for (GraphicalRepresentation<?> gr : getContainedGraphicalRepresentations()) { if (gr instanceof ShapeGraphicalRepresentation) { ShapeGraphicalRepresentation<?> child = (ShapeGraphicalRepresentation<?>) gr; child.setLocation(new FGEPoint(r.nextDouble() * (width - child.getWidth()), r.nextDouble() * (height - child.getHeight()))); } } } public void performAutoLayout(double width, double height) { } public Stroke getSpecificStroke() { return specificStroke; } public void setSpecificStroke(Stroke aStroke) { specificStroke = aStroke; } // ******************************************************************************* // * Bindings: constraint expressions // ******************************************************************************* public boolean isRootGraphicalRepresentation() { return getParentGraphicalRepresentation() == null; } public GraphicalRepresentation<?> getRootGraphicalRepresentation() { GraphicalRepresentation<?> current = this; while (current != null && !current.isRootGraphicalRepresentation()) { current = current.getParentGraphicalRepresentation(); } return current; } private BindingModel _bindingModel = null; @Override public final BindingModel getBindingModel() { if (_bindingModel == null) { createBindingModel(); } return _bindingModel; /*if (isRootGraphicalRepresentation()) { return _bindingModel; } return getRootGraphicalRepresentation().getBindingModel();*/ } @Override public BindingFactory getBindingFactory() { return BINDING_FACTORY; } public void updateBindingModel() { logger.fine("updateBindingModel()"); /*if (getRootGraphicalRepresentation() != null) { getRootGraphicalRepresentation()._bindingModel = null; getRootGraphicalRepresentation().createBindingModel(); }*/ createBindingModel(); } private void createBindingModel() { _bindingModel = new BindingModel(); _bindingModel.addToBindingVariables(new GRBindingFactory.ComponentPathElement("this", this, this)); if (getParentGraphicalRepresentation() != null) { _bindingModel.addToBindingVariables(new GRBindingFactory.ComponentPathElement("parent", getParentGraphicalRepresentation(), this)); } _bindingModel.addToBindingVariables(new ComponentsBindingVariable(this)); Iterator<GraphicalRepresentation<?>> it = allContainedGRIterator(); while (it.hasNext()) { GraphicalRepresentation<?> subComponent = it.next(); // _bindingModel.addToBindingVariables(new ComponentBindingVariable(subComponent)); subComponent.notifiedBindingModelRecreated(); } logger.fine("Created binding model at root component level:\n" + _bindingModel); } @Override public Object getValue(BindingVariable variable) { BindingVariable bv = getBindingModel().bindingVariableNamed(variable.getVariableName()); /*if (bv instanceof ComponentBindingVariable) { return ((ComponentBindingVariable) bv).getReference(); } else*/if (bv instanceof ComponentPathElement) { return ((ComponentPathElement) bv).getComponent(); } else if (bv instanceof ComponentsBindingVariable) { return getRootGraphicalRepresentation(); } else { logger.warning("Could not find variable named " + variable); return null; } } public void notifiedBindingModelRecreated() { logger.fine("notifiedBindingModelRecreated()"); } public void notifyBindingChanged(DataBinding binding) { logger.fine("notifyBindingChanged() for " + binding); } public List<GraphicalRepresentation<?>> retrieveAllContainedGR() { if (!isValidated()) { return EMPTY_GR_VECTOR; } List<GraphicalRepresentation<?>> returned = new ArrayList<GraphicalRepresentation<?>>(); addAllContainedGR(this, returned); return returned; } private void addAllContainedGR(GraphicalRepresentation<?> gr, List<GraphicalRepresentation<?>> returned) { if (gr.getContainedGraphicalRepresentations() == null) { return; } for (GraphicalRepresentation<?> gr2 : gr.getContainedGraphicalRepresentations()) { returned.add(gr2); addAllContainedGR(gr2, returned); } } public Iterator<GraphicalRepresentation<?>> allGRIterator() { List<GraphicalRepresentation<?>> returned = getRootGraphicalRepresentation().retrieveAllContainedGR(); if (!isValidated()) { return returned.iterator(); } returned.add(0, getRootGraphicalRepresentation()); return returned.iterator(); } public Iterator<GraphicalRepresentation<?>> allContainedGRIterator() { List<GraphicalRepresentation<?>> allGR = retrieveAllContainedGR(); if (!isValidated()) { return allGR.iterator(); } if (allGR == null) { return new Iterator<GraphicalRepresentation<?>>() { @Override public boolean hasNext() { return false; } @Override public GraphicalRepresentation<?> next() { return null; } @Override public void remove() { } }; } else { return allGR.iterator(); } } public Vector<ConstraintDependency> getDependancies() { return dependancies; } public Vector<ConstraintDependency> getAlterings() { return alterings; } public void declareDependantOf(GraphicalRepresentation<?> aComponent, GRParameter requiringParameter, GRParameter requiredParameter) throws DependencyLoopException { // logger.info("Component "+this+" depends of "+aComponent); if (aComponent == this) { logger.warning("Forbidden reflexive dependancies"); return; } // Look if this dependancy may cause a loop in dependancies try { Vector<GraphicalRepresentation<?>> actualDependancies = new Vector<GraphicalRepresentation<?>>(); actualDependancies.add(aComponent); searchLoopInDependenciesWith(aComponent, actualDependancies); } catch (DependencyLoopException e) { logger.warning("Forbidden loop in dependancies: " + e.getMessage()); throw e; } ConstraintDependency newDependancy = new ConstraintDependency(this, requiringParameter, aComponent, requiredParameter); if (!dependancies.contains(newDependancy)) { dependancies.add(newDependancy); logger.info("Parameter " + requiringParameter + " of GR " + this + " depends of parameter " + requiredParameter + " of GR " + aComponent); } if (!aComponent.alterings.contains(newDependancy)) { aComponent.alterings.add(newDependancy); } } private void searchLoopInDependenciesWith(GraphicalRepresentation<?> aComponent, Vector<GraphicalRepresentation<?>> actualDependancies) throws DependencyLoopException { for (ConstraintDependency dependancy : aComponent.dependancies) { GraphicalRepresentation<?> c = dependancy.requiredGR; if (c == this) { throw new DependencyLoopException(actualDependancies); } Vector<GraphicalRepresentation<?>> newVector = new Vector<GraphicalRepresentation<?>>(); newVector.addAll(actualDependancies); newVector.add(c); searchLoopInDependenciesWith(c, newVector); } } protected static class DependencyLoopException extends Exception { private Vector<GraphicalRepresentation<?>> dependencies; public DependencyLoopException(Vector<GraphicalRepresentation<?>> dependancies) { this.dependencies = dependancies; } @Override public String getMessage() { return "DependencyLoopException: " + dependencies; } } protected void propagateConstraintsAfterModification(GRParameter parameter) { for (ConstraintDependency dependency : alterings) { if (dependency.requiredParameter == parameter) { dependency.requiringGR.computeNewConstraint(dependency); } } } protected void computeNewConstraint(ConstraintDependency dependency) { // None known at this level } private Vector<GRVariable> variables = new Vector<GRVariable>(); public Vector<GRVariable> getVariables() { return variables; } public void setVariables(Vector<GRVariable> variables) { this.variables = variables; } public void addToVariables(GRVariable v) { variables.add(v); setChanged(); notifyObservers(new FGENotification(Parameters.variables, variables, variables)); } public void removeFromVariables(GRVariable v) { variables.remove(v); setChanged(); notifyObservers(new FGENotification(Parameters.variables, variables, variables)); } public GRVariable createStringVariable() { GRVariable returned = new GRVariable("variable", GRVariableType.String, "value"); addToVariables(returned); return returned; } public GRVariable createIntegerVariable() { GRVariable returned = new GRVariable("variable", GRVariableType.Integer, "0"); addToVariables(returned); return returned; } public void deleteVariable(GRVariable v) { removeFromVariables(v); } @Override public final PropertyChangeSupport getPropertyChangeSupport() { if (pcSupport == null && !isDeleted) { pcSupport = new PropertyChangeSupport(this); } return pcSupport; } @Override public String getDeletedProperty() { return "delete"; } private boolean validated = false; protected LabelMetricsProvider labelMetricsProvider; /** * Return boolean indicating if this graphical representation is validated. A validated graphical representation is a graphical * representation fully embedded in its graphical representation tree, which means that parent and child are set and correct, and that * start and end shapes are set for connectors * * * @return */ public boolean isValidated() { return validated; } public void setValidated(boolean validated) { this.validated = validated; } public LabelMetricsProvider getLabelMetricsProvider() { return labelMetricsProvider; } public void setLabelMetricsProvider(LabelMetricsProvider labelMetricsProvider) { this.labelMetricsProvider = labelMetricsProvider; } /** * Returns the number of pixels available for the label considering its positioning. This method is used in case of line wrapping. * * @param scale * @return */ public int getAvailableLabelWidth(double scale) { return Integer.MAX_VALUE; } }