GraphicalCompositeFigure.java

/*
 * @(#)GraphicalCompositeFigure.java
 *
 * Project:		JHotdraw - a GUI framework for technical drawings
 *				http://www.jhotdraw.org
 *				http://jhotdraw.sourceforge.net
 * Copyright:	© by the original author(s) and all contributors
 * License:		Lesser GNU Public License (LGPL)
 *				http://www.opensource.org/licenses/lgpl-license.html
 */
 
package CH.ifa.draw.contrib;

import CH.ifa.draw.framework.*;
import CH.ifa.draw.standard.*;
import CH.ifa.draw.util.*;
import CH.ifa.draw.figures.*;
import java.awt.*;
import java.util.*;
import java.io.*;

/**
 * The GraphicalCompositeFigure fills in the gap between a CompositeFigure
 * and other figures which mainly have a presentation purpose. The
 * GraphicalCompositeFigure can be configured with any Figure which
 * takes over the task for rendering the graphical presentation for
 * a CompositeFigure. Therefore, the GraphicalCompositeFigure manages
 * contained figures like the CompositeFigure does, but delegates
 * its graphical presentation to another (graphical) figure which
 * purpose it is to draw the container for all contained figures.
 *
 * @author	Wolfram Kaiser
 * @version <$CURRENT_VERSION$>
 */
public class GraphicalCompositeFigure extends CompositeFigure implements Layoutable  {

	/**
	 * Figure which performs all presentation tasks for this
	 * CompositeFigure as CompositeFigures usually don't have
	 * an own presentation but present only the sum of all its
	 * children.
	 */
	private Figure	myPresentationFigure;
	
	/**
	 * A Layouter determines how the CompositeFigure should
	 * be laid out graphically.
	 */
	private Layouter myLayouter;

	private static final long serialVersionUID = 1265742491024232713L;
	
	/**
	 * Default constructor which uses a RectangleFigure as presentation
	 * figure. This constructor is needed by the Storable mechanism.
	 */
	public GraphicalCompositeFigure() {
		this(new RectangleFigure());
	}
	
	/**
	 * Constructor which creates a GraphicalCompositeFigure with
	 * a given graphical figure for presenting it.
	 *
	 * @param	newPresentationFigure	figure which renders the container
	 */
	public GraphicalCompositeFigure(Figure newPresentationFigure) {
		super();
		setPresentationFigure(newPresentationFigure);
		initialize();
	}

	/**
	 * This method performs additional initialization operations,
	 * in this case setting the Layouter.
	 * It is called from the constructors and the clone() method.
	 * A StandardLayouter is set.
	 */
	protected void initialize() {
		setLayouter(new StandardLayouter(this));
	}

	/**
	 * Clones a figure and initializes it
	 *
	 * @see Figure#clone
	 */
	public Object clone() {
		Object cloneObject = super.clone();
		((GraphicalCompositeFigure)cloneObject).initialize();
		return cloneObject;
	}

	/**
	 * Return the display area. This method is delegated to the encapsulated presentation figure.
	 */
	public Rectangle displayBox() {
		return getPresentationFigure().displayBox();
	}

	/**
	 * Standard presentation method which is delegated to the encapsulated presentation figure.
	 */
	public void basicDisplayBox(Point origin, Point corner) {
		Rectangle r = getLayouter().layout(origin, corner);
		getPresentationFigure().basicDisplayBox(r.getLocation(), new Point(r.width, r.height));
	}

	/**
	 * Standard presentation method which is delegated to the encapsulated presentation figure.
	 * The presentation figure is moved as well as all contained figures.
	 */
	protected void basicMoveBy(int dx, int dy) {
		super.basicMoveBy(dx, dy);
		getPresentationFigure().moveBy(dx, dy);
	}

	/**
	 * Expicit update: an updated involves a layout for all contained figures.
	 */
	public void update() {
		willChange();
		layout();
		change();
		changed();
	}

	/**
	 * Draw the figure. This method is delegated to the encapsulated presentation figure.
	 */
	public void draw(Graphics g) {
		getPresentationFigure().draw(g);
		super.draw(g);
	}

	/**
	 * Return default handles from the presentation figure.
	 */
	public Vector handles() {
		return getPresentationFigure().handles();
	}

