StandardDrawing.java

/*
 * @(#)StandardDrawing.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.standard;

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

/**
 * The standard implementation of the Drawing interface.
 *
 * @see Drawing
 *
 * @version <$CURRENT_VERSION$>
 */

public class StandardDrawing extends CompositeFigure implements Drawing {


	/**
	 * the registered listeners
	 */
	private transient Vector              fListeners;

	/**
	 * boolean that serves as a condition variable
	 * to lock the access to the drawing.
	 * The lock is recursive and we keep track of the current
	 * lock holder.
	 */
	private transient Thread    fDrawingLockHolder = null;
	private String				myTitle;

	/*
	 * Serialization support
	 */
	private static final long serialVersionUID = -2602151437447962046L;
	private int drawingSerializedDataVersion = 1;

	/**
	 * Constructs the Drawing.
	 */
	public StandardDrawing() {
		super();
		fListeners = new Vector(2);
		init(new Rectangle(-500, -500, 2000, 2000));
	}

	/**
	 * Adds a listener for this drawing.
	 */
	public void addDrawingChangeListener(DrawingChangeListener listener) {
		if (fListeners == null) {
			fListeners = new Vector(2);
		}
		fListeners.addElement(listener);
	}

	/**
	 * Removes a listener from this drawing.
	 */
	public void removeDrawingChangeListener(DrawingChangeListener listener) {
		fListeners.removeElement(listener);
	}

	/**
	 * Gets an enumeration with all listener for this drawing.
	 */
	public Enumeration drawingChangeListeners() {
		return fListeners.elements();
	}

	/**
	 * Removes the figure from the drawing and releases it.
	 */

	public synchronized Figure remove(Figure figure) {
		// ensure that we remove the top level figure in a drawing
		if (figure.listener() != null) {
			figure.listener().figureRequestRemove(new FigureChangeEvent(figure, null));
			return figure;
		}
		return null;
	}

	/**
	 * Handles a removeFromDrawing request that
	 * is passed up the figure container hierarchy.
	 * @see FigureChangeListener
	 */
	public void figureRequestRemove(FigureChangeEvent e) {
		Figure figure = e.getFigure();
		if (fFigures.contains(figure)) {
			fFigures.removeElement(figure);
			figure.removeFromContainer(this);   // will invalidate figure
			figure.release();
		}
		else {
			System.err.println("Attempt to remove non-existing figure");
		}
	}

	/**
	 * Invalidates a rectangle and merges it with the
	 * existing damaged area.
	 * @see FigureChangeListener
	 */
	public void figureInvalidated(FigureChangeEvent e) {
		if (fListeners != null) {
			for (int i = 0; i < fListeners.size(); i++) {
				DrawingChangeListener l = (DrawingChangeListener)fListeners.elementAt(i);
				l.drawingInvalidated(new DrawingChangeEvent(this, e.getInvalidatedRectangle()));
			}
		}
	}

	/**
	 * Forces an update
	 */
	public void figureRequestUpdate(FigureChangeEvent e) {
		if (fListeners != null) {
			for (int i = 0; i < fListeners.size(); i++) {
				DrawingChangeListener l = (DrawingChangeListener)fListeners.elementAt(i);
				l.drawingRequestUpdate(new DrawingChangeEvent(this, null));
			}
		}
	}

	/**
	 * Return's the figure's handles. This is only used when a drawing
	 * is nested inside another drawing.
	 */
	public Vector handles() {
		Vector handles = new Vector();
		handles.addElement(new NullHandle(this, RelativeLocator.northWest()));
		handles.addElement(new NullHandle(this, RelativeLocator.northEast()));
		handles.addElement(new NullHandle(this, RelativeLocator.southWest()));
		handles.addElement(new NullHandle(this, RelativeLocator.southEast()));
		return handles;
	}

	/**
	 * Gets the display box. This is the union of all figures.
	 */
	public Rectangle displayBox() {
		if (fFigures.size() > 0) {
			FigureEnumeration k = figures();

			Rectangle r = k.nextFigure().displayBox();

			while (k.hasMoreElements()) {
				r.add(k.nextFigure().displayBox());
			}
			return r;
		}
		return new Rectangle(0, 0, 0, 0);
	}

	public void basicDisplayBox(Point p1, Point p2) {
	}

	/**
	 * Acquires the drawing lock.
	 */
	public synchronized void lock() {
		// recursive lock
		Thread current = Thread.currentThread();
		if (fDrawingLockHolder == current) {
			return;
		}
		while (fDrawingLockHolder != null) {
			try {
				wait();
			}
			catch (InterruptedException ex) { }
		}
		fDrawingLockHolder = current;
	}

	/**
	 * Releases the drawing lock.
	 */
	public synchronized void unlock() {
		if (fDrawingLockHolder != null) {
			fDrawingLockHolder = null;
			notifyAll();
		}
	}

	private void readObject(ObjectInputStream s)
		throws ClassNotFoundException, IOException {

		s.defaultReadObject();

		fListeners = new Vector(2);
	}

	public String getTitle() {
		return myTitle;
	}

	public void setTitle(String newTitle) {
		myTitle = newTitle;
	}
}