/* * (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.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.List; import java.util.Observable; import java.util.Observer; import java.util.Vector; import java.util.logging.Logger; import org.openflexo.fge.connectors.Connector; import org.openflexo.fge.connectors.Connector.ConnectorType; import org.openflexo.fge.connectors.ConnectorSymbol.EndSymbolType; import org.openflexo.fge.connectors.ConnectorSymbol.MiddleSymbolType; import org.openflexo.fge.connectors.ConnectorSymbol.StartSymbolType; 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.cp.ControlArea; import org.openflexo.fge.cp.ControlPoint; import org.openflexo.fge.geom.FGEGeometricObject.Filling; import org.openflexo.fge.geom.FGEPoint; import org.openflexo.fge.geom.FGERectangle; import org.openflexo.fge.graphics.FGEConnectorGraphics; import org.openflexo.fge.graphics.ForegroundStyle; import org.openflexo.fge.notifications.ConnectorModified; 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.view.ConnectorView; import org.openflexo.toolbox.ToolBox; public class ConnectorGraphicalRepresentation<O> extends GraphicalRepresentation<O> implements Observer { private static final Logger logger = Logger.getLogger(ConnectorGraphicalRepresentation.class.getPackage().getName()); // ******************************************************************************* // * Parameters * // ******************************************************************************* public static enum Parameters implements GRParameter { connector, foreground, selectedForeground, focusedForeground, hasSelectedForeground, hasFocusedForeground, startSymbol, endSymbol, middleSymbol, startSymbolSize, endSymbolSize, middleSymbolSize, relativeMiddleSymbolLocation, applyForegroundToSymbols, debugCoveringArea } private Connector connector = null; private ForegroundStyle foreground; private ForegroundStyle selectedForeground = null; private ForegroundStyle focusedForeground = null; private boolean hasSelectedForeground = false; private boolean hasFocusedForeground = false; private StartSymbolType startSymbol = StartSymbolType.NONE; private EndSymbolType endSymbol = EndSymbolType.NONE; private MiddleSymbolType middleSymbol = MiddleSymbolType.NONE; private double startSymbolSize = 10.0; private double endSymbolSize = 10.0; private double middleSymbolSize = 10.0; private double relativeMiddleSymbolLocation = 0.5; // default is in the middle ! private boolean applyForegroundToSymbols = true; private boolean debugCoveringArea = false; // ******************************************************************************* // * Inner classes * // ******************************************************************************* // ******************************************************************************* // * Fields * // ******************************************************************************* private ShapeGraphicalRepresentation<?> startObject; private ShapeGraphicalRepresentation<?> endObject; protected FGEConnectorGraphics graphics; // ******************************************************************************* // * Constructor * // ******************************************************************************* // Never use this constructor (used during deserialization only) public ConnectorGraphicalRepresentation() { this(null, null, null, null, null); } public ConnectorGraphicalRepresentation(ConnectorType aConnectorType, ShapeGraphicalRepresentation<?> aStartObject, ShapeGraphicalRepresentation<?> anEndObject, O aDrawable, Drawing<?> aDrawing) { super(aDrawable, aDrawing); layer = FGEConstants.DEFAULT_CONNECTOR_LAYER; setStartObject(aStartObject); setEndObject(anEndObject); setConnectorType(aConnectorType); graphics = new FGEConnectorGraphics(this); foreground = ForegroundStyle.makeStyle(Color.BLACK); // foreground.setGraphicalRepresentation(this); foreground.addObserver(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)); } } // *************************************************************************** // * Deletion * // *************************************************************************** @Override public void delete() { if (foreground != null) { foreground.deleteObserver(this); } super.delete(); disableStartObjectObserving(); disableEndObjectObserving(); } @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; } @Override public final void setsWith(GraphicalRepresentation<?> gr) { super.setsWith(gr); if (gr instanceof ConnectorGraphicalRepresentation) { for (Parameters p : Parameters.values()) { if (p != Parameters.connector) { _setParameterValueWith(p, gr); } } Connector connectorToCopy = ((ConnectorGraphicalRepresentation<?>) gr).getConnector(); Connector clone = connectorToCopy.clone(); setConnector(clone); } } @Override public final void setsWith(GraphicalRepresentation<?> gr, GRParameter... exceptedParameters) { super.setsWith(gr, exceptedParameters); if (gr instanceof ConnectorGraphicalRepresentation) { for (Parameters p : Parameters.values()) { boolean excepted = false; for (GRParameter ep : exceptedParameters) { if (p == ep) { excepted = true; } } if (p != Parameters.connector && !excepted) { _setParameterValueWith(p, gr); } } Connector connectorToCopy = ((ConnectorGraphicalRepresentation<?>) gr).getConnector(); Connector clone = connectorToCopy.clone(); setConnector(clone); } } // ******************************************************************************* // * Accessors * // ******************************************************************************* public Connector getConnector() { return connector; } public void setConnector(Connector aConnector) { if (aConnector != null) { aConnector.setGraphicalRepresentation(this); } FGENotification notification = requireChange(Parameters.connector, aConnector); if (notification != null) { this.connector = aConnector; hasChanged(notification); } } public ForegroundStyle getForeground() { return foreground; } public void setForeground(ForegroundStyle aForeground) { FGENotification notification = requireChange(Parameters.foreground, aForeground); 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; } @Override public final boolean shouldBeDisplayed() { return super.shouldBeDisplayed(); } public void notifyConnectorChanged() { if (!isRegistered()) { return; } checkViewBounds(); setChanged(); notifyObservers(new ConnectorModified()); } public ConnectorType getConnectorType() { if (connector != null) { return connector.getConnectorType(); } return null; } public void setConnectorType(ConnectorType connectorType) { if (getConnectorType() != connectorType) { setConnector(Connector.makeConnector(connectorType, this)); } } @Override public final void setText(String text) { super.setText(text); } public ShapeGraphicalRepresentation<?> getStartObject() { return startObject; } public void setStartObject(ShapeGraphicalRepresentation<?> aStartObject) { startObject = aStartObject; if (!enabledStartObjectObserving) { enableStartObjectObserving(startObject); } } private boolean enabledStartObjectObserving = false; private Vector<Observable> observedStartObjects = new Vector<Observable>(); private boolean enabledEndObjectObserving = false; private Vector<Observable> observedEndObjects = new Vector<Observable>(); protected void enableStartObjectObserving(ShapeGraphicalRepresentation<?> aStartObject) { if (aStartObject == null || !aStartObject.isValidated()) { return; } if (enabledStartObjectObserving) { disableStartObjectObserving(); } if (aStartObject != null /*&& !enabledStartObjectObserving*/) { aStartObject.addObserver(this); observedStartObjects.add(aStartObject); if (!isDeserializing()) { for (Object o : aStartObject.getAncestors(true)) { if (getGraphicalRepresentation(o) != null) { getGraphicalRepresentation(o).addObserver(this); observedStartObjects.add(getGraphicalRepresentation(o)); } } } enabledStartObjectObserving = true; } } protected void disableStartObjectObserving(/*ShapeGraphicalRepresentation<?> aStartObject*/) { if (/*aStartObject != null &&*/enabledStartObjectObserving) { /*aStartObject.deleteObserver(this); if (!isDeserializing()) { for (Object o : aStartObject.getAncestors()) if (getGraphicalRepresentation(o) != null) getGraphicalRepresentation(o).deleteObserver(this); }*/ for (Observable o : observedStartObjects) { o.deleteObserver(this); } enabledStartObjectObserving = false; } } public ShapeGraphicalRepresentation<?> getEndObject() { return endObject; } public void setEndObject(ShapeGraphicalRepresentation<?> anEndObject) { endObject = anEndObject; if (!enabledEndObjectObserving) { enableEndObjectObserving(endObject); } } protected void enableEndObjectObserving(ShapeGraphicalRepresentation<?> anEndObject) { if (anEndObject == null || !anEndObject.isValidated()) { return; } if (enabledEndObjectObserving) { disableEndObjectObserving(); } if (anEndObject != null /*&& !enabledEndObjectObserving*/) { anEndObject.addObserver(this); observedEndObjects.add(anEndObject); if (!isDeserializing()) { for (Object o : anEndObject.getAncestors(true)) { if (getGraphicalRepresentation(o) != null) { getGraphicalRepresentation(o).addObserver(this); observedEndObjects.add(getGraphicalRepresentation(o)); } } } enabledEndObjectObserving = true; } } protected void disableEndObjectObserving(/*ShapeGraphicalRepresentation<?> anEndObject*/) { if (/*anEndObject != null &&*/enabledEndObjectObserving) { /*anEndObject.deleteObserver(this); if (!isDeserializing()) { for (Object o : anEndObject.getAncestors()) if (getGraphicalRepresentation(o) != null) getGraphicalRepresentation(o).deleteObserver(this); }*/ for (Observable o : observedEndObjects) { o.deleteObserver(this); } enabledEndObjectObserving = false; } } public void observeRelevantObjects() { enableStartObjectObserving(getStartObject()); enableEndObjectObserving(getEndObject()); } @Override public int getViewX(double scale) { return getViewBounds(scale).x; } @Override public int getViewY(double scale) { return getViewBounds(scale).y; } @Override public int getViewWidth(double scale) { return getViewBounds(scale).width; } @Override public int getViewHeight(double scale) { return getViewBounds(scale).height; } private double minX = 0.0; private double minY = 0.0; private double maxX = 1.0; private double maxY = 1.0; private void checkViewBounds() { FGERectangle r = getConnector().getConnectorUsedBounds(); if (FGEUtils.checkDoubleIsAValue(r.getMinX()) && FGEUtils.checkDoubleIsAValue(r.getMinY()) && FGEUtils.checkDoubleIsAValue(r.getMaxX()) && FGEUtils.checkDoubleIsAValue(r.getMaxY())) { minX = Math.min(r.getMinX(), 0.0); minY = Math.min(r.getMinY(), 0.0); maxX = Math.max(r.getMaxX(), 1.0); maxY = Math.max(r.getMaxY(), 1.0); } } @Override public Rectangle getViewBounds(double scale) { // return getNormalizedBounds(scale); Rectangle bounds = getNormalizedBounds(scale); /*System.out.println("Bounds="+bounds); System.out.println("minX="+minX); System.out.println("minY="+minY); System.out.println("maxX="+maxX); System.out.println("maxY="+maxY);*/ Rectangle returned = new Rectangle(); returned.x = (int) (bounds.x + (minX < 0 ? minX * bounds.width : 0)); returned.y = (int) (bounds.y + (minY < 0 ? minY * bounds.height : 0)); returned.width = (int) ((maxX - minX) * bounds.width); returned.height = (int) ((maxY - minY) * bounds.height); return returned; } public int getExtendedX(double scale) { Rectangle bounds = getNormalizedBounds(scale); return (int) (bounds.x + (minX < 0 ? minX * bounds.width : 0)); } public int getExtendedY(double scale) { Rectangle bounds = getNormalizedBounds(scale); return (int) (bounds.y + (minY < 0 ? minY * bounds.height : 0)); } /** * Return normalized bounds Those bounds corresponds to the normalized area defined as (0.0,0.0)-(1.0,1.0) enclosing EXACTELY the two * related shape bounds. Those bounds should eventually be extended to contain connector contained outside this area. */ public Rectangle getNormalizedBounds(double scale) { if (getStartObject() == null || getStartObject().isDeleted() || getEndObject() == null || getEndObject().isDeleted()) { logger.warning("Could not obtain connector bounds: start or end object is null or deleted"); logger.warning("Object: " + this + " startObject=" + getStartObject() + " endObject=" + getEndObject()); // Here, we return a (1,1)-size to avoid obtaining Infinity AffinTransform !!! return new Rectangle(0, 0, 1, 1); } if (getContainerGraphicalRepresentation() == null) { logger.warning("getNormalizedBounds() called for GR " + this + " with containerGR=null, validated=" + isValidated()); } Rectangle startBounds = getStartObject().getViewBounds(getContainerGraphicalRepresentation(), scale); Rectangle endsBounds = getEndObject().getViewBounds(getContainerGraphicalRepresentation(), scale); Rectangle bounds = new Rectangle(); Rectangle2D.union(startBounds, endsBounds, bounds); return bounds; } @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 (ControlArea<?> ca : getConnector().getControlAreas()) { if (ca instanceof ControlPoint) { ControlPoint cp = (ControlPoint) ca; 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; } @Override public AffineTransform convertNormalizedPointToViewCoordinatesAT(double scale) { Rectangle bounds = getNormalizedBounds(scale); // return AffineTransform.getScaleInstance(bounds.width, bounds.height); AffineTransform returned = AffineTransform.getTranslateInstance(minX < 0 ? -minX * bounds.width : 0, minY < 0 ? -minY * bounds.height : 0); returned.concatenate(AffineTransform.getScaleInstance(bounds.width, bounds.height)); return returned; } @Override public AffineTransform convertViewCoordinatesToNormalizedPointAT(double scale) { Rectangle bounds = getNormalizedBounds(scale); // return AffineTransform.getScaleInstance(1.0/bounds.width, 1.0/bounds.height); AffineTransform returned = AffineTransform.getTranslateInstance(minX < 0 ? minX * bounds.width : 0, minY < 0 ? minY * bounds.height : 0); returned.preConcatenate(AffineTransform.getScaleInstance(1.0 / bounds.width, 1.0 / bounds.height)); return returned; } /** * Return distance from point to connector representation with a given scale * * @param aPoint * expressed in local normalized coordinates system * @param scale * @return */ public double distanceToConnector(FGEPoint aPoint, double scale) { return connector.distanceToConnector(aPoint, scale); } // ******************************************************************************* // * Methods * // ******************************************************************************* @Override public void paint(Graphics g, DrawingController<?> controller) { if (!isRegistered()) { setRegistered(true); } super.paint(g, controller); if (getStartObject() == null || getStartObject().isDeleted()) { logger.warning("Could not paint connector: start object is null or deleted"); return; } if (getEndObject() == null || getEndObject().isDeleted()) { logger.warning("Could not paint connector: end object is null or deleted"); return; } Graphics2D g2 = (Graphics2D) g; graphics.createGraphics(g2, controller); if (FGEConstants.DEBUG) { g2.setColor(Color.PINK); g2.drawRect(0, 0, getViewWidth(controller.getScale()) - 1, getViewHeight(controller.getScale()) - 1); } if (connector != null) { if (!isValidated()) { logger.warning("paint connector requested for not validated connector graphical representation: " + this); return; } if (getStartObject() == null || getStartObject().isDeleted() || !getStartObject().isValidated()) { logger.warning("paint connector requested for invalid start object (either null, deleted or not validated) : " + this + " start=" + getStartObject()); return; } if (getEndObject() == null || getEndObject().isDeleted() || !getEndObject().isValidated()) { logger.warning("paint connector requested for invalid start object (either null, deleted or not validated) : " + this + " end=" + getEndObject()); return; } connector.paintConnector(graphics); } graphics.releaseGraphics(); } @Override public Point getLabelLocation(double scale) { Point connectorCenter = convertNormalizedPointToViewCoordinates(getConnector().getMiddleSymbolLocation(), scale); return new Point((int) (connectorCenter.x + getAbsoluteTextX() * scale + getViewX(scale)), (int) (connectorCenter.y + getAbsoluteTextY() * scale + getViewY(scale))); } @Override public void setLabelLocation(Point point, double scale) { Point connectorCenter = convertNormalizedPointToViewCoordinates(getConnector().getMiddleSymbolLocation(), scale); setAbsoluteTextX(((double) point.x - connectorCenter.x - getViewX(scale)) / scale); setAbsoluteTextY(((double) point.y - connectorCenter.y - getViewY(scale)) / scale); } @Override public boolean hasFloatingLabel() { return hasText(); } @Override public String getInspectorName() { return "ConnectorGraphicalRepresentation.inspector"; } @Override public void update(Observable observable, Object notification) { // System.out.println("Connector received "+notification+" from "+observable); super.update(observable, notification); if (observable instanceof ForegroundStyle) { notifyAttributeChange(Parameters.foreground); } if (notification instanceof ObjectWillMove || notification instanceof ObjectWillResize) { connector.connectorWillBeModified(); // Propagate notification to views setChanged(); notifyObservers(notification); } if (notification instanceof ObjectHasMoved || notification instanceof ObjectHasResized) { connector.connectorHasBeenModified(); // Propagate notification to views setChanged(); notifyObservers(notification); } if (notification instanceof ObjectMove || notification instanceof ObjectResized || notification instanceof ShapeChanged) { // if (observable == startObject || observable == endObject) { // !!! or any of ancestors refreshConnector(); // } } } protected boolean isConnectorConsistent() { // if (true) return true; return getStartObject() != null && getEndObject() != null && !getStartObject().isDeleted() && !getEndObject().isDeleted() && GraphicalRepresentation.areElementsConnectedInGraphicalHierarchy(getStartObject(), getEndObject()); } public void refreshConnector() { refreshConnector(false); } protected void refreshConnector(boolean forceRefresh) { if (!isConnectorConsistent()) { // Dont' go further for connector that are inconsistent (this may happen // during big model restructurations (for example during a multiple delete) return; } try { if (forceRefresh || connector.needsRefresh()) { connector.refreshConnector(forceRefresh); checkViewBounds(); setChanged(); notifyObservers(new ConnectorModified()); } } catch (Exception e) { logger.warning("Unexpected exception: " + e); e.printStackTrace(); } } @Override public void setRegistered(boolean aFlag) { if (aFlag != isRegistered()) { super.setRegistered(aFlag); if (aFlag && !isDeleted()) { refreshConnector(); } } } // Override for a custom view management public ConnectorView<O> makeConnectorView(DrawingController<?> controller) { return new ConnectorView<O>(this, controller); } public EndSymbolType getEndSymbol() { return endSymbol; } public void setEndSymbol(EndSymbolType endSymbol) { FGENotification notification = requireChange(Parameters.endSymbol, endSymbol); if (notification != null) { this.endSymbol = endSymbol; hasChanged(notification); } } public double getEndSymbolSize() { return endSymbolSize; } public void setEndSymbolSize(double endSymbolSize) { FGENotification notification = requireChange(Parameters.endSymbolSize, endSymbolSize); if (notification != null) { this.endSymbolSize = endSymbolSize; hasChanged(notification); } } public MiddleSymbolType getMiddleSymbol() { return middleSymbol; } public void setMiddleSymbol(MiddleSymbolType middleSymbol) { FGENotification notification = requireChange(Parameters.middleSymbol, middleSymbol); if (notification != null) { this.middleSymbol = middleSymbol; hasChanged(notification); } } public double getMiddleSymbolSize() { return middleSymbolSize; } public void setMiddleSymbolSize(double middleSymbolSize) { FGENotification notification = requireChange(Parameters.middleSymbolSize, middleSymbolSize); if (notification != null) { this.middleSymbolSize = middleSymbolSize; hasChanged(notification); } } public StartSymbolType getStartSymbol() { return startSymbol; } public void setStartSymbol(StartSymbolType startSymbol) { FGENotification notification = requireChange(Parameters.startSymbol, startSymbol); if (notification != null) { this.startSymbol = startSymbol; hasChanged(notification); } } public double getStartSymbolSize() { return startSymbolSize; } public void setStartSymbolSize(double startSymbolSize) { FGENotification notification = requireChange(Parameters.startSymbolSize, startSymbolSize); if (notification != null) { this.startSymbolSize = startSymbolSize; hasChanged(notification); } } public double getRelativeMiddleSymbolLocation() { return relativeMiddleSymbolLocation; } public void setRelativeMiddleSymbolLocation(double relativeMiddleSymbolLocation) { FGENotification notification = requireChange(Parameters.relativeMiddleSymbolLocation, relativeMiddleSymbolLocation); if (notification != null) { this.relativeMiddleSymbolLocation = relativeMiddleSymbolLocation; hasChanged(notification); } } public boolean getApplyForegroundToSymbols() { return applyForegroundToSymbols; } public void setApplyForegroundToSymbols(boolean applyForegroundToSymbols) { FGENotification notification = requireChange(Parameters.applyForegroundToSymbols, applyForegroundToSymbols); if (notification != null) { this.applyForegroundToSymbols = applyForegroundToSymbols; hasChanged(notification); } } public boolean getDebugCoveringArea() { return debugCoveringArea; } public void setDebugCoveringArea(boolean debugCoveringArea) { FGENotification notification = requireChange(Parameters.debugCoveringArea, debugCoveringArea); if (notification != null) { this.debugCoveringArea = debugCoveringArea; hasChanged(notification); } } // Override when required @Override public void notifyObjectHierarchyHasBeenUpdated() { super.notifyObjectHierarchyHasBeenUpdated(); refreshConnector(); } public FGEConnectorGraphics getGraphics() { return graphics; } public List<? extends ControlArea> getControlAreas() { return getConnector().getControlAreas(); } /** * Override parent's behaviour by enabling start and end object observing */ @Override public void setValidated(boolean validated) { super.setValidated(validated); if (!enabledStartObjectObserving) { enableStartObjectObserving(startObject); } if (!enabledEndObjectObserving) { enableEndObjectObserving(endObject); } } @Override public ConnectorGraphicalRepresentation<O> clone() { // logger.info("La GR "+this+" se fait cloner la"); try { return (ConnectorGraphicalRepresentation<O>) super.clone(); } catch (CloneNotSupportedException e) { // cannot happen since we are clonable e.printStackTrace(); return null; } } }