	/**
	 * Delegate capabilities for storing and retrieving attributes to a
	 * CompositeFigure if the encapsulated presentation figure. If no
	 * presentation figure is found then the superclass' getAttribute()
	 * will be invoked (which currently returns always "null").
	 *
	 * @param	name	name of the attribute whose value should be returned
	 * @return	value of the attribute with the given name
	 */
	public Object getAttribute(String name) {
		if (getPresentationFigure() != null) {	
			return getPresentationFigure().getAttribute(name);
		}
		else {
			return super.getAttribute(name);
		}
	}

	/**
	 * Delegate capabilities for storing and retrieving attributes to a
	 * CompositeFigure if the encapsulated presentation figure. If no
	 * presentation figure is found then the superclass' setAttribute()
	 * will be invoked (which currently does not set an attribute).
	 *
	 * @param	name	name of the attribute
	 * @param	value	value associated with this attribute
	 */
	public void setAttribute(String name, Object value) {
		if (getPresentationFigure() != null) {
			getPresentationFigure().setAttribute(name, value);
		}
		else {
			super.setAttribute(name, value);
		}
	}

	/**
	 * Set a figure which renders this CompositeFigure. The presentation
	 * tasks for the CompositeFigure are delegated to this presentation
	 * figure.
	 *
	 * @param	newPresentationFigure	figure takes over the presentation tasks
	 */
	public void setPresentationFigure(Figure newPresentationFigure) {
		myPresentationFigure = newPresentationFigure;
	}

	/**
	 * Get a figure which renders this CompositeFigure. The presentation
	 * tasks for the CompositeFigure are delegated to this presentation
	 * figure.
	 *
	 * @return	figure takes over the presentation tasks
	 */
	public Figure getPresentationFigure() {
		return myPresentationFigure;
	}

	/**
	 * A layout algorithm is used to define how the child components
	 * should be laid out in relation to each other. The task for
	 * layouting the child components for presentation is delegated
	 * to a Layouter which can be plugged in at runtime.
	 */
	public void layout() {
		if (getLayouter() != null) {
			Rectangle r = getLayouter().calculateLayout(displayBox().getLocation(), displayBox().getLocation());
			displayBox(r.getLocation(), new Point(r.x + r.width, r.y + r.height));
		}
	}

	/**
	 * Set a Layouter object which encapsulated a layout
	 * algorithm for this figure. Typically, a Layouter
	 * accesses the child components of this figure and arranges
	 * their graphical presentation. It is a good idea to set
	 * the Layouter in the protected initialize() method
	 * so it can be recreated if a GraphicalCompositeFigure is
	 * read and restored from a StorableInput stream.
	 *
	 * @param	newLayouter	encapsulation of a layout algorithm.
	 */
	public void setLayouter(Layouter newLayouter) {
		myLayouter = newLayouter;
	}

	/**
	 * Get a Layouter object which encapsulated a layout
	 * algorithm for this figure. Typically, a Layouter
	 * accesses the child components of this figure and arranges
	 * their graphical presentation.
	 *
	 * @return	layout strategy used by this figure
	 */
	public Layouter getLayouter() {
		return myLayouter;
	}
	
	/**
	 * Notify the registered change listener if an exlicit change
	 * to the component (or one of its child components has occurred).
	 */
	protected void change() {
		if (listener() != null) {
			listener().figureRequestUpdate(new FigureChangeEvent(this));
		}
	}

	/**
	 * Propagates the removeFromDrawing request up to the container.
	 */
	public void figureRequestRemove(FigureChangeEvent e) {
		if (listener() != null) {
			if (includes(e.getFigure())) {
				listener().figureRequestRemove(new FigureChangeEvent(e.getFigure(), e.getInvalidatedRectangle()));
			}
			else {
				super.figureRequestRemove(e);
			}
		}
	}

	/**
	 * Reads the contained figures from StorableInput. The
	 * figure responsible for graphical presentation is read
	 * together with all child components. The Layouter
	 * is not stored and therefore not read.
	 */
	public void read(StorableInput dr) throws IOException {
		super.read(dr);
		setPresentationFigure((Figure)dr.readStorable());
	}

	/**
	 * Writes the contained figures to the StorableOutput. The
	 * figure responsible for graphical presentation is written
	 * together with all child components. The Layouter
	 * is not written.
	 */
	public void write(StorableOutput dw) {
		super.write(dw);
		dw.writeStorable(getPresentationFigure());
	}
}