/*******************************************************************************
 * Copyright (c) 2000, 2007 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.draw2d;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.draw2d.geometry.Translatable;

/**
 * The base implementation for graphical figures.
 */
public class Figure
	implements IFigure
{

private static final Rectangle PRIVATE_RECT = new Rectangle();
private static final Point PRIVATE_POINT = new Point();
private static final int
	FLAG_VALID = new Integer(1).intValue(),
	FLAG_OPAQUE = new Integer(1 << 1).intValue(),
	FLAG_VISIBLE = new Integer(1 << 2).intValue(),
	FLAG_FOCUSABLE = new Integer(1 << 3).intValue(),
	FLAG_ENABLED = new Integer(1 << 4).intValue(),
	FLAG_FOCUS_TRAVERSABLE = new Integer(1 << 5).intValue();

static final int
	FLAG_REALIZED = 1 << 31;

/**
 * The largest flag defined in this class.  If subclasses define flags, they should
 * declare them as larger than this value and redefine MAX_FLAG to be their largest flag
 * value.
 * <P>
 * This constant is evaluated at runtime and will not be inlined by the compiler.
 */
protected static int MAX_FLAG = FLAG_FOCUS_TRAVERSABLE;

/**
 * The rectangular area that this Figure occupies.
 */
protected Rectangle bounds = new Rectangle(0, 0, 0, 0);

private LayoutManager layoutManager;

/**
 * The flags for this Figure.
 */
protected int flags = FLAG_VISIBLE | FLAG_ENABLED;

private IFigure parent;
private Cursor cursor;

private PropertyChangeSupport propertyListeners;
private EventListenerList eventListeners = new EventListenerList();

private List children = Collections.EMPTY_LIST;

/**
 * This Figure's preferred size.
 */
protected Dimension prefSize;

/**
 * This Figure's minimum size.
 */
protected Dimension minSize;

/**
 * This Figure's maximum size.
 */
protected Dimension maxSize;

/** 
 * @deprecated access using {@link #getLocalFont()}
 */
protected Font font;

/**
 * @deprecated access using {@link #getLocalBackgroundColor()}.
 */
protected Color bgColor;

/**
 * @deprecated access using {@link #getLocalForegroundColor()}.
 */
protected Color fgColor;

/**
 * @deprecated access using {@link #getBorder()}
 */
protected Border border;

/**
 * @deprecated access using {@link #getToolTip()}
 */
protected IFigure toolTip;

private AncestorHelper ancestorHelper;

/**
 * Calls {@link #add(IFigure, Object, int)} with -1 as the index.
 * @see IFigure#add(IFigure, Object)
 */
public final void add(IFigure figure, Object constraint) {
	add(figure, constraint, -1);
}

/**
 * @see IFigure#add(IFigure, Object, int)
 */
public void add(IFigure figure, Object constraint, int index) {
	if (children == Collections.EMPTY_LIST)
		children = new ArrayList(2);
	if (index < -1 || index > children.size())
		throw new IndexOutOfBoundsException("Index does not exist"); //$NON-NLS-1$

	//Check for Cycle in hierarchy
	for (IFigure f = this; f != null; f = f.getParent())
		if (figure == f)
			throw new IllegalArgumentException(
						"Figure being added introduces cycle"); //$NON-NLS-1$

	//Detach the child from previous parent
	if (figure.getParent() != null)
		figure.getParent().remove(figure);

	if (index == -1)
		children.add(figure);
	else
		children.add(index, figure);
	figure.setParent(this);

	if (layoutManager != null)
		layoutManager.setConstraint(figure, constraint);

	revalidate();

	if (getFlag(FLAG_REALIZED))
		figure.addNotify();
	figure.repaint();
}

/**
 * Calls {@link #add(IFigure, Object, int)} with <code>null</code> as the constraint and 
 * -1 as the index.
 * @see IFigure#add(IFigure)
 */
public final void add(IFigure figure) {
	add(figure, null, -1);
}

/**
 * Calls {@link #add(IFigure, Object, int)} with <code>null</code> as the constraint.
 * @see IFigure#add(IFigure, int)
 */
public final void add(IFigure figure, int index) {
	add(figure, null, index);
}
/**
 * @see IFigure#addAncestorListener(AncestorListener)
 */
public void addAncestorListener(AncestorListener ancestorListener) {
	if (ancestorHelper == null)
		ancestorHelper = new AncestorHelper(this);
	ancestorHelper.addAncestorListener(ancestorListener);
}

/**
 * @see IFigure#addCoordinateListener(CoordinateListener)
 */
public void addCoordinateListener(CoordinateListener listener) {
	eventListeners.addListener(CoordinateListener.class, listener);
}

/**
 * @see IFigure#addFigureListener(FigureListener)
 */
public void addFigureListener(FigureListener listener) {
	eventListeners.addListener(FigureListener.class, listener);
}

/**
 * @see IFigure#addFocusListener(FocusListener)
 */
public void addFocusListener(FocusListener listener) {
	eventListeners.addListener(FocusListener.class, listener);
}

/**
 * @see IFigure#addKeyListener(KeyListener)
 */
public void addKeyListener(KeyListener listener) {
	eventListeners.addListener(KeyListener.class, listener);
}	

/**
 * Appends the given layout listener to the list of layout listeners.
 * @since 3.1
 * @param listener the listener being added
 */
public void addLayoutListener(LayoutListener listener) {
	if (layoutManager instanceof LayoutNotifier) {
		LayoutNotifier notifier = (LayoutNotifier)layoutManager;
		notifier.listeners.add(listener);
	} else
		layoutManager = new LayoutNotifier(layoutManager, listener);
}

/**
 * Adds a listener of type <i>clazz</i> to this Figure's list of event listeners.
 * @param clazz The listener type
 * @param listener The listener
 */
protected void addListener(Class clazz, Object listener) {
	eventListeners.addListener(clazz, listener);
}

/**
 * @see IFigure#addMouseListener(MouseListener)
 */
public void addMouseListener(MouseListener listener) {
	eventListeners.addListener(MouseListener.class, listener);
}

/**
 * @see IFigure#addMouseMotionListener(MouseMotionListener)
 */
public void addMouseMotionListener(MouseMotionListener listener) {
	eventListeners.addListener(MouseMotionListener.class, listener);
}

/**
 * Called after the receiver's parent has been set and it has been added to its parent.
 * 
 * @since 2.0
 */
public void addNotify() {
	if (getFlag(FLAG_REALIZED))
		throw new RuntimeException("addNotify() should not be called multiple times"); //$NON-NLS-1$
	setFlag(FLAG_REALIZED, true);
	for (int i = 0; i < children.size(); i++)
		((IFigure)children.get(i)).addNotify();
}

/**
 * @see IFigure#addPropertyChangeListener(String,
 * PropertyChangeListener)
 */
public void addPropertyChangeListener(String property, PropertyChangeListener listener) {
	if (propertyListeners == null)
		propertyListeners = new PropertyChangeSupport(this);
	propertyListeners.addPropertyChangeListener(property, listener);
}

/**
 * @see IFigure#addPropertyChangeListener(PropertyChangeListener)
 */
public void addPropertyChangeListener(PropertyChangeListener listener) {
	if (propertyListeners == null)
		propertyListeners = new PropertyChangeSupport(this);
	propertyListeners.addPropertyChangeListener(listener);
}

/**
 * This method is final.  Override {@link #containsPoint(int, int)} if needed.
 * @see IFigure#containsPoint(Point)
 * @since 2.0
 */
public final boolean containsPoint(Point p) {
	return containsPoint(p.x, p.y);
}

/**
 * @see IFigure#containsPoint(int, int)
 */
public boolean containsPoint(int x, int y) {
	return getBounds().contains(x, y);
}

/**
 * @see IFigure#erase()
 */
public void erase() {
	if (getParent() == null || !isVisible())
		return;
	
	Rectangle r = new Rectangle(getBounds());
	getParent().translateToParent(r);
	getParent().repaint(r.x, r.y, r.width, r.height);
}

/**
 * Returns a descendant of this Figure such that the Figure returned contains the point
 * (x, y), and is accepted by the given TreeSearch. Returns <code>null</code> if none 
 * found.
 * @param x The X coordinate
 * @param y The Y coordinate
 * @param search the TreeSearch
 * @return The descendant Figure at (x,y)
 */
protected IFigure findDescendantAtExcluding(int x, int y, TreeSearch search) {
	PRIVATE_POINT.setLocation(x, y);
	translateFromParent(PRIVATE_POINT);
	if (!getClientArea(Rectangle.SINGLETON).contains(PRIVATE_POINT))
		return null;

	x = PRIVATE_POINT.x;
	y = PRIVATE_POINT.y;
	IFigure fig;
	for (int i = children.size(); i > 0;) {
		i--;
		fig = (IFigure)children.get(i);
		if (fig.isVisible()) {
			fig = fig.findFigureAt(x, y, search);
			if (fig != null)
				return fig;
		}
	}
	//No descendants were found
	return null;
}

/**
 * @see IFigure#findFigureAt(Point)
 */
public final IFigure findFigureAt(Point pt) {
	return findFigureAtExcluding(pt.x, pt.y, Collections.EMPTY_LIST);
}

/**
 * @see IFigure#findFigureAt(int, int)
 */
public final IFigure findFigureAt(int x, int y) {
	return findFigureAt(x, y, IdentitySearch.INSTANCE);
}

/**
 * @see IFigure#findFigureAt(int, int, TreeSearch)
 */
public IFigure findFigureAt(int x, int y, TreeSearch search) {
	if (!containsPoint(x, y))
		return null;
	if (search.prune(this))
		return null;
	IFigure child = findDescendantAtExcluding(x, y, search);
	if (child != null)
		return child;
	if (search.accept(this))
		return this;
	return null;
}

/**
 * @see IFigure#findFigureAtExcluding(int, int, Collection)
 */
public final IFigure findFigureAtExcluding(int x, int y, Collection c) {
	return findFigureAt(x, y, new ExclusionSearch(c));
}

/**
 * Returns the deepest descendant for which {@link #isMouseEventTarget()} returns
 * <code>true</code> or <code>null</code> if none found. The Parameters <i>x</i> and
 * <i>y</i> are absolute locations. Any Graphics transformations applied by this Figure to
 * its children during {@link #paintChildren(Graphics)} (thus causing the children to
 * appear transformed to the user) should be applied inversely to the points <i>x</i> and
 * <i>y</i> when called on the children.
 * 
 * @param x The X coordinate
 * @param y The Y coordinate
 * @return The deepest descendant for which isMouseEventTarget() returns true
 */
public IFigure findMouseEventTargetAt(int x, int y) {
	if (!containsPoint(x, y))
		return null;
	IFigure f = findMouseEventTargetInDescendantsAt(x, y);
	if (f != null)
		return f;
	if (isMouseEventTarget())
		return this;
	return null;
}

/**
 * Searches this Figure's children for the deepest descendant for which 
 * {@link #isMouseEventTarget()} returns <code>true</code> and returns that descendant or
 * <code>null</code> if none found.
 * @see #findMouseEventTargetAt(int, int)
 * @param x The X coordinate
 * @param y The Y coordinate
 * @return The deepest descendant for which isMouseEventTarget() returns true
 */
protected IFigure findMouseEventTargetInDescendantsAt(int x, int y) {
	PRIVATE_POINT.setLocation(x, y);
	translateFromParent(PRIVATE_POINT);

	if (!getClientArea(Rectangle.SINGLETON).contains(PRIVATE_POINT))
		return null;

	IFigure fig;
	for (int i = children.size(); i > 0;) {
		i--;
		fig = (IFigure)children.get(i);
		if (fig.isVisible() && fig.isEnabled()) {
			if (fig.containsPoint(PRIVATE_POINT.x, PRIVATE_POINT.y)) {
				fig = fig.findMouseEventTargetAt(PRIVATE_POINT.x, PRIVATE_POINT.y);
				return fig;
			}
		}
	}
	return null;
}

/**
 * Notifies to all {@link CoordinateListener}s that this figure's local coordinate system
 * has changed in a way which affects the absolute bounds of figures contained within. 
 * 
 * @since 3.1
 */
protected void fireCoordinateSystemChanged() {
	if (!eventListeners.containsListener(CoordinateListener.class))
		return;
	Iterator figureListeners = eventListeners.getListeners(CoordinateListener.class);
	while (figureListeners.hasNext())
		((CoordinateListener)figureListeners.next()).
			coordinateSystemChanged(this);
}

/**
 * Notifies to all {@link FigureListener}s that this figure has moved. Moved means
 * that the bounds have changed in some way, location and/or size. 
 * @since 3.1
 */
protected void fireFigureMoved() {
	if (!eventListeners.containsListener(FigureListener.class))
		return;
	Iterator figureListeners = eventListeners.getListeners(FigureListener.class);
	while (figureListeners.hasNext())
		((FigureListener)figureListeners.next()).
			figureMoved(this);
}

/**
 * Fires both figuremoved and coordinate system changed. This method exists for
 * compatibility. Some listeners which used to listen for figureMoved now listen for
 * coordinates changed.  So to be sure that those new listeners are notified, any client
 * code which used called this method will also result in notification of coordinate
 * changes.
 * @since 2.0
 * @deprecated call fireFigureMoved() or fireCoordinateSystemChanged() as appropriate
 */
protected void fireMoved() {
	fireFigureMoved();
	fireCoordinateSystemChanged();
}

/**
 * Notifies any {@link PropertyChangeListener PropertyChangeListeners} listening to this
 * Figure that the boolean property with id <i>property</i> has changed.
 * @param property The id of the property that changed
 * @param old The old value of the changed property
 * @param current The current value of the changed property
 * @since 2.0
 */
protected void firePropertyChange(String property, boolean old, boolean current) {
	if (propertyListeners == null) 
		return;
	propertyListeners.firePropertyChange(property, old, current);
}

/**
 * Notifies any {@link PropertyChangeListener PropertyChangeListeners} listening to this
 * figure that the Object property with id <i>property</i> has changed.
 * @param property The id of the property that changed
 * @param old The old value of the changed property
 * @param current The current value of the changed property
 * @since 2.0
 */
protected void firePropertyChange(String property, Object old, Object current) {
	if (propertyListeners == null) 
		return;
	propertyListeners.firePropertyChange(property, old, current);
}

/**
 * Notifies any {@link PropertyChangeListener PropertyChangeListeners} listening to this
 * figure that the integer property with id <code>property</code> has changed.
 * @param property The id of the property that changed
 * @param old The old value of the changed property
 * @param current The current value of the changed property
 * @since 2.0
 */
protected void firePropertyChange(String property, int old, int current) {
	if (propertyListeners == null) 
		return;
	propertyListeners.firePropertyChange(property, old, current);
}

/**
 * Returns this Figure's background color.  If this Figure's background color is
 * <code>null</code> and its parent is not <code>null</code>, the background color is
 * inherited from the parent.
 * @see IFigure#getBackgroundColor()
 */
public Color getBackgroundColor() {
	if (bgColor == null && getParent() != null)
		return getParent().getBackgroundColor();
	return bgColor;
}

/**
 * @see IFigure#getBorder()
 */
public Border getBorder() {
	return border;
}

/**
 * Returns the smallest rectangle completely enclosing the figure. Implementors may return
 * the Rectangle by reference. For this reason, callers of this method must not modify the
 * returned Rectangle.
 * @return The bounds of this Figure
 */
public Rectangle getBounds() {
	return bounds;
}

/**
 * @see IFigure#getChildren()
 */
public List getChildren() {
	return children;
}

/**
 * @see IFigure#getClientArea(Rectangle)
 */
public Rectangle getClientArea(Rectangle rect) {
	rect.setBounds(getBounds());
	rect.crop(getInsets());
	if (useLocalCoordinates())
		rect.setLocation(0, 0);
	return rect;
}

/**
 * @see IFigure#getClientArea()
 */
public final Rectangle getClientArea() {
	return getClientArea(new Rectangle());
}

/**
 * @see IFigure#getCursor()
 */
public Cursor getCursor() {
	if (cursor == null && getParent() != null)
		return getParent().getCursor();
	return cursor;
}

/**
 * Returns the value of the given flag.
 * @param flag The flag to get
 * @return The value of the given flag
 */
protected boolean getFlag(int flag) {
	return (flags & flag) != 0;
}

/**
 * @see IFigure#getFont()
 */
public Font getFont() {
	if (font != null)
		return font;
	if (getParent() != null)
		return getParent().getFont();
	return null;
}

/**
 * @see IFigure#getForegroundColor()
 */
public Color getForegroundColor() {
	if (fgColor == null && getParent() != null)
		return getParent().getForegroundColor();
	return fgColor;
}

/**
 * Returns the border's Insets if the border is set. Otherwise returns NO_INSETS, an
 * instance of Insets with all 0s. Returns Insets by reference.  DO NOT Modify returned
 * value. Cannot return null.
 * @return This Figure's Insets
 */
public Insets getInsets() {
	if (getBorder() != null)
		return getBorder().getInsets(this);
	return NO_INSETS;
}

/**
 * @see IFigure#getLayoutManager()
 */
public LayoutManager getLayoutManager() {
	if (layoutManager instanceof LayoutNotifier)
		return ((LayoutNotifier)layoutManager).realLayout;
	return layoutManager;
}

/**
 * Returns an Iterator over the listeners of type <i>clazz</i> that are listening to
 * this Figure. If there are no listeners of type <i>clazz</i>, an empty iterator is
 * returned.
 * @param clazz The type of listeners to get
 * @return An Iterator over the requested listeners
 * @since 2.0
 */
protected Iterator getListeners(Class clazz) {
	if (eventListeners == null)
		return Collections.EMPTY_LIST.iterator();
	return eventListeners.getListeners(clazz);
}

/**
 * Returns <code>null</code> or the local background Color of this Figure. Does not
 * inherit this Color from the parent.
 * @return bgColor <code>null</code> or the local background Color
 */
public Color getLocalBackgroundColor() {
	return bgColor;
}	

/**
 * Returns <code>null</code> or the local font setting for this figure.  Does not return
 * values inherited from the parent figure.
 * @return <code>null</code> or the local font
 * @since 3.1
 */
protected Font getLocalFont() {
	return font;
}

/**
 * Returns <code>null</code> or the local foreground Color of this Figure. Does not
 * inherit this Color from the parent.
 * @return fgColor <code>null</code> or the local foreground Color
 */
public Color getLocalForegroundColor() {
	return fgColor;
}	

/**
 * Returns the top-left corner of this Figure's bounds.
 * @return The top-left corner of this Figure's bounds
 * @since 2.0
 */
public final Point getLocation() {
	return getBounds().getLocation();
}

/**
 * @see IFigure#getMaximumSize()
 */
public Dimension getMaximumSize() {
	if (maxSize != null)
		return maxSize;
	return MAX_DIMENSION;
}

/**
 * @see IFigure#getMinimumSize()
 */
public final Dimension getMinimumSize() {
	return getMinimumSize(-1, -1);
}

/**
 * @see IFigure#getMinimumSize(int, int)
 */
public Dimension getMinimumSize(int wHint, int hHint) {
	if (minSize != null)
		return minSize;
	if (getLayoutManager() != null) {
		Dimension d = getLayoutManager().getMinimumSize(this, wHint, hHint);
		if (d != null)
			return d;
	}
	return getPreferredSize(wHint, hHint);
}

/**
 * @see IFigure#getParent()
 */
public IFigure getParent() {
	return parent;
}

/**
 * @see IFigure#getPreferredSize()
 */
public final Dimension getPreferredSize() {
	return getPreferredSize(-1, -1);
}

/**
 * @see IFigure#getPreferredSize(int, int)
 */
public Dimension getPreferredSize(int wHint, int hHint) {
	if (prefSize != null)
		return prefSize;
	if (getLayoutManager() != null) {
		Dimension d = getLayoutManager().getPreferredSize(this, wHint, hHint);
		if (d != null)
			return d;
	}
	return getSize();
}

/**
 * @see IFigure#getSize()
 */
public final Dimension getSize() {
	return getBounds().getSize();
}

/**
 * @see IFigure#getToolTip()
 */
public IFigure getToolTip() {
	return toolTip;
}

/**
 * @see IFigure#getUpdateManager()
 */
public UpdateManager getUpdateManager() {
	if (getParent() != null) 
		return getParent().getUpdateManager();
	// Only happens when the figure has not been realized
	return NO_MANAGER;
}

/**
 * @see IFigure#handleFocusGained(FocusEvent)
 */
public void handleFocusGained(FocusEvent event) {
	Iterator iter = eventListeners.getListeners(FocusListener.class);
	while (iter.hasNext())
		((FocusListener)iter.next()).
			focusGained(event);
}

/**
 * @see IFigure#handleFocusLost(FocusEvent)
 */
public void handleFocusLost(FocusEvent event) {
	Iterator iter = eventListeners.getListeners(FocusListener.class);
	while (iter.hasNext())
		((FocusListener)iter.next()).
			focusLost(event);
}

/**
 * @see IFigure#handleKeyPressed(KeyEvent)
 */
public void handleKeyPressed(KeyEvent event) {
	Iterator iter = eventListeners.getListeners(KeyListener.class);
	while (!event.isConsumed() && iter.hasNext()) 
		((KeyListener)iter.next()).
			keyPressed(event);
}

/**
 * @see IFigure#handleKeyReleased(KeyEvent)
 */
public void handleKeyReleased(KeyEvent event) {
	Iterator iter = eventListeners.getListeners(KeyListener.class);
	while (!event.isConsumed() && iter.hasNext()) 
		((KeyListener)iter.next()).
			keyReleased(event);
}

/**
 * @see IFigure#handleMouseDoubleClicked(MouseEvent)
 */
public void handleMouseDoubleClicked(MouseEvent event) {
	Iterator iter = eventListeners.getListeners(MouseListener.class);
	while (!event.isConsumed() && iter.hasNext()) 
		((MouseListener)iter.next()).
			mouseDoubleClicked(event);
}

/**
 * @see IFigure#handleMouseDragged(MouseEvent)
 */
public void handleMouseDragged(MouseEvent event) {
	Iterator iter = eventListeners.getListeners(MouseMotionListener.class);
	while (!event.isConsumed() && iter.hasNext()) 
		((MouseMotionListener)iter.next()).
			mouseDragged(event);
}

/**
 * @see IFigure#handleMouseEntered(MouseEvent)
 */
public void handleMouseEntered(MouseEvent event) {
	Iterator iter = eventListeners.getListeners(MouseMotionListener.class);
	while (!event.isConsumed() && iter.hasNext()) 
		((MouseMotionListener)iter.next()).
			mouseEntered(event);
}

/**
 * @see IFigure#handleMouseExited(MouseEvent)
 */
public void handleMouseExited(MouseEvent event) {
	Iterator iter = eventListeners.getListeners(MouseMotionListener.class);
	while (!event.isConsumed() && iter.hasNext()) 
		((MouseMotionListener)iter.next()).
			mouseExited(event);
}

/**
 * @see IFigure#handleMouseHover(MouseEvent)
 */
public void handleMouseHover(MouseEvent event) {
	Iterator iter = eventListeners.getListeners(MouseMotionListener.class);
	while (!event.isConsumed() && iter.hasNext()) 
		((MouseMotionListener)iter.next()).
			mouseHover(event);
}

/**
 * @see IFigure#handleMouseMoved(MouseEvent)
 */
public void handleMouseMoved(MouseEvent event) {
	Iterator iter = eventListeners.getListeners(MouseMotionListener.class);
	while (!event.isConsumed() && iter.hasNext()) 
		((MouseMotionListener)iter.next()).
			mouseMoved(event);
}

/**
 * @see IFigure#handleMousePressed(MouseEvent)
 */
public void handleMousePressed(MouseEvent event) {
	Iterator iter = eventListeners.getListeners(MouseListener.class);
	while (!event.isConsumed() && iter.hasNext()) 
		((MouseListener)iter.next()).
			mousePressed(event);
}

/**
 * @see IFigure#handleMouseReleased(MouseEvent)
 */
public void handleMouseReleased(MouseEvent event) {
	Iterator iter = eventListeners.getListeners(MouseListener.class);
	while (!event.isConsumed() && iter.hasNext()) 
		((MouseListener)iter.next()).
			mouseReleased(event);
}

/**
 * @see IFigure#hasFocus()
 */
public boolean hasFocus() {
	EventDispatcher dispatcher = internalGetEventDispatcher();
	if (dispatcher == null)
		return false;
	return dispatcher.getFocusOwner() == this;
}

/**
 * @see IFigure#internalGetEventDispatcher()
 */
public EventDispatcher internalGetEventDispatcher() {
	if (getParent() != null)
		return getParent().internalGetEventDispatcher();
	return null;
}

/**
 * @see IFigure#intersects(Rectangle)
 */
public boolean intersects(Rectangle rect) {
	return getBounds().intersects(rect);
}

/**
 * @see IFigure#invalidate()
 */
public void invalidate() {
	if (layoutManager != null)
		layoutManager.invalidate();
	setValid(false);
}

/**
 * @see IFigure#invalidateTree()
 */
public void invalidateTree() {
	invalidate();
	for (Iterator iter = children.iterator(); iter.hasNext();) {
		IFigure child = (IFigure) iter.next();
		child.invalidateTree();
	}
}

/**
 * @see IFigure#isCoordinateSystem()
 */
public boolean isCoordinateSystem() {
	return useLocalCoordinates();
}

/**
 * @see IFigure#isEnabled()
 */
public boolean isEnabled() {
	return (flags & FLAG_ENABLED) != 0;
}

/**
 * @see IFigure#isFocusTraversable()
 */
public boolean isFocusTraversable() {
	return (flags & FLAG_FOCUS_TRAVERSABLE) != 0;
}

/**
 * Returns <code>true</code> if this Figure can receive {@link MouseEvent MouseEvents}.
 * @return <code>true</code> if this Figure can receive {@link MouseEvent MouseEvents}
 * @since 2.0
 */
protected boolean isMouseEventTarget() {
	return (eventListeners.containsListener(MouseListener.class)
		|| eventListeners.containsListener(MouseMotionListener.class));
}

/**
 * @see org.eclipse.draw2d.IFigure#isMirrored()
 */
public boolean isMirrored() {
	if (getParent() != null)
		return getParent().isMirrored();
	return false;
}

/**
 * @see IFigure#isOpaque()
 */
public boolean isOpaque() {
	return (flags & FLAG_OPAQUE) != 0;
}

/**
 * @see IFigure#isRequestFocusEnabled()
 */
public boolean isRequestFocusEnabled() {
	return (flags & FLAG_FOCUSABLE) != 0;
}

/**
 * @see IFigure#isShowing()
 */
public boolean isShowing() {
	return isVisible()
	  && (getParent() == null
		|| getParent().isShowing());
}

/**
 * Returns <code>true</code> if this Figure is valid.
 * @return <code>true</code> if this Figure is valid
 * @since 2.0
 */
protected boolean isValid() {
	return (flags & FLAG_VALID) != 0;
}

/**
 * Returns <code>true</code> if revalidating this Figure does not require revalidating its
 * parent.
 * @return <code>true</code> if revalidating this Figure doesn't require revalidating its
 * parent.
 * @since 2.0
 */
protected boolean isValidationRoot() {
	return false;
}

/**
 * @see IFigure#isVisible()
 */
public boolean isVisible() {
	return getFlag(FLAG_VISIBLE);
}

/**
 * Lays out this Figure using its {@link LayoutManager}.
 * 
 * @since 2.0
 */
protected void layout() {
	if (layoutManager != null)
		layoutManager.layout(this);
}

/**
 * Paints this Figure and its children.
 * @param graphics The Graphics object used for painting
 * @see #paintFigure(Graphics)
 * @see #paintClientArea(Graphics)
 * @see #paintBorder(Graphics)
 */
public void paint(Graphics graphics) {
	if (getLocalBackgroundColor() != null)
		graphics.setBackgroundColor(getLocalBackgroundColor());
	if (getLocalForegroundColor() != null)
		graphics.setForegroundColor(getLocalForegroundColor());
	if (font != null)
		graphics.setFont(font);

	graphics.pushState();
	try {
		paintFigure(graphics);
		graphics.restoreState();
		paintClientArea(graphics);
		paintBorder(graphics);
	} finally {
		graphics.popState();
	}
}

/**
 * Paints the border associated with this Figure, if one exists.
 * @param graphics The Graphics used to paint
 * @see Border#paint(IFigure, Graphics, Insets)
 * @since 2.0
 */
protected void paintBorder(Graphics graphics) {
	if (getBorder() != null)
		getBorder().paint(this, graphics, NO_INSETS);
}

/**
 * Paints this Figure's children. The caller must save the state of the graphics prior to
 * calling this method, such that <code>graphics.restoreState()</code> may be called
 * safely, and doing so will return the graphics to its original state when the method was
 * entered.
 * <P>
 * This method must leave the Graphics in its original state upon return.
 * @param graphics the graphics used to paint
 * @since 2.0
 */
protected void paintChildren(Graphics graphics) {
	IFigure child;

	Rectangle clip = Rectangle.SINGLETON;
	for (int i = 0; i < children.size(); i++) {
		child = (IFigure)children.get(i);
		if (child.isVisible() && child.intersects(graphics.getClip(clip))) {
			graphics.clipRect(child.getBounds());
			child.paint(graphics);
			graphics.restoreState();
		}
	}
}

/**
 * Paints this Figure's client area. The client area is typically defined as the anything
 * inside the Figure's {@link Border} or {@link Insets}, and by default includes the
 * children of this Figure. On return, this method must leave the given Graphics in its
 * initial state.
 * @param graphics The Graphics used to paint
 * @since 2.0
 */
protected void paintClientArea(Graphics graphics) {
	if (children.isEmpty())
		return;

	boolean optimizeClip = getBorder() == null || getBorder().isOpaque();

	if (useLocalCoordinates()) {
		graphics.translate(
			getBounds().x + getInsets().left,
			getBounds().y + getInsets().top);
		if (!optimizeClip)
			graphics.clipRect(getClientArea(PRIVATE_RECT));
		graphics.pushState();
		paintChildren(graphics);
		graphics.popState();
		graphics.restoreState();
	} else {
		if (optimizeClip)
			paintChildren(graphics);
		else {
			graphics.clipRect(getClientArea(PRIVATE_RECT));
			graphics.pushState();
			paintChildren(graphics);
			graphics.popState();
			graphics.restoreState();
		}
	}
}

/**
 * Paints this Figure's primary representation, or background. Changes made to the
 * graphics to the graphics current state will not affect the subsequent calls to {@link
 * #paintClientArea(Graphics)} and {@link #paintBorder(Graphics)}. Furthermore, it is safe
 * to call <code>graphics.restoreState()</code> within this method, and doing so will
 * restore the graphics to its original state upon entry.
 * @param graphics The Graphics used to paint
 * @since 2.0
 */
protected void paintFigure(Graphics graphics) {
	if (isOpaque())
		graphics.fillRectangle(getBounds());
	if (getBorder() instanceof AbstractBackground)
		((AbstractBackground) getBorder()).paintBackground(this, graphics, NO_INSETS);
}

/**
 * Translates this Figure's bounds, without firing a move.
 * @param dx The amount to translate horizontally
 * @param dy The amount to translate vertically
 * @see #translate(int, int)
 * @since 2.0
 */
protected void primTranslate(int dx, int dy) {
	bounds.x += dx;
	bounds.y += dy;
	if (useLocalCoordinates()) {
		fireCoordinateSystemChanged();
		return;
	}
	for (int i = 0; i < children.size(); i++)
		((IFigure)children.get(i)).translate(dx, dy);
}

/**
 * Removes the given child Figure from this Figure's hierarchy and revalidates this
 * Figure. The child Figure's {@link #removeNotify()} method is also called.
 * @param figure The Figure to remove
 */
public void remove(IFigure figure) {
	if ((figure.getParent() != this))
		throw new IllegalArgumentException(
				"Figure is not a child"); //$NON-NLS-1$
	if (getFlag(FLAG_REALIZED))
		figure.removeNotify();
	if (layoutManager != null)
		layoutManager.remove(figure);
	// The updates in the UpdateManager *have* to be
	// done asynchronously, else will result in 
	// incorrect dirty region corrections.
	figure.erase();
	figure.setParent(null);
	children.remove(figure);
	revalidate();
}

/**
 * Removes all children from this Figure.
 * 
 * @see #remove(IFigure)
 * @since 2.0
 */
public void removeAll() {
	List list = new ArrayList(getChildren());
	for (int i = 0; i < list.size(); i++) {
		remove((IFigure)list.get(i));
	}
}

/**
 * @see IFigure#removeAncestorListener(AncestorListener)
 */
public void removeAncestorListener(AncestorListener listener) {
	if (ancestorHelper != null) {
		ancestorHelper.removeAncestorListener(listener);
		if (ancestorHelper.isEmpty()) {
			ancestorHelper.dispose();
			ancestorHelper = null;
		}
	}
}

/**
 * @see IFigure#removeCoordinateListener(CoordinateListener)
 */
public void removeCoordinateListener(CoordinateListener listener) {
	eventListeners.removeListener(CoordinateListener.class, listener);
}

/**
 * @see IFigure#removeFigureListener(FigureListener)
 */
public void removeFigureListener(FigureListener listener) {
	eventListeners.removeListener(FigureListener.class, listener);
}

/**
 * @see IFigure#removeFocusListener(FocusListener)
 */
public void removeFocusListener(FocusListener listener) {
	eventListeners.removeListener(FocusListener.class, listener);
}

/**
 * @see IFigure#removeKeyListener(KeyListener)
 */
public void removeKeyListener(KeyListener listener) {
	eventListeners.removeListener(KeyListener.class, listener);
}

/**
 * Removes the first occurence of the given listener.
 * @since 3.1
 * @param listener the listener being removed
 */
public void removeLayoutListener(LayoutListener listener) {
	if (layoutManager instanceof LayoutNotifier) {
		LayoutNotifier notifier = (LayoutNotifier)layoutManager;
		notifier.listeners.remove(listener);
		if (notifier.listeners.isEmpty())
			layoutManager = notifier.realLayout;
	}
}

/**
 * Removes <i>listener</i> of type <i>clazz</i> from this Figure's list of listeners.
 * @param clazz The type of listener
 * @param listener The listener to remove
 * @since 2.0
 */
protected void removeListener(Class clazz, Object listener) {
	if (eventListeners == null)
		return;
	eventListeners.removeListener(clazz, listener);
}

/**
 * @see IFigure#removeMouseListener(MouseListener)
 */
public void removeMouseListener(MouseListener listener) {
	eventListeners.removeListener(MouseListener.class, listener);
}

/**
 * @see IFigure#removeMouseMotionListener(MouseMotionListener)
 */
public void removeMouseMotionListener(MouseMotionListener listener) {
	eventListeners.removeListener(MouseMotionListener.class, listener);
}

/**
 * Called prior to this figure's removal from its parent
 */
public void removeNotify() {
	for (int i = 0; i < children.size(); i++)
		((IFigure)children.get(i)).removeNotify();
	if (internalGetEventDispatcher() != null)
		internalGetEventDispatcher().requestRemoveFocus(this);
	setFlag(FLAG_REALIZED, false);
}

/**
 * @see IFigure#removePropertyChangeListener(PropertyChangeListener)
 */
public void removePropertyChangeListener(PropertyChangeListener listener) {
	if (propertyListeners == null) return;
	propertyListeners.removePropertyChangeListener(listener);
}

/**
 * @see IFigure#removePropertyChangeListener(String, PropertyChangeListener)
 */
public void removePropertyChangeListener(
	String property,
	PropertyChangeListener listener) {
	if (propertyListeners == null) return;
	propertyListeners.removePropertyChangeListener(property, listener);
}

/**
 * @see IFigure#repaint(Rectangle)
 */
public final void repaint(Rectangle rect) {
	repaint(rect.x, rect.y, rect.width, rect.height);
}

/**
 * @see IFigure#repaint(int, int, int, int)
 */
public void repaint(int x, int y, int w, int h) {
	if (isVisible())
		getUpdateManager().addDirtyRegion(this, x, y, w, h);
}

/**
 * @see IFigure#repaint()
 */
public void repaint() {
	repaint(getBounds());
}

/**
 * @see IFigure#requestFocus()
 */
public final void requestFocus() {
	if (!isRequestFocusEnabled() || hasFocus())
		return;
	EventDispatcher dispatcher = internalGetEventDispatcher();
	if (dispatcher == null)
		return;
	dispatcher.requestFocus(this);
}

/**
 * @see IFigure#revalidate()
 */
public void revalidate() {
	invalidate();
	if (getParent() == null || isValidationRoot())
		getUpdateManager().addInvalidFigure(this);
	else
		getParent().revalidate();
}

/**
 * @see IFigure#setBackgroundColor(Color)
 */
public void setBackgroundColor(Color bg) {
	bgColor = bg;
	repaint();
}

/**
 * @see IFigure#setBorder(Border)
 */
public void setBorder(Border border) {
	this.border = border;
	revalidate();
	repaint();
}

/**
 * Sets the bounds of this Figure to the Rectangle <i>rect</i>. Note that <i>rect</i> is
 * compared to the Figure's current bounds to determine what needs to be repainted and/or
 * exposed and if validation is required. Since {@link #getBounds()} may return the
 * current bounds by reference, it is not safe to modify that Rectangle and then call
 * setBounds() after making modifications. The figure would assume that the bounds are
 * unchanged, and no layout or paint would occur. For proper behavior, always use a copy.
 * @param rect The new bounds
 * @since 2.0
 */
public void setBounds(Rectangle rect) {
	int x = bounds.x,
	    y = bounds.y;

	boolean resize = (rect.width != bounds.width) || (rect.height != bounds.height),
		  translate = (rect.x != x) || (rect.y != y);

	if ((resize || translate) && isVisible())
		erase();
	if (translate) {
		int dx = rect.x - x;
		int dy = rect.y - y;
		primTranslate(dx, dy);
	}
	
	bounds.width = rect.width;
	bounds.height = rect.height;
	
	if (translate || resize) {
		if (resize)
			invalidate();
		fireFigureMoved();
		repaint();
	}
}

/**
 * Sets the direction of any {@link Orientable} children.  Allowable values for
 * <code>dir</code> are found in {@link PositionConstants}.
 * @param direction The direction
 * @see Orientable#setDirection(int)
 * @since 2.0
 */
protected void setChildrenDirection(int direction) {
	FigureIterator iterator = new FigureIterator(this);
	IFigure child;
	while (iterator.hasNext()) {
		child = iterator.nextFigure();
		if (child instanceof Orientable)
			((Orientable)child).setDirection(direction);
	}
}

/**
 * Sets all childrens' enabled property to <i>value</i>.
 * @param value The enable value
 * @see #setEnabled(boolean)
 * @since 2.0
 */
protected void setChildrenEnabled(boolean value) {
	FigureIterator iterator = new FigureIterator(this);
	while (iterator.hasNext())
		iterator.nextFigure().setEnabled(value);
}

/**
 * Sets the orientation of any {@link Orientable} children. Allowable values for
 * <i>orientation</i> are found in {@link PositionConstants}.
 * @param orientation The Orientation
 * @see Orientable#setOrientation(int)
 * @since 2.0
 */
protected void setChildrenOrientation(int orientation) {
	FigureIterator iterator = new FigureIterator(this);
	IFigure child;
	while (iterator.hasNext()) {
		child = iterator.nextFigure();
		if (child instanceof Orientable)
			((Orientable)child).setOrientation(orientation);
	}
}

/**
 * @see IFigure#setConstraint(IFigure, Object)
 */
public void setConstraint(IFigure child, Object constraint) {
	if (child.getParent() != this)
		throw new IllegalArgumentException(
			"Figure must be a child"); //$NON-NLS-1$
	
	if (layoutManager != null)
		layoutManager.setConstraint(child, constraint);
	revalidate();
}

/**
 * @see IFigure#setCursor(Cursor)
 */
public void setCursor(Cursor cursor) {
	if (this.cursor == cursor)
		return;
	this.cursor = cursor;
	EventDispatcher dispatcher = internalGetEventDispatcher();
	if (dispatcher != null)
		dispatcher.updateCursor();
}

/**
 * @see IFigure#setEnabled(boolean)
 */
public void setEnabled(boolean value) {
	if (isEnabled() == value) 
		return;
	setFlag(FLAG_ENABLED, value);
}

/**
 * Sets the given flag to the given value.
 * @param flag The flag to set
 * @param value The value
 * @since 2.0
 */
protected final void setFlag(int flag, boolean value) {
	if (value) 
		flags |= flag;
	else 
		flags &= ~flag;
}

/**
 * @see IFigure#setFocusTraversable(boolean)
 */
public void setFocusTraversable(boolean focusTraversable) {
	if (isFocusTraversable() == focusTraversable)
		return;
	setFlag(FLAG_FOCUS_TRAVERSABLE, focusTraversable);	
}

/**
 * @see IFigure#setFont(Font)
 */
public void setFont(Font f) {
	if (font != f) {
		font = f;
		revalidate();
		repaint();
	}
}

/**
 * @see IFigure#setForegroundColor(Color)
 */
public void setForegroundColor(Color fg) {
	if (fgColor != null && fgColor.equals(fg)) 
		return;
	fgColor = fg;
	repaint();
}

/**
 * @see IFigure#setLayoutManager(LayoutManager)
 */
public void setLayoutManager(LayoutManager manager) {
	if (layoutManager instanceof LayoutNotifier)
		((LayoutNotifier)layoutManager).realLayout = manager;
	else
		layoutManager = manager;
	revalidate();
}

/**
 * @see IFigure#setLocation(Point)
 */
public void setLocation(Point p) {
	if (getLocation().equals(p)) 
		return;
	Rectangle r = new Rectangle(getBounds());
	r.setLocation(p);
	setBounds(r);
}

/**
 * @see IFigure#setMaximumSize(Dimension)
 */
public void setMaximumSize(Dimension d) {
	if (maxSize != null && maxSize.equals(d)) 
		return;
	maxSize = d;
	revalidate();
}

/**
 * @see IFigure#setMinimumSize(Dimension)
 */
public void setMinimumSize(Dimension d) {
	if (minSize != null && minSize.equals(d)) 
		return;
	minSize = d;
	revalidate();
}

/**
 * @see IFigure#setOpaque(boolean)
 */
public void setOpaque(boolean opaque) {
	if (isOpaque() == opaque)
		return;
	setFlag(FLAG_OPAQUE, opaque);
	repaint();
}

/**
 * @see IFigure#setParent(IFigure)
 */
public void setParent(IFigure p) {
	IFigure oldParent = parent;
	parent = p;
	firePropertyChange("parent", oldParent, p);//$NON-NLS-1$
}

/**
 * @see IFigure#setPreferredSize(Dimension)
 */
public void setPreferredSize(Dimension size) {
	if (prefSize != null && prefSize.equals(size))
		return;
	prefSize = size;
	revalidate();
}

/**
 * Sets the preferred size of this figure.
 * @param w The new preferred width
 * @param h The new preferred height
 * @see #setPreferredSize(Dimension)
 * @since 2.0
 */
public final void setPreferredSize(int w, int h) {
	setPreferredSize(new Dimension(w, h));
}

/**
 * @see IFigure#setRequestFocusEnabled(boolean)
 */
public void setRequestFocusEnabled(boolean requestFocusEnabled) {
	if (isRequestFocusEnabled() == requestFocusEnabled)
		return;
	setFlag(FLAG_FOCUSABLE, requestFocusEnabled);	
}

/**
 * @see IFigure#setSize(Dimension)
 */
public final void setSize(Dimension d) {
	setSize(d.width, d.height);
}

/**
 * @see IFigure#setSize(int, int)
 */
public void setSize(int w, int h) {
	Rectangle bounds = getBounds();
	if (bounds.width == w && bounds.height == h)
		return;
	Rectangle r = new Rectangle(getBounds());
	r.setSize(w, h);
	setBounds(r);
}

/**
 * @see IFigure#setToolTip(IFigure)
 */
public void setToolTip(IFigure f) {
	if (toolTip == f)
		return;
	toolTip = f;
}

/**
 * Sets this figure to be valid if <i>value</i> is <code>true</code> and invalid
 * otherwise.
 * @param value The valid value 
 * @since 2.0
 */
public void setValid(boolean value) {
	setFlag(FLAG_VALID, value);
}

/**
 * @see IFigure#setVisible(boolean)
 */
public void setVisible(boolean visible) {
	boolean currentVisibility = isVisible();
	if (visible == currentVisibility) 
		return;
	if (currentVisibility) 
		erase();
	setFlag(FLAG_VISIBLE, visible);
	if (visible) 
		repaint();
	revalidate();
}

/**
 * @see IFigure#translate(int, int)
 */
public final void translate(int x, int y) {
	primTranslate(x, y);
	fireFigureMoved();
}

/**
 * @see IFigure#translateFromParent(Translatable)
 */
public void translateFromParent(Translatable t) {
	if (useLocalCoordinates())
		t.performTranslate(
			-getBounds().x - getInsets().left,
			-getBounds().y - getInsets().top);
}

/**
 * @see IFigure#translateToAbsolute(Translatable)
 */
public final void translateToAbsolute(Translatable t) {
	if (getParent() != null) {
		getParent().translateToParent(t);
		getParent().translateToAbsolute(t);
	}
}

/**
 * @see IFigure#translateToParent(Translatable)
 */
public void translateToParent(Translatable t) {
	if (useLocalCoordinates())
		t.performTranslate(
			getBounds().x + getInsets().left,
			getBounds().y + getInsets().top);
}

/**
 * @see IFigure#translateToRelative(Translatable)
 */
public final void translateToRelative(Translatable t) {
	if (getParent() != null) {
		getParent().translateToRelative(t);
		getParent().translateFromParent(t);
	}
}

/**
 * Returns <code>true</code> if this Figure uses local coordinates. This means its
 * children are placed relative to this Figure's top-left corner.
 * @return <code>true</code> if this Figure uses local coordinates
 * @since 2.0
 */
protected boolean useLocalCoordinates() {
	return false;
}

/**
 * @see IFigure#validate()
 */
public void validate() {
	if (isValid())
		return;
	setValid(true);
	layout();
	for (int i = 0; i < children.size(); i++)
		((IFigure)children.get(i)).validate();
}

/**
 * A search which does not filter any figures.
 * since 3.0
 */
protected static final class IdentitySearch implements TreeSearch {
	/**
	 * The singleton instance.
	 */
	public static final TreeSearch INSTANCE = new IdentitySearch();
	private IdentitySearch() { }
	/**
	 * Always returns <code>true</code>.
	 * @see TreeSearch#accept(IFigure)
	 */
	public boolean accept(IFigure f) {
		return true;
	}
	/**
	 * Always returns <code>false</code>.
	 * @see TreeSearch#prune(IFigure)
	 */
	public boolean prune(IFigure f) {
		return false;
	}
}

final class LayoutNotifier implements LayoutManager {
	
	LayoutManager realLayout;
	List listeners = new ArrayList(1);
	
	LayoutNotifier(LayoutManager layout, LayoutListener listener) {
		realLayout = layout;
		listeners.add(listener);
	}
		
	public Object getConstraint(IFigure child) {
		if (realLayout != null)
			return realLayout.getConstraint(child);
		return null;
	}

	public Dimension getMinimumSize(IFigure container, int wHint, int hHint) {
		if (realLayout != null)
			return realLayout.getMinimumSize(container, wHint, hHint);
		return null;
	}

	public Dimension getPreferredSize(IFigure container, int wHint, int hHint) {
		if (realLayout != null)
			return realLayout.getPreferredSize(container, wHint, hHint);
		return null;
	}

	public void invalidate() {
		for (int i = 0; i < listeners.size(); i++)
			((LayoutListener)listeners.get(i)).invalidate(Figure.this);
		
		if (realLayout != null)
			realLayout.invalidate();
	}

	public void layout(IFigure container) {
		boolean consumed = false;
		for (int i = 0; i < listeners.size(); i++)
			consumed |= ((LayoutListener)listeners.get(i)).layout(container);

		if (realLayout != null && !consumed)
			realLayout.layout(container);
		for (int i = 0; i < listeners.size(); i++)
			((LayoutListener)listeners.get(i)).postLayout(container);
	}

	public void remove(IFigure child) {
		for (int i = 0; i < listeners.size(); i++)
			((LayoutListener)listeners.get(i)).remove(child);
		if (realLayout != null)
			realLayout.remove(child);
	}

	public void setConstraint(IFigure child, Object constraint) {
		for (int i = 0; i < listeners.size(); i++)
			((LayoutListener)listeners.get(i)).setConstraint(child, constraint);
		if (realLayout != null)
			realLayout.setConstraint(child, constraint);
	}
}

/**
 * Iterates over a Figure's children.
 */
public static class FigureIterator {
	private List list;
	private int index;
	/**
	 * Constructs a new FigureIterator for the given Figure.
	 * @param figure The Figure whose children to iterate over
	 */
	public FigureIterator(IFigure figure) {
		list = figure.getChildren();
		index = list.size();
	}
	/**
	 * Returns the next Figure.
	 * @return The next Figure
	 */
	public IFigure nextFigure() {
		return (IFigure)list.get(--index);
	}
	/**
	 * Returns <code>true</code> if there's another Figure to iterate over.
	 * @return <code>true</code> if there's another Figure to iterate over
	 */
	public boolean hasNext() {
		return index > 0;
	}
}

/**
 * An UpdateManager that does nothing.
 */
protected static final UpdateManager NO_MANAGER = new UpdateManager() {
	public void addDirtyRegion (IFigure figure, int x, int y, int w, int h) { }
	public void addInvalidFigure(IFigure f) { }
	public void performUpdate() { }
	public void performUpdate(Rectangle region) { }
	public void setRoot(IFigure root) { }
	public void setGraphicsSource(GraphicsSource gs) { }
};

}
