/*
* (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.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Vector;
import java.util.logging.Logger;
import org.openflexo.fge.controller.DrawingController;
import org.openflexo.fge.controller.MouseClickControl;
import org.openflexo.fge.controller.MouseClickControlAction.MouseClickControlActionType;
import org.openflexo.fge.controller.MouseControl.MouseButton;
import org.openflexo.fge.controller.MouseDragControl;
import org.openflexo.fge.controller.MouseDragControlAction.MouseDragControlActionType;
import org.openflexo.fge.geom.FGEDimension;
import org.openflexo.fge.geom.FGEGeometricObject.Filling;
import org.openflexo.fge.geom.FGEPoint;
import org.openflexo.fge.geom.FGERectangle;
import org.openflexo.fge.graphics.BackgroundStyle;
import org.openflexo.fge.graphics.DrawingDecorationPainter;
import org.openflexo.fge.graphics.FGEDrawingDecorationGraphics;
import org.openflexo.fge.graphics.FGEDrawingGraphics;
import org.openflexo.fge.graphics.ForegroundStyle;
import org.openflexo.fge.notifications.FGENotification;
import org.openflexo.fge.notifications.ObjectHasResized;
import org.openflexo.fge.notifications.ObjectResized;
import org.openflexo.fge.notifications.ObjectWillResize;
public class DrawingGraphicalRepresentation<M> extends GraphicalRepresentation<M> {
private static final Logger logger = Logger.getLogger(DrawingGraphicalRepresentation.class.getPackage().getName());
// *******************************************************************************
// * Parameters *
// *******************************************************************************
public static enum Parameters implements GRParameter {
backgroundColor, width, height, rectangleSelectingSelectionColor, focusColor, selectionColor, drawWorkingArea, isResizable;
}
private Color backgroundColor = Color.WHITE;
private double width;
private double height;
private Color rectangleSelectingSelectionColor = Color.BLUE;
private Color focusColor = Color.RED;
private Color selectionColor = Color.BLUE;
private boolean drawWorkingArea = false;
private boolean isResizable = false;
protected DrawingDecorationPainter decorationPainter;
// *******************************************************************************
// * Inner classes *
// *******************************************************************************
// *******************************************************************************
// * Fields *
// *******************************************************************************
protected FGEDrawingGraphics graphics;
private FGEDrawingDecorationGraphics decorationGraphics;
private BackgroundStyle bgStyle;
// *******************************************************************************
// * Methods *
// *******************************************************************************
// *******************************************************************************
// * Constructor *
// *******************************************************************************
// Never use this constructor (used during deserialization only)
public DrawingGraphicalRepresentation() {
this(null, true);
}
public DrawingGraphicalRepresentation(Drawing<M> aDrawing) {
this(aDrawing, true);
}
public DrawingGraphicalRepresentation(Drawing<M> aDrawing, boolean initBasicControls) {
super(aDrawing != null ? aDrawing.getModel() : null, aDrawing);
graphics = new FGEDrawingGraphics(this);
if (initBasicControls) {
addToMouseClickControls(MouseClickControl.makeMouseClickControl("Drawing selection", MouseButton.LEFT, 1,
MouseClickControlActionType.SELECTION));
addToMouseDragControls(MouseDragControl.makeMouseDragControl("Rectangle selection", MouseButton.LEFT,
MouseDragControlActionType.RECTANGLE_SELECTING));
}
width = 1000;
height = 1000;
bgStyle = BackgroundStyle.makeColoredBackground(getBackgroundColor());
}
@Override
public void delete() {
super.delete();
if (graphics != null) {
graphics.delete();
}
graphics = null;
decorationGraphics = null;
decorationPainter = null;
}
@Override
public Vector<GRParameter> getAllParameters() {
Vector<GRParameter> returned = super.getAllParameters();
Parameters[] allParams = Parameters.values();
for (int i = 0; i < allParams.length; i++) {
returned.add(allParams[i]);
}
return returned;
}
/**
* Override parent behaviour by always returning true<br>
* IMPORTANT: a drawing graphical representation MUST be always validated
*/
@Override
public final boolean isValidated() {
return true;
}
// ***************************************************************************
// * Cloning *
// ***************************************************************************
@Override
public final void setsWith(GraphicalRepresentation<?> gr) {
super.setsWith(gr);
if (gr instanceof DrawingGraphicalRepresentation) {
for (Parameters p : Parameters.values()) {
_setParameterValueWith(p, gr);
}
}
}
@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 (!excepted) {
_setParameterValueWith(p, gr);
}
}
}
}
// *******************************************************************************
// * Accessors *
// *******************************************************************************
public FGERectangle getWorkingArea() {
return new FGERectangle(0, 0, getWidth(), getHeight(), Filling.FILLED);
}
@Override
public int getLayer() {
return 0;
}
public Color getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(Color backgroundColor) {
// logger.info("For "+this+" Set bg color to "+backgroundColor);
FGENotification notification = requireChange(Parameters.backgroundColor, backgroundColor);
if (notification != null) {
this.backgroundColor = backgroundColor;
bgStyle = BackgroundStyle.makeColoredBackground(backgroundColor);
hasChanged(notification);
}
}
public double getHeight() {
return height;
}
public void setHeight(double aValue) {
FGENotification notification = requireChange(Parameters.height, aValue);
if (notification != null) {
height = aValue;
hasChanged(notification);
}
}
public double getWidth() {
return width;
}
public void setWidth(double aValue) {
FGENotification notification = requireChange(Parameters.width, aValue);
if (notification != null) {
width = aValue;
hasChanged(notification);
}
}
public Color getFocusColor() {
return focusColor;
}
public void setFocusColor(Color focusColor) {
FGENotification notification = requireChange(Parameters.focusColor, focusColor);
if (notification != null) {
this.focusColor = focusColor;
hasChanged(notification);
}
}
public Color getSelectionColor() {
return selectionColor;
}
public void setSelectionColor(Color selectionColor) {
FGENotification notification = requireChange(Parameters.selectionColor, selectionColor);
if (notification != null) {
this.selectionColor = selectionColor;
hasChanged(notification);
}
}
public Color getRectangleSelectingSelectionColor() {
return rectangleSelectingSelectionColor;
}
public void setRectangleSelectingSelectionColor(Color selectionColor) {
FGENotification notification = requireChange(Parameters.rectangleSelectingSelectionColor, selectionColor);
if (notification != null) {
this.rectangleSelectingSelectionColor = selectionColor;
hasChanged(notification);
}
}
@Override
public int getViewX(double scale) {
return 0;
}
@Override
public int getViewY(double scale) {
return 0;
}
@Override
public int getViewWidth(double scale) {
return (int) (getWidth() * scale);
}
@Override
public int getViewHeight(double scale) {
return (int) (getHeight() * scale);
}
@Override
public FGERectangle getNormalizedBounds() {
return new FGERectangle(0, 0, getWidth(), getHeight(), Filling.FILLED);
}
@Override
public boolean isContainedInSelection(Rectangle drawingViewSelection, double scale) {
return false;
}
@Override
public AffineTransform convertNormalizedPointToViewCoordinatesAT(double scale) {
return AffineTransform.getScaleInstance(scale, scale);
/*AffineTransform returned = AffineTransform.getScaleInstance(getWidth(), getHeight());
if (scale != 1) {
returned.preConcatenate(AffineTransform.getScaleInstance(scale,scale));
}
return returned;*/
}
@Override
public AffineTransform convertViewCoordinatesToNormalizedPointAT(double scale) {
return AffineTransform.getScaleInstance(1 / scale, 1 / scale);
/*AffineTransform returned = new AffineTransform();
if (scale != 1) returned = AffineTransform.getScaleInstance(1/scale, 1/scale);
returned.preConcatenate(AffineTransform.getScaleInstance(1/getWidth(),1/getHeight()));
return returned;*/
}
// *******************************************************************************
// * Methods *
// *******************************************************************************
@Override
public void paint(Graphics g, DrawingController controller) {
Graphics2D g2 = (Graphics2D) g;
graphics.createGraphics(g2, controller);
// If there is a decoration painter init its graphics
if (decorationPainter != null) {
decorationGraphics.createGraphics(g2, controller);
}
// If there is a decoration painter and decoration should be painted BEFORE shape, fo it now
if (decorationPainter != null && decorationPainter.paintBeforeDrawing()) {
decorationPainter.paintDecoration(decorationGraphics);
}
super.paint(g, controller);
if (!(bgStyle instanceof BackgroundStyle.Color) || !((BackgroundStyle.Color) bgStyle).getColor().equals(getBackgroundColor())) {
bgStyle = BackgroundStyle.makeColoredBackground(getBackgroundColor());
}
ForegroundStyle fgStyle = ForegroundStyle.makeStyle(Color.DARK_GRAY);
graphics.setDefaultForeground(fgStyle);
graphics.setDefaultBackground(bgStyle);
if (drawWorkingArea) {
getWorkingArea().paint(graphics);
}
// If there is a decoration painter and decoration should be painted BEFORE shape, fo it now
if (decorationPainter != null && !decorationPainter.paintBeforeDrawing()) {
decorationPainter.paintDecoration(decorationGraphics);
}
graphics.releaseGraphics();
}
public DrawingDecorationPainter getDecorationPainter() {
return decorationPainter;
}
public void setDecorationPainter(DrawingDecorationPainter aPainter) {
decorationGraphics = new FGEDrawingDecorationGraphics(this);
decorationPainter = aPainter;
}
@Override
public final boolean hasText() {
return false;
}
@Override
public final String getText() {
return null;
}
@Override
public boolean hasFloatingLabel() {
return false;
}
@Override
public String getInspectorName() {
return "DrawingGraphicalRepresentation.inspector";
}
@Override
public GraphicalRepresentation<?> getContainerGraphicalRepresentation() {
return null;
}
@Override
public final boolean shouldBeDisplayed() {
return true;
}
@Override
public boolean getIsVisible() {
return true;
}
public Vector<GraphicalRepresentation> allGraphicalRepresentations() {
Vector<GraphicalRepresentation> returned = new Vector<GraphicalRepresentation>();
_appendGraphicalRepresentations(returned, this);
return returned;
}
private static void _appendGraphicalRepresentations(Vector<GraphicalRepresentation> v, GraphicalRepresentation<?> gr) {
v.add(gr);
List<? extends Object> containedObjects = gr.getContainedObjects();
if (containedObjects == null) {
return;
}
for (Object drawable : containedObjects) {
GraphicalRepresentation<?> next = gr.getGraphicalRepresentation(drawable);
_appendGraphicalRepresentations(v, next);
}
}
/*@Override
public void finalizeDeserialization()
{
logger.info("ICI ???");
super.finalizeDeserialization();
for (GraphicalRepresentation gr : allGraphicalRepresentations()) {
logger.info("gr="+gr);
if (gr instanceof ConnectorGraphicalRepresentation) {
((ConnectorGraphicalRepresentation)gr).observeRelevantObjects();
}
}
}*/
public boolean getDrawWorkingArea() {
return drawWorkingArea;
}
public void setDrawWorkingArea(boolean drawWorkingArea) {
// logger.info("setDrawWorkingArea with "+drawWorkingArea);
FGENotification notification = requireChange(Parameters.drawWorkingArea, drawWorkingArea);
if (notification != null) {
this.drawWorkingArea = drawWorkingArea;
hasChanged(notification);
}
}
public boolean isResizable() {
return isResizable;
}
public void setIsResizable(boolean isResizable) {
FGENotification notification = requireChange(Parameters.isResizable, isResizable);
if (notification != null) {
this.isResizable = isResizable;
hasChanged(notification);
}
}
public void startConnectorObserving() {
for (GraphicalRepresentation gr : allGraphicalRepresentations()) {
if (gr instanceof ConnectorGraphicalRepresentation) {
((ConnectorGraphicalRepresentation) gr).observeRelevantObjects();
}
}
}
public FGEDimension getSize() {
return new FGEDimension(getWidth(), getHeight());
}
/**
* Notify that the object just resized
*/
public void notifyObjectResized(FGEDimension oldSize) {
setChanged();
notifyObservers(new ObjectResized(oldSize, getSize()));
}
/**
* Notify that the object will be resized
*/
public void notifyObjectWillResize() {
setChanged();
notifyObservers(new ObjectWillResize());
}
/**
* Notify that the object resizing has finished (take care that this just notify END of resize, this should NOT be used to notify a
* resizing: use notifyObjectResize() instead)
*/
public void notifyObjectHasResized() {
setChanged();
notifyObservers(new ObjectHasResized());
}
public void notifyDrawingNeedsToBeRedrawn() {
setChanged();
notifyObservers(new DrawingNeedsToBeRedrawn());
}
public FGEDrawingGraphics getGraphics() {
return graphics;
}
public ShapeGraphicalRepresentation<?> getTopLevelShapeGraphicalRepresentation(FGEPoint p) {
return getTopLevelShapeGraphicalRepresentation(this, p);
}
private ShapeGraphicalRepresentation<?> getTopLevelShapeGraphicalRepresentation(GraphicalRepresentation<?> container, FGEPoint p) {
List<ShapeGraphicalRepresentation<?>> enclosingShapes = new ArrayList<ShapeGraphicalRepresentation<?>>();
for (GraphicalRepresentation<?> gr : container.getContainedGraphicalRepresentations()) {
if (gr instanceof ShapeGraphicalRepresentation) {
ShapeGraphicalRepresentation<?> child = (ShapeGraphicalRepresentation<?>) gr;
if (child.getShape().getShape().containsPoint(convertNormalizedPoint(this, p, child))) {
enclosingShapes.add(child);
} else {
// Look if we are not contained in a child shape outside current shape
GraphicalRepresentation<?> insideFocusedShape = getTopLevelShapeGraphicalRepresentation(child, p);
if (insideFocusedShape != null && insideFocusedShape instanceof ShapeGraphicalRepresentation) {
enclosingShapes.add((ShapeGraphicalRepresentation<?>) insideFocusedShape);
}
}
}
}
if (enclosingShapes.size() > 0) {
Collections.sort(enclosingShapes, new Comparator<ShapeGraphicalRepresentation<?>>() {
@Override
public int compare(ShapeGraphicalRepresentation<?> o1, ShapeGraphicalRepresentation<?> o2) {
if (o2.getLayer() == o1.getLayer() && o1.getParentGraphicalRepresentation() != null
&& o1.getParentGraphicalRepresentation() == o2.getParentGraphicalRepresentation()) {
return o1.getParentGraphicalRepresentation().getOrder(o1, o2);
}
return o2.getLayer() - o1.getLayer();
}
});
ShapeGraphicalRepresentation<?> focusedShape = enclosingShapes.get(0);
ShapeGraphicalRepresentation<?> insideFocusedShape = getTopLevelShapeGraphicalRepresentation(focusedShape, p);
if (insideFocusedShape != null) {
return insideFocusedShape;
} else {
return focusedShape;
}
}
return null;
}
// *******************************************************************************
// * Layout *
// *******************************************************************************
public void performRandomLayout() {
performRandomLayout(getWidth(), getHeight());
}
public void performAutoLayout() {
performAutoLayout(getWidth(), getHeight());
}
}