Mercurial > projects > dwt-addons
diff dwtx/draw2d/Figure.d @ 98:95307ad235d9
Added Draw2d code, still work in progress
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 03 Aug 2008 00:52:14 +0200 |
parents | |
children | 1082a0fc2bb8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Figure.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,1922 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.draw2d.Figure; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Bean; +import dwtx.dwtxhelper.Collection; + +import dwt.graphics.Color; +import dwt.graphics.Cursor; +import dwt.graphics.Font; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.Translatable; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.LayoutManager; +import dwtx.draw2d.EventListenerList; +import dwtx.draw2d.Border; +import dwtx.draw2d.AncestorHelper; +import dwtx.draw2d.CoordinateListener; +import dwtx.draw2d.FigureListener; +import dwtx.draw2d.FocusListener; +import dwtx.draw2d.KeyListener; +import dwtx.draw2d.MouseListener; +import dwtx.draw2d.MouseMotionListener; +import dwtx.draw2d.TreeSearch; +import dwtx.draw2d.LayoutListener; +import dwtx.draw2d.UpdateManager; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.AncestorListener; +import dwtx.draw2d.MouseEvent; +import dwtx.draw2d.EventDispatcher; +import dwtx.draw2d.FocusEvent; +import dwtx.draw2d.KeyEvent; +import dwtx.draw2d.AncestorHelper; +import dwtx.draw2d.ExclusionSearch; +import dwtx.draw2d.AbstractBackground; +import dwtx.draw2d.Orientable; +import dwtx.draw2d.GraphicsSource; + +/** + * The base implementation for graphical figures. + */ +public class Figure + : IFigure +{ + +private static /+const+/ Rectangle PRIVATE_RECT; +private static /+const+/ Point PRIVATE_POINT; +private static const int + FLAG_VALID = 1, + FLAG_OPAQUE = (1 << 1), + FLAG_VISIBLE = (1 << 2), + FLAG_FOCUSABLE = (1 << 3), + FLAG_ENABLED = (1 << 4), + FLAG_FOCUS_TRAVERSABLE = (1 << 5); + +static const 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 const int MAX_FLAG = FLAG_FOCUS_TRAVERSABLE; + +/** + * The rectangular area that this Figure occupies. + */ +protected Rectangle bounds; + +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; + +private List children; + +/** + * 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; + +private static void static_this(){ + if( PRIVATE_RECT is null ){ + synchronized( Figure.classinfo ){ + if( PRIVATE_RECT is null ){ + PRIVATE_RECT = new Rectangle(); + PRIVATE_POINT = new Point(); + } + } + } +} + +private void instanceInit(){ + static_this(); + bounds = new Rectangle(0, 0, 0, 0); + eventListeners = new EventListenerList(); + children = Collections.EMPTY_LIST; +} + +this(){ + instanceInit(); +} +/** + * 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 is 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 !is null; f = f.getParent()) + if (figure is f) + throw new IllegalArgumentException( + "Figure being added introduces cycle"); //$NON-NLS-1$ + + //Detach the child from previous parent + if (figure.getParent() !is null) + figure.getParent().remove(figure); + + if (index is -1) + children.add(cast(Object) figure); + else + children.add(index, cast(Object)figure); + figure.setParent(this); + + if (layoutManager !is 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 is null) + ancestorHelper = new AncestorHelper(this); + ancestorHelper.addAncestorListener(ancestorListener); +} + +/** + * @see IFigure#addCoordinateListener(CoordinateListener) + */ +public void addCoordinateListener(CoordinateListener listener) { + eventListeners.addListener(CoordinateListener.classinfo, cast(Object)listener); +} + +/** + * @see IFigure#addFigureListener(FigureListener) + */ +public void addFigureListener(FigureListener listener) { + eventListeners.addListener(FigureListener.classinfo, cast(Object)listener); +} + +/** + * @see IFigure#addFocusListener(FocusListener) + */ +public void addFocusListener(FocusListener listener) { + eventListeners.addListener(FocusListener.classinfo, cast(Object)listener); +} + +/** + * @see IFigure#addKeyListener(KeyListener) + */ +public void addKeyListener(KeyListener listener) { + eventListeners.addListener(KeyListener.classinfo, cast(Object)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 (auto n = cast(LayoutNotifier)layoutManager ) { + LayoutNotifier notifier = n; + notifier.listeners.add(cast(Object)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(ClassInfo clazz, Object listener) { + eventListeners.addListener(clazz, cast(Object)listener); +} + +/** + * @see IFigure#addMouseListener(MouseListener) + */ +public void addMouseListener(MouseListener listener) { + eventListeners.addListener(MouseListener.classinfo, cast(Object)listener); +} + +/** + * @see IFigure#addMouseMotionListener(MouseMotionListener) + */ +public void addMouseMotionListener(MouseMotionListener listener) { + eventListeners.addListener(MouseMotionListener.classinfo, cast(Object)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++) + (cast(IFigure)children.get(i)).addNotify(); +} + +/** + * @see IFigure#addPropertyChangeListener(String, + * PropertyChangeListener) + */ +public void addPropertyChangeListener(String property, PropertyChangeListener listener) { + if (propertyListeners is null) + propertyListeners = new PropertyChangeSupport(this); + propertyListeners.addPropertyChangeListener(property, listener); +} + +/** + * @see IFigure#addPropertyChangeListener(PropertyChangeListener) + */ +public void addPropertyChangeListener(PropertyChangeListener listener) { + if (propertyListeners is 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 bool containsPoint(Point p) { + return containsPoint(p.x, p.y); +} + +/** + * @see IFigure#containsPoint(int, int) + */ +public bool containsPoint(int x, int y) { + return getBounds().contains(x, y); +} + +/** + * @see IFigure#erase() + */ +public void erase() { + if (getParent() is 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 = cast(IFigure)children.get(i); + if (fig.isVisible()) { + fig = fig.findFigureAt(x, y, search); + if (fig !is 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 !is 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 !is 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 = cast(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.classinfo)) + return; + Iterator figureListeners = eventListeners.getListeners(CoordinateListener.classinfo); + while (figureListeners.hasNext()) + (cast(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.classinfo)) + return; + Iterator figureListeners = eventListeners.getListeners(FigureListener.classinfo); + while (figureListeners.hasNext()) + (cast(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 bool 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, bool old, bool current) { + if (propertyListeners is 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 is 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 is 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 is null && getParent() !is 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 is null && getParent() !is 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 bool getFlag(int flag) { + return (flags & flag) !is 0; +} + +/** + * @see IFigure#getFont() + */ +public Font getFont() { + if (font !is null) + return font; + if (getParent() !is null) + return getParent().getFont(); + return null; +} + +/** + * @see IFigure#getForegroundColor() + */ +public Color getForegroundColor() { + if (fgColor is null && getParent() !is 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() !is null) + return getBorder().getInsets(this); + return IFigure_NO_INSETS; +} + +/** + * @see IFigure#getLayoutManager() + */ +public LayoutManager getLayoutManager() { + if (auto n = cast(LayoutNotifier)layoutManager ) + return n.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(ClassInfo clazz) { + if (eventListeners is 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 !is null) + return maxSize; + return IFigure_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 !is null) + return minSize; + if (getLayoutManager() !is null) { + Dimension d = getLayoutManager().getMinimumSize(this, wHint, hHint); + if (d !is 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 !is null) + return prefSize; + if (getLayoutManager() !is null) { + Dimension d = getLayoutManager().getPreferredSize(this, wHint, hHint); + if (d !is 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() !is 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.classinfo); + while (iter.hasNext()) + (cast(FocusListener)iter.next()). + focusGained(event); +} + +/** + * @see IFigure#handleFocusLost(FocusEvent) + */ +public void handleFocusLost(FocusEvent event) { + Iterator iter = eventListeners.getListeners(FocusListener.classinfo); + while (iter.hasNext()) + (cast(FocusListener)iter.next()). + focusLost(event); +} + +/** + * @see IFigure#handleKeyPressed(KeyEvent) + */ +public void handleKeyPressed(KeyEvent event) { + Iterator iter = eventListeners.getListeners(KeyListener.classinfo); + while (!event.isConsumed() && iter.hasNext()) + (cast(KeyListener)iter.next()). + keyPressed(event); +} + +/** + * @see IFigure#handleKeyReleased(KeyEvent) + */ +public void handleKeyReleased(KeyEvent event) { + Iterator iter = eventListeners.getListeners(KeyListener.classinfo); + while (!event.isConsumed() && iter.hasNext()) + (cast(KeyListener)iter.next()). + keyReleased(event); +} + +/** + * @see IFigure#handleMouseDoubleClicked(MouseEvent) + */ +public void handleMouseDoubleClicked(MouseEvent event) { + Iterator iter = eventListeners.getListeners(MouseListener.classinfo); + while (!event.isConsumed() && iter.hasNext()) + (cast(MouseListener)iter.next()). + mouseDoubleClicked(event); +} + +/** + * @see IFigure#handleMouseDragged(MouseEvent) + */ +public void handleMouseDragged(MouseEvent event) { + Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo); + while (!event.isConsumed() && iter.hasNext()) + (cast(MouseMotionListener)iter.next()). + mouseDragged(event); +} + +/** + * @see IFigure#handleMouseEntered(MouseEvent) + */ +public void handleMouseEntered(MouseEvent event) { + Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo); + while (!event.isConsumed() && iter.hasNext()) + (cast(MouseMotionListener)iter.next()). + mouseEntered(event); +} + +/** + * @see IFigure#handleMouseExited(MouseEvent) + */ +public void handleMouseExited(MouseEvent event) { + Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo); + while (!event.isConsumed() && iter.hasNext()) + (cast(MouseMotionListener)iter.next()). + mouseExited(event); +} + +/** + * @see IFigure#handleMouseHover(MouseEvent) + */ +public void handleMouseHover(MouseEvent event) { + Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo); + while (!event.isConsumed() && iter.hasNext()) + (cast(MouseMotionListener)iter.next()). + mouseHover(event); +} + +/** + * @see IFigure#handleMouseMoved(MouseEvent) + */ +public void handleMouseMoved(MouseEvent event) { + Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo); + while (!event.isConsumed() && iter.hasNext()) + (cast(MouseMotionListener)iter.next()). + mouseMoved(event); +} + +/** + * @see IFigure#handleMousePressed(MouseEvent) + */ +public void handleMousePressed(MouseEvent event) { + Iterator iter = eventListeners.getListeners(MouseListener.classinfo); + while (!event.isConsumed() && iter.hasNext()) + (cast(MouseListener)iter.next()). + mousePressed(event); +} + +/** + * @see IFigure#handleMouseReleased(MouseEvent) + */ +public void handleMouseReleased(MouseEvent event) { + Iterator iter = eventListeners.getListeners(MouseListener.classinfo); + while (!event.isConsumed() && iter.hasNext()) + (cast(MouseListener)iter.next()). + mouseReleased(event); +} + +/** + * @see IFigure#hasFocus() + */ +public bool hasFocus() { + EventDispatcher dispatcher = internalGetEventDispatcher(); + if (dispatcher is null) + return false; + return dispatcher.getFocusOwner() is this; +} + +/** + * @see IFigure#internalGetEventDispatcher() + */ +public EventDispatcher internalGetEventDispatcher() { + if (getParent() !is null) + return getParent().internalGetEventDispatcher(); + return null; +} + +/** + * @see IFigure#intersects(Rectangle) + */ +public bool intersects(Rectangle rect) { + return getBounds().intersects(rect); +} + +/** + * @see IFigure#invalidate() + */ +public void invalidate() { + if (layoutManager !is null) + layoutManager.invalidate(); + setValid(false); +} + +/** + * @see IFigure#invalidateTree() + */ +public void invalidateTree() { + invalidate(); + for (Iterator iter = children.iterator(); iter.hasNext();) { + IFigure child = cast(IFigure) iter.next(); + child.invalidateTree(); + } +} + +/** + * @see IFigure#isCoordinateSystem() + */ +public bool isCoordinateSystem() { + return useLocalCoordinates(); +} + +/** + * @see IFigure#isEnabled() + */ +public bool isEnabled() { + return (flags & FLAG_ENABLED) !is 0; +} + +/** + * @see IFigure#isFocusTraversable() + */ +public bool isFocusTraversable() { + return (flags & FLAG_FOCUS_TRAVERSABLE) !is 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 bool isMouseEventTarget() { + return (eventListeners.containsListener(MouseListener.classinfo) + || eventListeners.containsListener(MouseMotionListener.classinfo)); +} + +/** + * @see dwtx.draw2d.IFigure#isMirrored() + */ +public bool isMirrored() { + if (getParent() !is null) + return getParent().isMirrored(); + return false; +} + +/** + * @see IFigure#isOpaque() + */ +public bool isOpaque() { + return (flags & FLAG_OPAQUE) !is 0; +} + +/** + * @see IFigure#isRequestFocusEnabled() + */ +public bool isRequestFocusEnabled() { + return (flags & FLAG_FOCUSABLE) !is 0; +} + +/** + * @see IFigure#isShowing() + */ +public bool isShowing() { + return isVisible() + && (getParent() is 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 bool isValid() { + return (flags & FLAG_VALID) !is 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 bool isValidationRoot() { + return false; +} + +/** + * @see IFigure#isVisible() + */ +public bool isVisible() { + return getFlag(FLAG_VISIBLE); +} + +/** + * Lays out this Figure using its {@link LayoutManager}. + * + * @since 2.0 + */ +protected void layout() { + if (layoutManager !is 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() !is null) + graphics.setBackgroundColor(getLocalBackgroundColor()); + if (getLocalForegroundColor() !is null) + graphics.setForegroundColor(getLocalForegroundColor()); + if (font !is 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() !is null) + getBorder().paint(this, graphics, IFigure_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 = cast(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; + + bool optimizeClip = getBorder() is 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 (null !is cast(AbstractBackground)getBorder() ) + (cast(AbstractBackground) getBorder()).paintBackground(this, graphics, IFigure_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++) + (cast(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() !is this)) + throw new IllegalArgumentException( + "Figure is not a child"); //$NON-NLS-1$ + if (getFlag(FLAG_REALIZED)) + figure.removeNotify(); + if (layoutManager !is 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(cast(Object)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(cast(IFigure)list.get(i)); + } +} + +/** + * @see IFigure#removeAncestorListener(AncestorListener) + */ +public void removeAncestorListener(AncestorListener listener) { + if (ancestorHelper !is null) { + ancestorHelper.removeAncestorListener(listener); + if (ancestorHelper.isEmpty()) { + ancestorHelper.dispose(); + ancestorHelper = null; + } + } +} + +/** + * @see IFigure#removeCoordinateListener(CoordinateListener) + */ +public void removeCoordinateListener(CoordinateListener listener) { + eventListeners.removeListener(CoordinateListener.classinfo, cast(Object)listener); +} + +/** + * @see IFigure#removeFigureListener(FigureListener) + */ +public void removeFigureListener(FigureListener listener) { + eventListeners.removeListener(FigureListener.classinfo, cast(Object)listener); +} + +/** + * @see IFigure#removeFocusListener(FocusListener) + */ +public void removeFocusListener(FocusListener listener) { + eventListeners.removeListener(FocusListener.classinfo, cast(Object)listener); +} + +/** + * @see IFigure#removeKeyListener(KeyListener) + */ +public void removeKeyListener(KeyListener listener) { + eventListeners.removeListener(KeyListener.classinfo, cast(Object)listener); +} + +/** + * Removes the first occurence of the given listener. + * @since 3.1 + * @param listener the listener being removed + */ +public void removeLayoutListener(LayoutListener listener) { + if (auto notifier = cast(LayoutNotifier)layoutManager ) { + notifier.listeners.remove(cast(Object)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(ClassInfo clazz, Object listener) { + if (eventListeners is null) + return; + eventListeners.removeListener(clazz, listener); +} + +/** + * @see IFigure#removeMouseListener(MouseListener) + */ +public void removeMouseListener(MouseListener listener) { + eventListeners.removeListener(MouseListener.classinfo, cast(Object)listener); +} + +/** + * @see IFigure#removeMouseMotionListener(MouseMotionListener) + */ +public void removeMouseMotionListener(MouseMotionListener listener) { + eventListeners.removeListener(MouseMotionListener.classinfo, cast(Object)listener); +} + +/** + * Called prior to this figure's removal from its parent + */ +public void removeNotify() { + for (int i = 0; i < children.size(); i++) + (cast(IFigure)children.get(i)).removeNotify(); + if (internalGetEventDispatcher() !is null) + internalGetEventDispatcher().requestRemoveFocus(this); + setFlag(FLAG_REALIZED, false); +} + +/** + * @see IFigure#removePropertyChangeListener(PropertyChangeListener) + */ +public void removePropertyChangeListener(PropertyChangeListener listener) { + if (propertyListeners is null) return; + propertyListeners.removePropertyChangeListener(listener); +} + +/** + * @see IFigure#removePropertyChangeListener(String, PropertyChangeListener) + */ +public void removePropertyChangeListener( + String property, + PropertyChangeListener listener) { + if (propertyListeners is 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 is null) + return; + dispatcher.requestFocus(this); +} + +/** + * @see IFigure#revalidate() + */ +public void revalidate() { + invalidate(); + if (getParent() is 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; + + bool resize = (rect.width !is bounds.width) || (rect.height !is bounds.height), + translate = (rect.x !is x) || (rect.y !is 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 ( auto c = cast(Orientable)child ) + c.setDirection(direction); + } +} + +/** + * Sets all childrens' enabled property to <i>value</i>. + * @param value The enable value + * @see #setEnabled(bool) + * @since 2.0 + */ +protected void setChildrenEnabled(bool 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 (auto c = cast(Orientable)child ) + c.setOrientation(orientation); + } +} + +/** + * @see IFigure#setConstraint(IFigure, Object) + */ +public void setConstraint(IFigure child, Object constraint) { + if (child.getParent() !is this) + throw new IllegalArgumentException( + "Figure must be a child"); //$NON-NLS-1$ + + if (layoutManager !is null) + layoutManager.setConstraint(child, constraint); + revalidate(); +} + +/** + * @see IFigure#setCursor(Cursor) + */ +public void setCursor(Cursor cursor) { + if (this.cursor is cursor) + return; + this.cursor = cursor; + EventDispatcher dispatcher = internalGetEventDispatcher(); + if (dispatcher !is null) + dispatcher.updateCursor_package(); +} + +/** + * @see IFigure#setEnabled(bool) + */ +public void setEnabled(bool value) { + if (isEnabled() is 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, bool value) { + if (value) + flags |= flag; + else + flags &= ~flag; +} + +/** + * @see IFigure#setFocusTraversable(bool) + */ +public void setFocusTraversable(bool focusTraversable) { + if (isFocusTraversable() is focusTraversable) + return; + setFlag(FLAG_FOCUS_TRAVERSABLE, focusTraversable); +} + +/** + * @see IFigure#setFont(Font) + */ +public void setFont(Font f) { + if (font !is f) { + font = f; + revalidate(); + repaint(); + } +} + +/** + * @see IFigure#setForegroundColor(Color) + */ +public void setForegroundColor(Color fg) { + if (fgColor !is null && fgColor.opEquals(fg)) + return; + fgColor = fg; + repaint(); +} + +/** + * @see IFigure#setLayoutManager(LayoutManager) + */ +public void setLayoutManager(LayoutManager manager) { + if (auto n = cast(LayoutNotifier)layoutManager ) + n.realLayout = manager; + else + layoutManager = manager; + revalidate(); +} + +/** + * @see IFigure#setLocation(Point) + */ +public void setLocation(Point p) { + if (getLocation().opEquals(p)) + return; + Rectangle r = new Rectangle(getBounds()); + r.setLocation(p); + setBounds(r); +} + +/** + * @see IFigure#setMaximumSize(Dimension) + */ +public void setMaximumSize(Dimension d) { + if (maxSize !is null && maxSize.opEquals(d)) + return; + maxSize = d; + revalidate(); +} + +/** + * @see IFigure#setMinimumSize(Dimension) + */ +public void setMinimumSize(Dimension d) { + if (minSize !is null && minSize.opEquals(d)) + return; + minSize = d; + revalidate(); +} + +/** + * @see IFigure#setOpaque(bool) + */ +public void setOpaque(bool opaque) { + if (isOpaque() is opaque) + return; + setFlag(FLAG_OPAQUE, opaque); + repaint(); +} + +/** + * @see IFigure#setParent(IFigure) + */ +public void setParent(IFigure p) { + IFigure oldParent = parent; + parent = p; + firePropertyChange("parent", cast(Object)oldParent, cast(Object)p);//$NON-NLS-1$ +} + +/** + * @see IFigure#setPreferredSize(Dimension) + */ +public void setPreferredSize(Dimension size) { + if (prefSize !is null && prefSize.opEquals(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(bool) + */ +public void setRequestFocusEnabled(bool requestFocusEnabled) { + if (isRequestFocusEnabled() is 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 is w && bounds.height is h) + return; + Rectangle r = new Rectangle(getBounds()); + r.setSize(w, h); + setBounds(r); +} + +/** + * @see IFigure#setToolTip(IFigure) + */ +public void setToolTip(IFigure f) { + if (toolTip is 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(bool value) { + setFlag(FLAG_VALID, value); +} + +/** + * @see IFigure#setVisible(bool) + */ +public void setVisible(bool visible) { + bool currentVisibility = isVisible(); + if (visible is 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() !is 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() !is 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 bool useLocalCoordinates() { + return false; +} + +/** + * @see IFigure#validate() + */ +public void validate() { + if (isValid()) + return; + setValid(true); + layout(); + for (int i = 0; i < children.size(); i++) + (cast(IFigure)children.get(i)).validate(); +} + +/** + * A search which does not filter any figures. + * since 3.0 + */ +protected static final class IdentitySearch : TreeSearch { + /** + * The singleton instance. + */ + public static const TreeSearch INSTANCE; + static this(){ + INSTANCE = new IdentitySearch(); + } + private this() { } + /** + * Always returns <code>true</code>. + * @see TreeSearch#accept(IFigure) + */ + public bool accept(IFigure f) { + return true; + } + /** + * Always returns <code>false</code>. + * @see TreeSearch#prune(IFigure) + */ + public bool prune(IFigure f) { + return false; + } +} + +final class LayoutNotifier : LayoutManager { + + LayoutManager realLayout; + List listeners; + + this(LayoutManager layout, LayoutListener listener) { + listeners = new ArrayList(1); + realLayout = layout; + listeners.add(cast(Object)listener); + } + + public Object getConstraint(IFigure child) { + if (realLayout !is null) + return realLayout.getConstraint(child); + return null; + } + + public Dimension getMinimumSize(IFigure container, int wHint, int hHint) { + if (realLayout !is null) + return realLayout.getMinimumSize(container, wHint, hHint); + return null; + } + + public Dimension getPreferredSize(IFigure container, int wHint, int hHint) { + if (realLayout !is null) + return realLayout.getPreferredSize(container, wHint, hHint); + return null; + } + + public void invalidate() { + for (int i = 0; i < listeners.size(); i++) + (cast(LayoutListener)listeners.get(i)).invalidate(this.outer); + + if (realLayout !is null) + realLayout.invalidate(); + } + + public void layout(IFigure container) { + bool consumed = false; + for (int i = 0; i < listeners.size(); i++) + consumed |= (cast(LayoutListener)listeners.get(i)).layout(container); + + if (realLayout !is null && !consumed) + realLayout.layout(container); + for (int i = 0; i < listeners.size(); i++) + (cast(LayoutListener)listeners.get(i)).postLayout(container); + } + + public void remove(IFigure child) { + for (int i = 0; i < listeners.size(); i++) + (cast(LayoutListener)listeners.get(i)).remove(child); + if (realLayout !is null) + realLayout.remove(child); + } + + public void setConstraint(IFigure child, Object constraint) { + for (int i = 0; i < listeners.size(); i++) + (cast(LayoutListener)listeners.get(i)).setConstraint(child, constraint); + if (realLayout !is 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 this(IFigure figure) { + list = figure.getChildren(); + index = list.size(); + } + /** + * Returns the next Figure. + * @return The next Figure + */ + public IFigure nextFigure() { + return cast(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 bool hasNext() { + return index > 0; + } +} + +/** + * An UpdateManager that does nothing. + */ +protected static const UpdateManager NO_MANAGER; +static this(){ + NO_MANAGER = new class() 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) { } + }; +} +}