# HG changeset patch # User Frank Benoit # Date 1217717534 -7200 # Node ID 95307ad235d9bd0c371a5842ef8eadbd78915880 # Parent b492ba44e44dcc1ad1486bba389484752f8f82b3 Added Draw2d code, still work in progress diff -r b492ba44e44d -r 95307ad235d9 dsss.conf --- a/dsss.conf Sat Jul 26 14:39:08 2008 +0200 +++ b/dsss.conf Sun Aug 03 00:52:14 2008 +0200 @@ -1,9 +1,13 @@ +[dwtx/draw2d] +type=library +buildflags+=-Jres [dwtx] type=library +exclude=dwtx/draw2d buildflags+=-Jres preinstall = \ installdir res $LIB_PREFIX/res diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AbsoluteBendpoint.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AbsoluteBendpoint.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AbsoluteBendpoint; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.Bendpoint; + +/** + * AbsoluteBendpoint is a Bendpoint that defines its location simply as its X and Y + * coordinates. It is used by bendable {@link Connection Connections}. + */ +public class AbsoluteBendpoint + : Point + , Bendpoint +{ + +/** + * Creates a new AbsoluteBendpoint at the Point p. + * @param p The absolute location of the bendpoint + * @since 2.0 + */ +public this(Point p) { + super(p); +} + +/** + * Creates a new AbsoluteBendpoint at the Point (x,y). + * @param x The X coordinate + * @param y The Y coordinate + * @since 2.0 + */ +public this(int x, int y) { + super(x, y); +} + +/** + * @see dwtx.draw2d.Bendpoint#getLocation() + */ +public Point getLocation() { + return this; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AbstractBackground.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AbstractBackground.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +module dwtx.draw2d.AbstractBackground; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.AbstractBorder; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; + +/** + * A special border which can paint both underneath and on top of a Figure. + * Normal borders only paint on top of a figure and its children. A background + * has the opportunity to paint both first, and optionally last. + *

+ * WARNING: Experimental for 3.3. Clients should help validate the use cases + * of this new function. + * @since 3.3 + */ +public class AbstractBackground : AbstractBorder { + + /** + * {@inheritDoc} + */ + public Insets getInsets(IFigure figure) { + return IFigure_NO_INSETS; + } + + /** + * {@inheritDoc} + * By default, this method is stubbed out for backgrounds which only paint + * underneath a figure. + */ + public void paint(IFigure figure, Graphics graphics, Insets insets) { + } + + /** + * Called when this Background should paint. If the background is being painted + * inside another border or background, the insets indicate how far inside the + * target figure the background should be painted. In most cases, the insets + * will be all zero. + * @param figure The figure on which the background is being painted + * @param graphics The graphics + * @param insets Amount to inset from the figure's bounds + * @since 3.2 + */ + public void paintBackground(IFigure figure, Graphics graphics, Insets insets) { + } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AbstractBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AbstractBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AbstractBorder; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.Border; +import dwtx.draw2d.IFigure; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; + +/** + * Provides generic support for borders. + * @author hudsonr + */ +public abstract class AbstractBorder + : Border +{ + +private static const Dimension EMPTY; + +/** A temporary Rectangle*/ +protected static Rectangle tempRect; + +static this(){ + EMPTY = new Dimension(); + tempRect = new Rectangle(); +} + +/** + * Returns a temporary rectangle representing the figure's bounds cropped by the specified + * insets. This method exists for convenience and performance; the method does not new + * any Objects and returns a rectangle which the caller can manipulate. + * @since 2.0 + * @param figure Figure for which the paintable rectangle is needed + * @param insets The insets + * @return The paintable region on the Figure f + */ +protected static final Rectangle getPaintRectangle(IFigure figure, Insets insets) { + tempRect.setBounds(figure.getBounds()); + return tempRect.crop(insets); +} + +/** + * @see dwtx.draw2d.Border#getPreferredSize(IFigure) + */ +public Dimension getPreferredSize(IFigure f) { + return EMPTY; +} + +/** + * @see dwtx.draw2d.Border#isOpaque() + */ +public bool isOpaque() { + return false; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AbstractConnectionAnchor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AbstractConnectionAnchor.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AbstractConnectionAnchor; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.ConnectionAnchorBase; +import dwtx.draw2d.AncestorListener; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.AnchorListener; + +/** + * Provides support for anchors which depend on a figure for thier location. + * @author hudsonr + */ +public abstract class AbstractConnectionAnchor + : ConnectionAnchorBase + , AncestorListener +{ + +private IFigure owner; + +/** + * Constructs an AbstractConnectionAnchor with no owner. + * + * @since 2.0 + */ +public this() { } + +/** + * Constructs an AbstractConnectionAnchor with the owner supplied as input. + * + * @since 2.0 + * @param owner Owner of this anchor + */ +public this(IFigure owner) { + setOwner(owner); +} + +/** + * Adds the given listener to the listeners to be notified of anchor location changes. + * + * @since 2.0 + * @param listener Listener to be added + * @see #removeAnchorListener(AnchorListener) + */ +public void addAnchorListener(AnchorListener listener) { + if (listener is null) + return; + if (listeners.size() is 0) + getOwner().addAncestorListener(this); + super.addAnchorListener(listener); +} + +/** + * Notifies all the listeners of this anchor's location change. + * + * @since 2.0 + * @param figure Anchor-owning Figure which has moved + * @see dwtx.draw2d.AncestorListener#ancestorMoved(IFigure) + */ +public void ancestorMoved(IFigure figure) { + fireAnchorMoved(); +} + +/** + * @see dwtx.draw2d.AncestorListener#ancestorAdded(IFigure) + */ +public void ancestorAdded(IFigure ancestor) { } + +/** + * @see dwtx.draw2d.AncestorListener#ancestorRemoved(IFigure) + */ +public void ancestorRemoved(IFigure ancestor) { } + +/** + * Returns the owner Figure on which this anchor's location is dependent. + * + * @since 2.0 + * @return Owner of this anchor + * @see #setOwner(IFigure) + */ +public IFigure getOwner() { + return owner; +} + +/** + * Returns the point which is used as the reference by this AbstractConnectionAnchor. It + * is generally dependent on the Figure which is the owner of this + * AbstractConnectionAnchor. + * + * @since 2.0 + * @return The reference point of this anchor + * @see dwtx.draw2d.ConnectionAnchor#getReferencePoint() + */ +public Point getReferencePoint() { + if (getOwner() is null) + return null; + else { + Point ref_ = getOwner().getBounds().getCenter(); + getOwner().translateToAbsolute(ref_); + return ref_; + } +} + +/** + * Removes the given listener from this anchor. If all the listeners are removed, then + * this anchor removes itself from its owner. + * + * @since 2.0 + * @param listener Listener to be removed from this anchors listeners list + * @see #addAnchorListener(AnchorListener) + */ +public void removeAnchorListener(AnchorListener listener) { + super.removeAnchorListener(listener); + if (listeners.size() is 0) + getOwner().removeAncestorListener(this); +} + +/** + * Sets the owner of this anchor, on whom this anchors location is dependent. + * + * @since 2.0 + * @param owner Owner of this anchor + */ +public void setOwner(IFigure owner) { + this.owner = owner; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AbstractHintLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AbstractHintLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AbstractHintLayout; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.AbstractLayout; +import dwtx.draw2d.IFigure; + + +/** + * The foundation for layout managers which are sensitive to width and/or height hints. + * This class will cache preferred and minimum sizes for a given set of hints. If the + * hints change in a meaningful way, the cached size is thrown out and redetermined. + *

+ * Subclasses may be sensitive to one or both hints. By default, this class assumes both + * hints are important. Subclasses may override this behavior in {@link + * #isSensitiveHorizontally(IFigure)} and {@link #isSensitiveVertically(IFigure)}. At + * least one of these method should return true. + * + * @author hudsonr + * @since 2.0 + */ +public abstract class AbstractHintLayout + : AbstractLayout +{ + +private Dimension minimumSize = null; +private Dimension cachedPreferredHint; +private Dimension cachedMinimumHint; + +this(){ + cachedPreferredHint = new Dimension(-1, -1); + cachedMinimumHint = new Dimension(-1, -1); +} +/** + * Calculates the minimum size using the given width and height hints. This method is + * called from {@link #getMinimumSize(IFigure, int, int)} whenever the cached minimum size + * has been flushed. + *

+ * By default, this method just calls {@link #getPreferredSize(IFigure, int, int)}, + * meaning minimum and preferres sizes will be the same unless this method is overridden. + * + * @param container the Figure on which this layout is installed + * @param wHint the width hint + * @param hHint the height hint + * + * @return the layout's minimum size + */ +protected Dimension calculateMinimumSize(IFigure container, int wHint, int hHint) { + return getPreferredSize(container, wHint, hHint); +} + +/** + * @see dwtx.draw2d.LayoutManager#getMinimumSize(IFigure, int, int) + */ +public Dimension getMinimumSize(IFigure container, int w, int h) { + bool flush = cachedMinimumHint.width !is w + && isSensitiveHorizontally(container); + flush |= cachedMinimumHint.height !is h + && isSensitiveVertically(container); + if (flush) { + minimumSize = null; + cachedMinimumHint.width = w; + cachedMinimumHint.height = h; + } + if (minimumSize is null) + minimumSize = calculateMinimumSize(container, w, h); + return minimumSize; +} + +/** + * @see dwtx.draw2d.LayoutManager#getPreferredSize(IFigure, int, int) + */ +public final Dimension getPreferredSize(IFigure container, int w, int h) { + bool flush = cachedPreferredHint.width !is w + && isSensitiveHorizontally(container); + flush |= cachedPreferredHint.height !is h + && isSensitiveVertically(container); + if (flush) { + preferredSize = null; + cachedPreferredHint.width = w; + cachedPreferredHint.height = h; + } + return super.getPreferredSize(container, w, h); +} + +/** + * Extends the superclass implementation to flush the cached minimum size. + * @see dwtx.draw2d.LayoutManager#invalidate() + */ +public void invalidate() { + minimumSize = null; + super.invalidate(); +} + +/** + * Returns whether this layout manager is sensitive to changes in the horizontal hint. By + * default, this method returns true. + * @param container the layout's container + * @return true if this layout is sensite to horizontal hint changes + */ +protected bool isSensitiveHorizontally(IFigure container) { + return true; +} + +/** + * Returns whether this layout manager is sensitive to changes in the vertical hint. By + * default, this method returns true. + * @param container the layout's container + * @return true if this layout is sensite to vertical hint changes + */ +protected bool isSensitiveVertically(IFigure container) { + return true; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AbstractLabeledBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AbstractLabeledBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AbstractLabeledBorder; + +import dwt.dwthelper.utils; + + + +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.AbstractBorder; +import dwtx.draw2d.LabeledBorder; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.ColorConstants; +import dwtx.draw2d.FigureUtilities; + +/** + * Provides support for a border with a label describing the contents of which it is + * surrounding. + */ +public abstract class AbstractLabeledBorder + : AbstractBorder + , LabeledBorder +{ + +private Dimension textExtents; +private String label; +private Insets insets; +private Color textColor; +private Font font; + +/** + * Constructs a default AbstractLabeledBorder with the name of this class set as its + * label. + * + * @since 2.0 + */ +public this() { + textColor = ColorConstants.black; + String className = this.classinfo.name; + setLabel(className.substring(className.lastIndexOf('.') + 1, className.length)); +} + +/** + * Constructs a border with the label set to the String passed in as input. + * + * @param s Label to be set on the border + * @since 2.0 + */ +public this(String s) { + textColor = ColorConstants.black; + setLabel(s); +} + +/** + * Calculates insets based on the current font and other attributes. This value will be + * cached until {@link #invalidate()} is called. + * @param figure The figure to which the border is being applied + * @return The Insets + */ +protected abstract Insets calculateInsets(IFigure figure); + +/** + * Returns the font that this border will use. If no Font has been specified, the font + * associated with the input Figure will be used. + * @param f Figure used to get a default font + * @return The font for this border + */ +protected Font getFont(IFigure f) { + if (font is null) + return f.getFont(); + return font; +} + +/** + * Returns the insets, or space associated for this border. Returns any previously set + * value if present, else calculates it from the Figure provided in as input. + * @param fig Figure used to calculate insets + * @return The insets + */ +public Insets getInsets(IFigure fig) { + if (insets is null) + insets = calculateInsets(fig); + return insets; +} + +/** + * @see dwtx.draw2d.LabeledBorder#getLabel() + */ +public String getLabel() { + return label; +} + +/** + * @see dwtx.draw2d.Border#getPreferredSize(IFigure) + */ +public Dimension getPreferredSize(IFigure fig) { + return new Dimension(getTextExtents(fig)); +} + +/** + * Returns the text Color of this AbstractLabeledBorder's label. + * + * @return The text color + * @since 2.0 + */ +public Color getTextColor() { + return textColor; +} + +/** + * Calculates and returns the size required by this border's label. + * + * @param f IFigure on which the calculations are to be made + * @return Dimensions required by the text of this border's label + * @since 2.0 + */ +protected Dimension getTextExtents(IFigure f) { + if (textExtents is null) + textExtents = FigureUtilities.getTextExtents(label, getFont(f)); + return textExtents; +} + +/** + * Resets the internal values and state so that they can be recalculated. Called whenever + * a state change has occurred that effects the insets or text extents of this border. + */ +protected void invalidate() { + insets = null; + textExtents = null; +} + +/** + * Sets the Font of this border to the input value, and invalidates the border forcing an + * update of internal parameters of insets and text extents. + * @param font The font + */ +public void setFont(Font font) { + this.font = font; + invalidate(); +} + +/** + * @see dwtx.draw2d.LabeledBorder#setLabel(String) + */ +public void setLabel(String s) { + label = ((s is null) ? "" : s); //$NON-NLS-1$ + invalidate(); +} + +/** + * Sets the color for this border's text. + * + * @param color Color to be set for this border's text + * @since 2.0 + */ +public void setTextColor(Color color) { + textColor = color; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AbstractLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AbstractLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AbstractLayout; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.LayoutManager; + +/** + * Provides generic support for LayoutManagers. + */ +public abstract class AbstractLayout + : LayoutManager +{ + +/** + * The cached preferred size. + */ +protected Dimension preferredSize; + +/** + * Whether or not this layout pays attention to visiblity of figures when + * calculating its bounds. By default, false. + */ +protected bool isObservingVisibility_ = false; + +/** + * This method is now {@link #calculatePreferredSize(IFigure, int, int)}. + * @param container the figure + */ +protected final void calculatePreferredSize(IFigure container) { } + +/** + * Calculates the preferred size of the given figure, using width and height hints. + * @param container The figure + * @param wHint The width hint + * @param hHint The height hint + * @return The preferred size + */ +protected abstract Dimension calculatePreferredSize(IFigure container, + int wHint, int hHint); + +/** + * Returns the preferred size of the figure's border. + * @param container The figure that the border is on + * @return The border's preferred size + */ +protected Dimension getBorderPreferredSize(IFigure container) { + if (container.getBorder() is null) + return new Dimension(); + return container.getBorder().getPreferredSize(container); +} + +/** + * Returns the constraint for the given figure. + * @param child The figure + * @return The constraint + */ +public Object getConstraint(IFigure child) { + return null; +} + +/** + * This method is now {@link #getMinimumSize(IFigure, int, int)}. + * @param container the figure + */ +public final void getMinimumSize(IFigure container) { } + +/** + * @see dwtx.draw2d.LayoutManager#getMinimumSize(IFigure, int, int) + */ +public Dimension getMinimumSize(IFigure container, int wHint, int hHint) { + return getPreferredSize(container, wHint, hHint); +} + +/** + * Returns the preferred size of the given figure, using width and height hints. If the + * preferred size is cached, that size is returned. Otherwise, {@link + * #calculatePreferredSize(IFigure, int, int)} is called. + * @param container The figure + * @param wHint The width hint + * @param hHint The height hint + * @return The preferred size + */ +public Dimension getPreferredSize(IFigure container, int wHint, int hHint) { + if (preferredSize is null) + preferredSize = calculatePreferredSize(container, wHint, hHint); + return preferredSize; +} + +/** + * This method is now {@link #getPreferredSize(IFigure, int, int)}. + * @param container the figure + */ +public final void getPreferredSize(IFigure container) { } + + +/** + * @see dwtx.draw2d.LayoutManager#invalidate() + */ +public void invalidate() { + preferredSize = null; +} + +/** + * Removes any cached information about the given figure. + * @param child the child that is invalidated + */ +protected void invalidate(IFigure child) { + invalidate(); +} + +/** + * Returns whether or not this layout pays attention to visiblity when calculating its + * bounds. + * @return true if invisible figures should not contribute to this layout's bounds. + */ +public bool isObservingVisibility() { + return isObservingVisibility_; +} + +/** + * Removes the given figure from this LayoutManager's list of figures. + * @param child The figure to remove + */ +public void remove(IFigure child) { + invalidate(); +} + +/** + * Sets the constraint for the given figure. + * @param child the child + * @param constraint the child's new constraint + */ +public void setConstraint(IFigure child, Object constraint) { + invalidate(child); +} + +/** + * Sets isObservingVisibility to the given value. + * @param newValue true if visibility should be observed + */ +public void setObserveVisibility(bool newValue) { + if (isObservingVisibility_ is newValue) + return; + isObservingVisibility_ = newValue; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AbstractLocator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AbstractLocator.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AbstractLocator; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Locator; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.PositionConstants; + +/** + * Places a figure relative to a point determined by the subclass. The figure may be + * placed in some location relative to the point with a configurable amount of spacing. + */ +public abstract class AbstractLocator + : Locator +{ + +private int relativePosition = PositionConstants.CENTER; +private int gap; + +/** + * Creates a new AbstractLocator. + */ +public this() { } + +/** + * Returns the number of pixels to leave between the figure being located and the + * reference point. Only used if {@link #getRelativePosition()} returns something other + * than {@link PositionConstants#CENTER}. + * + * @return The gap + * @since 2.0 + */ +public int getGap() { + return gap; +} + +/** + * Returns the reference point in absolute coordinates used to calculate the location. + * @return The reference point in absolute coordinates + * @since 2.0 + */ +protected abstract Point getReferencePoint(); + +/** + * Recalculate the location of the figure according to its desired position relative to + * the center point. + * + * @param size The size of the figure + * @param center The center point + * @return The new bounds + * @since 2.0 + */ +protected Rectangle getNewBounds(Dimension size, Point center) { + Rectangle bounds = new Rectangle(center, size); + + bounds.x -= bounds.width / 2; + bounds.y -= bounds.height / 2; + + int xFactor = 0, yFactor = 0; + int position = getRelativePosition(); + + if ((position & PositionConstants.NORTH) !is 0) + yFactor = -1; + else if ((position & PositionConstants.SOUTH) !is 0) + yFactor = 1; + + if ((position & PositionConstants.WEST) !is 0) + xFactor = -1; + else if ((position & PositionConstants.EAST) !is 0) + xFactor = 1; + + bounds.x += xFactor * (bounds.width / 2 + getGap()); + bounds.y += yFactor * (bounds.height / 2 + getGap()); + + return bounds; +} + +/** + * Returns the position of the figure with respect to the center point. Possible values + * can be found in {@link PositionConstants} and include CENTER, NORTH, SOUTH, EAST, WEST, + * NORTH_EAST, NORTH_WEST, SOUTH_EAST, or SOUTH_WEST. + * + * @return An int constant representing the relative position + * @since 2.0 + */ +public int getRelativePosition() { + return relativePosition; +} + +/** + * Recalculates the position of the figure and returns the updated bounds. + * @param target The figure to relocate + */ +public void relocate(IFigure target) { + Dimension prefSize = target.getPreferredSize(); + Point center = getReferencePoint(); + target.translateToRelative(center); + target.setBounds(getNewBounds(prefSize, center)); +} + +/** + * Sets the gap between the reference point and the figure being placed. Only used if + * getRelativePosition() returns something other than {@link PositionConstants#CENTER}. + * + * @param i The gap + * @since 2.0 + */ +public void setGap(int i) { + gap = i; +} + +/** + * Sets the position of the figure with respect to the center point. Possible values can + * be found in {@link PositionConstants} and include CENTER, NORTH, SOUTH, EAST, WEST, + * NORTH_EAST, NORTH_WEST, SOUTH_EAST, or SOUTH_WEST. + * + * @param pos The relative position + * @since 2.0 + */ +public void setRelativePosition(int pos) { + relativePosition = pos; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AbstractRouter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AbstractRouter.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AbstractRouter; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.Connection; +import dwtx.draw2d.ConnectionRouter; + +/** + * Base class for implementing a connection router. This class provides stubs for + * constraint usage, and some utility methods. + */ +public abstract class AbstractRouter + : ConnectionRouter +{ + +private static const Point START; +private static const Point END; + +static this(){ + START = new Point(); + END = new Point(); +} + +/** + * Returns the constraint for the given Connection. + * + * @param connection The connection + * @return The constraint + * @since 2.0 + */ +public Object getConstraint(Connection connection) { + return null; +} + +/** + * A convenience method for obtaining a connection's endpoint. The connection's endpoint + * is a point in absolute coordinates obtained by using its source and target {@link + * ConnectionAnchor}. The returned Point is a static singleton that is reused to reduce + * garbage collection. The caller may modify this point in any way. However, the point + * will be reused and its values overwritten during the next call to this method. + * + * @param connection The connection + * @return The endpoint + * @since 2.0 + */ +protected Point getEndPoint(Connection connection) { + Point ref_ = connection.getSourceAnchor().getReferencePoint(); + return END.setLocation(connection.getTargetAnchor().getLocation(ref_)); +} + +/** + * A convenience method for obtaining a connection's start point. The connection's + * startpoint is a point in absolute coordinates obtained by using its source and target + * {@link ConnectionAnchor}. The returned Point is a static singleton that is reused to + * reduce garbage collection. The caller may modify this point in any way. However, the + * point will be reused and its values overwritten during the next call to this method. + * + * @param conn The connection + * @return The start point + * @since 2.0 + */ +protected Point getStartPoint(Connection conn) { + Point ref_ = conn.getTargetAnchor().getReferencePoint(); + return START.setLocation(conn.getSourceAnchor().getLocation(ref_)); +} + +/** + * Causes the router to discard any cached information about the given Connection. + * + * @param connection The connection to invalidate + * @since 2.0 + */ +public void invalidate(Connection connection) { } + +/** + * Removes the given Connection from this routers list of Connections it is responsible + * for. + * + * @param connection The connection to remove + * @since 2.0 + */ +public void remove(Connection connection) { } + +/** + * Sets the constraint for the given Connection. + * + * @param connection The connection + * @param constraint The constraint + * @since 2.0 + */ +public void setConstraint(Connection connection, Object constraint) { } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AccessibleBase.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AccessibleBase.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AccessibleBase; + +import dwt.dwthelper.utils; + +/** + * The base class for accessible objects which provides accesibilty clients with a unique + * ID. + */ +public class AccessibleBase { + +/** + * Returns the id of this accessible object using {@link Object#toHash()}. + * @return the id + */ +public final int getAccessibleID() { + /* This assumes that the native implementation of toHash in Object is to + * return the pointer to the Object, which should be U-unique. + */ + int value = super.toHash(); + /* + * Values -3, -2, and -1 are reserved by DWT's ACC class to have special meaning. + * Therefore, a child cannot have this value. + */ + if (value < 0) + value -= 4; + return value; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ActionEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ActionEvent.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ActionEvent; + +import dwt.dwthelper.utils; + +/** + * An event that occurs as a result of an action being performed. + */ +public class ActionEvent + : /+java.util.+/EventObject +{ + +private String actionName; + +/** + * Constructs a new ActionEvent with source as the source of the event. + * + * @param source The source of the event + */ +public this(Object source) { + super(source); +} + +/** + * Constructs a new ActionEvent with source as the source of the event and + * name as the name of the action that was performed. + * + * @param source The source of the event + * @param name The name of the action + */ +public this(Object source, String name) { + super(source); + actionName = name; +} + +/** + * Returns the name of the action that was performed. + * + * @return String The name of the action + */ +public String getActionName() { + return actionName; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ActionListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ActionListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ActionListener; + +import dwt.dwthelper.utils; +import tango.core.Traits; +import tango.core.Tuple; + +import dwtx.draw2d.ActionEvent; + +/** + * A Listener interface for receiving {@link ActionEvent ActionEvents}. + */ +public interface ActionListener { + +/** + * Called when the action occurs. + * @param event The event + */ +void actionPerformed(ActionEvent event); + +} +// DWT Helper +private class _DgActionListenerT(Dg,T...) : ActionListener { + + alias ParameterTupleOf!(Dg) DgArgs; + static assert( is(DgArgs == Tuple!(ActionEvent,T)), + "Delegate args not correct" ); + + Dg dg; + T t; + + private this( Dg dg, T t ){ + this.dg = dg; + static if( T.length > 0 ){ + this.t = t; + } + } + + void actionPerformed( ActionEvent e ){ + dg(e,t); + } +} + +ActionListener dgActionListener( Dg, T... )( Dg dg, T args ){ + return new _DgActionListenerT!( Dg, T )( dg, args ); +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AncestorHelper.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AncestorHelper.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AncestorHelper; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Bean; + +import dwtx.draw2d.IFigure; +import dwtx.draw2d.FigureListener; +import dwtx.draw2d.AncestorListener; + +/** + * A helper object which tracks the parent chain hierarchy. + * @since 2.1 + */ +class AncestorHelper + : PropertyChangeListener, FigureListener +{ + +/** + * The base figure whose ancestor chain is being observed. + */ +protected final IFigure base; +/** + * The array of ancestor listeners. + */ +protected AncestorListener[] listeners; + +/** + * Constructs a new helper on the given base figure and starts listening to figure and + * property changes on the base figure's parent chain. When no longer needed, the helper + * should be disposed. + * @since 2.1 + * @param baseFigure + */ +public this(IFigure baseFigure) { + this.base = baseFigure; + addAncestors(baseFigure); +} + +/** + * Appends a new listener to the list of listeners. + * @param listener the listener + */ +public void addAncestorListener(AncestorListener listener) { + if (listeners is null) { + listeners = new AncestorListener[1]; + listeners[0] = listener; + } else { + int oldSize = listeners.length; + AncestorListener newListeners[] = new AncestorListener[oldSize + 1]; + SimpleType!(AncestorListener).arraycopy(listeners, 0, newListeners, 0, oldSize); + newListeners[oldSize] = listener; + listeners = newListeners; + } +} + +/** + * Hooks up internal listeners used for maintaining the proper figure listeners. + * @param rootFigure the root figure + */ +protected void addAncestors(IFigure rootFigure) { + for (IFigure ancestor = rootFigure; + ancestor !is null; + ancestor = ancestor.getParent()) { + ancestor.addFigureListener(this); + ancestor.addPropertyChangeListener("parent", this); //$NON-NLS-1$ + } +} + +/** + * Removes all internal listeners. + */ +public void dispose() { + removeAncestors(base); + listeners = null; +} + +/** + * @see dwtx.draw2d.FigureListener#figureMoved(dwtx.draw2d.IFigure) + */ +public void figureMoved(IFigure ancestor) { + fireAncestorMoved(ancestor); +} + +/** + * Fires notification to the listener list + * @param ancestor the figure which moved + */ +protected void fireAncestorMoved(IFigure ancestor) { + if (listeners is null) + return; + for (int i = 0; i < listeners.length; i++) + listeners[i].ancestorMoved(ancestor); +} + +/** + * Fires notification to the listener list + * @param ancestor the figure which moved + */ +protected void fireAncestorAdded(IFigure ancestor) { + if (listeners is null) + return; + for (int i = 0; i < listeners.length; i++) + listeners[i].ancestorAdded(ancestor); +} + +/** + * Fires notification to the listener list + * @param ancestor the figure which moved + */ +protected void fireAncestorRemoved(IFigure ancestor) { + if (listeners is null) + return; + for (int i = 0; i < listeners.length; i++) + listeners[i].ancestorRemoved(ancestor); +} + +/** + * Returns the total number of listeners. + * @return the number of listeners + */ +public bool isEmpty() { + return listeners is null; +} + +/** + * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) + */ +public void propertyChange(PropertyChangeEvent event) { + if (event.getPropertyName().equals("parent")) { //$NON-NLS-1$ + IFigure oldParent = cast(IFigure)event.getOldValue(); + IFigure newParent = cast(IFigure)event.getNewValue(); + if (oldParent !is null) { + removeAncestors(oldParent); + fireAncestorRemoved(oldParent); + } + if (newParent !is null) { + addAncestors(newParent); + fireAncestorAdded(newParent); + } + } +} + +/** + * Removes the first occurence of the given listener + * @param listener the listener to remove + */ +public void removeAncestorListener(AncestorListener listener) { + if (listeners is null) + return; + for (int index = 0; index < listeners.length; index++) + if (listeners[index] is listener) { + int newSize = listeners.length - 1; + AncestorListener newListeners[] = null; + if (newSize !is 0) { + newListeners = new AncestorListener[newSize]; + SimpleType!(AncestorListener).arraycopy(listeners, 0, newListeners, 0, index); + SimpleType!(AncestorListener).arraycopy(listeners, index + 1, newListeners, index, newSize - index); + } + listeners = newListeners; + return; + } +} + +/** + * Unhooks listeners starting at the given figure + * @param rootFigure + */ +protected void removeAncestors(IFigure rootFigure) { + for (IFigure ancestor = rootFigure; + ancestor !is null; + ancestor = ancestor.getParent()) { + ancestor.removeFigureListener(this); + ancestor.removePropertyChangeListener("parent", this); //$NON-NLS-1$ + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AncestorListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AncestorListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AncestorListener; + +import dwt.dwthelper.utils; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.ConnectionAnchor; + +/** + * Classes which implement this interface provide methods to respond to changes in the + * ancestor properties of a Figure. + *

+ * Instances of this class can be added as listeners to a figure using the + * addAncestorListener method and removed using the + * removeAncestoreListener method. When the parent chain of the figure being + * observed changes or moves, the listener will be notified appropriately. + */ +public interface AncestorListener { + +/** + * Called when an ancestor has been added into the listening figure's hierarchy. + * @param ancestor The ancestor that was added + */ +void ancestorAdded(IFigure ancestor); + +/** + * Called when an ancestor has moved to a new location. + * @param ancestor The ancestor that has moved + */ +void ancestorMoved(IFigure ancestor); + +/** + * Called when an ancestor has been removed from the listening figure's hierarchy. + * @param ancestor The ancestor that has been removed + */ +void ancestorRemoved(IFigure ancestor); + +/** + * An empty implementation of AncestorListener for convenience. + */ +class Stub : AncestorListener { + public void ancestorMoved(IFigure ancestor) { } + public void ancestorAdded(IFigure ancestor) { } + public void ancestorRemoved(IFigure ancestor) { } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AnchorListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AnchorListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AnchorListener; + +import dwt.dwthelper.utils; +import dwtx.draw2d.ConnectionAnchor; + +/** + * Classes which implement this interface provide a method to notify that an anchor has + * moved. + *

+ * Instances of this class can be added as listeners of an Anchor using the + * addAnchorListener method and removed using the + * removeAnchorListener method. + */ +public interface AnchorListener { + +/** + * Called when an anchor has moved to a new location. + * @param anchor The anchor that has moved. + */ +void anchorMoved(ConnectionAnchor anchor); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Animation.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Animation.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,278 @@ +/******************************************************************************* + * Copyright (c) 2005, 2006 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 + *******************************************************************************/ + +module dwtx.draw2d.Animation; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.Animator; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.UpdateManager; + + +/** + * A utility for coordinating figure animations. During animation, multiple + * animators are employed to capture the initial and final states + * for one or more figures. The animators then playback the animation by interpolating the + * intermediate states for each figure using the initial and final "keyframes". + *

+ * An animator is usually stateless and represents an specific technique. Any state + * information is stored by the Animation utility. Therefore, one instance can be used + * with multiple figures. Animators hook into the validation mechanism for figures and + * connections. These hooks are used to capture the states, and to intercept the typical + * layout process to insert the interpolated state. + *

+ * To indicate that animation is desired, clients must call {@link #markBegin()} prior to + * invalidating any figures that are to be included in the animation. After this method is + * called, changes are made, and {@link #run()} is invoked. The run method will force a + * validation pass to capture the final states, and then commence the animation. The + * animation is synchronous and the method does not return until the animation has + * completed. + * @see LayoutAnimator + * @since 3.2 + */ +public class Animation { + +static class AnimPair { + + const Animator animator; + const IFigure figure; + + this(Animator animator, IFigure figure) { + this.animator = animator; + this.figure = figure; + } + + public override int opEquals(Object obj) { + AnimPair pair = cast(AnimPair)obj; + return pair.animator is animator && pair.figure is figure; + } + + public override hash_t toHash() { + return (cast(Object)animator).toHash() ^ (cast(Object)figure).toHash(); + } +} +private static const int DEFAULT_DELAY = 250; +private static Set figureAnimators; +private static Map finalStates; + +private static Map initialStates; +private static const int PLAYBACK = 3; +private static float progress; +private static const int RECORD_FINAL = 2; + +private static const int RECORD_INITIAL = 1; +private static long startTime; +private static int state; +private static Set toCapture; + +private static UpdateManager updateManager; + +private static void capture() { + Iterator keys = figureAnimators.iterator(); + while (keys.hasNext()) { + AnimPair pair = cast(AnimPair) keys.next(); + if (toCapture.contains(pair)) + pair.animator.capture(pair.figure); + else + keys.remove(); + } +} + +static void cleanup() { + if (figureAnimators !is null) { + Iterator keys = figureAnimators.iterator(); + while (keys.hasNext()) { + AnimPair pair = cast(AnimPair) keys.next(); + pair.animator.tearDown(pair.figure); + } + } + + state = 0; + step(); + //Allow layout to occur normally + //updateManager.performUpdate(); + + initialStates = null; + finalStates = null; + figureAnimators = null; + updateManager = null; + toCapture = null; + state = 0; +} + +private static void doRun(int duration) { + state = RECORD_FINAL; + findUpdateManager(); + updateManager.performValidation(); + capture(); + state = PLAYBACK; + progress = 0.1f; + startTime = System.currentTimeMillis(); + + notifyPlaybackStarting(); + + while (progress !is 0) { + step(); + updateManager.performUpdate(); + if (progress is 1.0) + progress = 0; + else { + int delta = cast(int)(System.currentTimeMillis() - startTime); + if (delta >= duration) + progress = 1f; + else + progress = 0.1f + 0.9f * delta / duration; + } + } +} + +private static void findUpdateManager() { + AnimPair pair = cast(AnimPair) figureAnimators.iterator().next(); + updateManager = pair.figure.getUpdateManager(); +} + +/** + * Returns the final animation state for the given figure. + * @param animator the animator for the figure + * @param figure the figure being animated + * @return the final state + * @since 3.2 + */ +public static Object getFinalState(Animator animator, IFigure figure) { + return finalStates.get(new AnimPair(animator, figure)); +} + +/** + * Returns the initial animation state for the given animator and figure. If no state was + * recorded, null is returned. + * @param animator the animator for the figure + * @param figure the figure being animated + * @return the initial state + * @since 3.2 + */ +public static Object getInitialState(Animator animator, IFigure figure) { + return initialStates.get(new AnimPair(animator, figure)); +} + +/** + * Returns the animation progress, where 0.0 < progress ≤ 1.0. + * @return the progress of the animation + * @since 3.2 + */ +public static float getProgress() { + return progress; +} + +static void hookAnimator(IFigure figure, Animator animator) { + AnimPair pair = new AnimPair(animator, figure); + if (figureAnimators.add(pair)) + animator.init(figure); +} + +static void hookNeedsCapture(IFigure figure, Animator animator) { + AnimPair pair = new AnimPair(animator, figure); + if (figureAnimators.contains(pair)) + toCapture.add(pair); +} + +static bool hookPlayback(IFigure figure, Animator animator) { + if (toCapture.contains(new AnimPair(animator, figure))) + return animator.playback_package(figure); + return false; +} + +/** + * Returns true if animation is in progress. + * @return true when animating + * @since 3.2 + */ +public static bool isAnimating() { + return state is PLAYBACK; +} + +static bool isFinalRecording() { + return state is RECORD_FINAL; +} + +static bool isInitialRecording() { + return state is RECORD_INITIAL; +} + +/** + * Marks the beginning of the animation process. If the beginning has already been marked, + * this has no effect. + * @return returns true if beginning was not previously marked + * @since 3.2 + */ +public static bool markBegin() { + if (state is 0) { + state = RECORD_INITIAL; + initialStates = new HashMap(); + finalStates = new HashMap(); + figureAnimators = new HashSet(); + toCapture = new HashSet(); + return true; + } + return false; +} + +private static void notifyPlaybackStarting() { + Iterator keys = figureAnimators.iterator(); + while (keys.hasNext()) { + AnimPair pair = cast(AnimPair) keys.next(); + pair.animator.playbackStarting(pair.figure); + } +} + +static void putFinalState(Animator animator, IFigure key, Object state) { + finalStates.put(new AnimPair(animator, key), state); +} + +static void putInitialState(Animator animator, IFigure key, Object state) { + initialStates.put(new AnimPair(animator, key), state); +} + +/** + * Runs animation using the recommended duration: 250 milliseconds. + * @see #run(int) + * @since 3.2 + */ +public static void run() { + run(DEFAULT_DELAY); +} + +/** + * Captures the final states for the animation and then plays the animation. + * @param duration the length of animation in milliseconds + * @since 3.2 + */ +public static void run(int duration) { + if (state is 0) + return; + try { + if (!figureAnimators.isEmpty()) + doRun(duration); + } finally { + cleanup(); + } +} + +private static void step() { + Iterator iter = initialStates.keySet().iterator(); + while (iter.hasNext()) + (cast(AnimPair)iter.next()).figure.revalidate(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Animator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Animator.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.Animator; + +import dwt.dwthelper.utils; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Animation; + +/** + * Animates some aspect of a figure. Each animator will capture some of the effects of + * validation of the figures. + *

+ * Animators must be hooked to figure in special ways. Refer to each implementation for + * the specific requirements. Animators are generally stateless, which allows them to be + * shared and prevents them from leaking memory. + * + * @since 3.2 + */ +public abstract class Animator { + +this() { } + +/** + * Captures the final state of the given figure. This method is called once after the + * update manager has completed validation of all invalid figures. + * @param figure the container + * @since 3.2 + */ +public void capture(IFigure figure) { + recordFinalState(figure); +} + +/** + * Returns an object encapsulating the current state of the figure. This method is called + * to capture both the initial and final states. + * @param figure the figure + * @return the current state + * @since 3.2 + */ +protected abstract Object getCurrentState(IFigure figure); + +/** + * Plays back the animation for the given figure and returns true if + * successful. This method does nothing by default and return false. + * @param figure the figure being animated + * @return true if playback was successful + * @since 3.2 + */ +protected bool playback(IFigure figure) { + return false; +} +package bool playback_package(IFigure figure) { + return playback(figure); +} + +/** + * Sent as playback is starting for a given figure. + * @param figure the figure + * @since 3.2 + */ +public void playbackStarting(IFigure figure) { } + +/** + * Records the final state information for a figure. + * @param figure the figure + * @since 3.2 + */ +protected void recordFinalState(IFigure figure) { + Animation.putFinalState(this, figure, getCurrentState(figure)); +} + +/** + * Records initial state information for the given figure. + * @param figure the container. + * @since 3.2 + */ +protected void recordInitialState(IFigure figure) { + Animation.putInitialState(this, figure, getCurrentState(figure)); +} + +/** + * Sets up the animator for the given figure to be animated. This method is called exactly + * once time prior to any layouts happening. The animator can capture the figure's current + * state, and set any animation-time settings for the figure. Changes made to the figure + * should be reverted in {@link #tearDown(IFigure)}. + * @param figure the animated figure + * @since 3.2 + */ +public void init(IFigure figure) { + recordInitialState(figure); +} + +/** + * Reverts any temporary changes made to the figure during animation. This method is + * called exactly once after all animation has been completed. Subclasses should extend + * this method to revert any changes. + * @param figure the animated figure + * @since 3.2 + * @see #init(IFigure) + */ +public void tearDown(IFigure figure) { } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ArrowButton.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ArrowButton.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ArrowButton; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.Button; +import dwtx.draw2d.Orientable; +import dwtx.draw2d.Triangle; +import dwtx.draw2d.MarginBorder; +import dwtx.draw2d.ColorConstants; + +/** + * A Button which displays a triangle pointing in a specified direction. This class is + * used by the {@link ScrollBar} figure. + */ +public class ArrowButton + : Button + , Orientable +{ + +/** + * Constructs a default ArrowButton with the arrow pointing north. + * + * @since 2.0 + */ +public this() { + createTriangle(); + setRequestFocusEnabled(false); + setFocusTraversable(false); +} + +/** + * Constructs an ArrowButton with the arrow having the direction given in the input. + * The direction can be one of many directional constants defined in + * {@link PositionConstants}. + * + * @param direction Direction of the arrow + * @since 2.0 + */ +public this(int direction) { + this(); + setDirection(direction); +} + +/** + * Contructs a triangle with a black background pointing north, and sets it as the + * contents of the button. + * + * @since 2.0 + */ +protected void createTriangle() { + Triangle tri = new Triangle(); + tri.setOutline(true); + tri.setBackgroundColor(ColorConstants.listForeground); + tri.setForegroundColor(ColorConstants.listForeground); + tri.setBorder(new MarginBorder(new Insets(2))); + setContents(tri); +} + +/** + * @see dwtx.draw2d.Orientable#setDirection(int) + */ +public void setDirection(int value) { + setChildrenDirection(value); +} + +/** + * @see dwtx.draw2d.Orientable#setOrientation(int) + */ +public void setOrientation(int value) { + setChildrenOrientation(value); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ArrowLocator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ArrowLocator.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ArrowLocator; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.ConnectionLocator; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Connection; +import dwtx.draw2d.RotatableDecoration; + +/** + * Locator used to place a {@link RotatableDecoration} on a {@link Connection}. The + * decoration can be placed at the source or target end of the connection figure. The + * default connection implementation uses a {@link DelegatingLayout} which requires + * locators. + */ +public class ArrowLocator : ConnectionLocator { + +/** + * Constructs an ArrowLocator associated with passed connection and tip location (either + * {@link ConnectionLocator#SOURCE} or {@link ConnectionLocator#TARGET}). + * + * @param connection The connection associated with the locator + * @param location Location of the arrow decoration + * @since 2.0 + */ +public this(Connection connection, int location) { + super(connection, location); +} + +/** + * Relocates the passed in figure (which must be a {@link RotatableDecoration}) at either + * the start or end of the connection. + * @param target The RotatableDecoration to relocate + */ +public void relocate(IFigure target) { + PointList points = getConnection().getPoints(); + RotatableDecoration arrow = cast(RotatableDecoration)target; + arrow.setLocation(getLocation(points)); + + if (getAlignment() is SOURCE) + arrow.setReferencePoint(points.getPoint(1)); + else if (getAlignment() is TARGET) + arrow.setReferencePoint(points.getPoint(points.size() - 2)); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/AutomaticRouter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/AutomaticRouter.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.AutomaticRouter; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.internal.MultiValueMap; +import dwtx.draw2d.AbstractRouter; +import dwtx.draw2d.ConnectionRouter; +import dwtx.draw2d.ConnectionAnchor; +import dwtx.draw2d.Connection; + +/** + * An abstract router implementation which detects when multiple connections are + * overlapping. Two connections overlap if the combination of source and target + * anchors are equal. Subclasses must implement {@link #handleCollision(PointList, int)} + * to determine how to avoid the overlap. + *

+ * This router can delegate to another connection router. The wrappered router will route + * the connections first, after which overlapping will be determined. + */ +public abstract class AutomaticRouter + : AbstractRouter +{ + +private ConnectionRouter nextRouter; +private MultiValueMap connections; + +public this(){ + connections = new MultiValueMap(); +} + +private class HashKey { + + private ConnectionAnchor anchor1, anchor2; + + this(Connection conn) { + anchor1 = conn.getSourceAnchor(); + anchor2 = conn.getTargetAnchor(); + } + + public override int opEquals(Object object) { + bool isEqual = false; + HashKey hashKey; + + if (auto hashKey = cast(HashKey)object ) { + ConnectionAnchor hkA1 = hashKey.getFirstAnchor(); + ConnectionAnchor hkA2 = hashKey.getSecondAnchor(); + + isEqual = ((cast(Object)hkA1).opEquals(cast(Object)anchor1) && (cast(Object)hkA2).opEquals(cast(Object)anchor2)) + || ((cast(Object)hkA1).opEquals(cast(Object)anchor2) && (cast(Object)hkA2).opEquals(cast(Object)anchor1)); + } + return isEqual; + } + + public ConnectionAnchor getFirstAnchor() { + return anchor1; + } + + public ConnectionAnchor getSecondAnchor() { + return anchor2; + } + + public override hash_t toHash() { + return (cast(Object)anchor1).toHash() ^ (cast(Object)anchor2).toHash(); + } +} + +/** + * @see dwtx.draw2d.ConnectionRouter#getConstraint(Connection) + */ +public Object getConstraint(Connection connection) { + if (next() !is null) + return next().getConstraint(connection); + return null; +} + +/** + * Handles collisions between 2 or more Connections. Collisions are currently defined as 2 + * connections with no bendpoints and whose start and end points coincide. In other + * words, the 2 connections are the exact same line. + * + * @param list The PointList of a connection that collides with another connection + * @param index The index of the current connection in the list of colliding connections + */ +protected abstract void handleCollision(PointList list, int index); + +/** + * @see dwtx.draw2d.ConnectionRouter#invalidate(Connection) + */ +public void invalidate(Connection conn) { + if (next() !is null) + next().invalidate(conn); + if (conn.getSourceAnchor() is null || conn.getTargetAnchor() is null) + return; + HashKey connectionKey = new HashKey(conn); + ArrayList connectionList = connections.get(connectionKey); + int affected = connections.remove(connectionKey, cast(Object)conn); + if (affected !is -1) { + for (int i = affected; i < connectionList.size(); i++) + (cast(Connection)connectionList.get(i)).revalidate(); + } else + connections.removeValue(cast(Object)conn); + +} + +/** + * Returns the next router in the chain. + * @return The next router + * @since 2.0 + */ +protected ConnectionRouter next() { + return nextRouter; +} + + + +/** + * @see dwtx.draw2d.ConnectionRouter#remove(Connection) + */ +public void remove(Connection conn) { + if (conn.getSourceAnchor() is null || conn.getTargetAnchor() is null) + return; + HashKey connectionKey = new HashKey(conn); + ArrayList connectionList = connections.get(connectionKey); + if (connectionList !is null) { + int index = connections.remove(connectionKey,cast(Object) conn); + for (int i = index + 1; i < connectionList.size(); i++) + (cast(Connection)connectionList.get(i)).revalidate(); + } + if (next() !is null) + next().remove(conn); +} + +/** + * Routes the given connection. Calls the 'next' router first (if one exists) and if no + * bendpoints were added by the next router, collisions are dealt with by calling + * {@link #handleCollision(PointList, int)}. + * @param conn The connection to route + */ +public void route(Connection conn) { + if (next() !is null) + next().route(conn); + else { + conn.getPoints().removeAllPoints(); + setEndPoints(conn); + } + + if (conn.getPoints().size() is 2) { + PointList points = conn.getPoints(); + HashKey connectionKey = new HashKey(conn); + ArrayList connectionList = connections.get(connectionKey); + + if (connectionList !is null) { + + int index; + + if (connectionList.contains(cast(Object)conn)) { + index = connectionList.indexOf(cast(Object)conn) + 1; + } else { + index = connectionList.size() + 1; + connections.put(connectionKey, cast(Object)conn); + } + + handleCollision(points, index); + conn.setPoints(points); + } else { + connections.put(connectionKey, cast(Object)conn); + } + } +} + +/** + * An AutomaticRouter needs no constraints for the connections it routes. This method + * invalidates the connections and calls {@link #setConstraint(Connection, Object)} on the + * {@link #next()} router. + * @see dwtx.draw2d.ConnectionRouter#setConstraint(Connection, Object) + */ +public void setConstraint(Connection connection, Object constraint) { + invalidate(connection); + if (next() !is null) + next().setConstraint(connection, constraint); +} + +/** + * Sets the start and end points for the given connection. + * @param conn The connection + */ +protected void setEndPoints(Connection conn) { + PointList points = conn.getPoints(); + points.removeAllPoints(); + Point start = getStartPoint(conn); + Point end = getEndPoint(conn); + conn.translateToRelative(start); + conn.translateToRelative(end); + points.addPoint(start); + points.addPoint(end); + conn.setPoints(points); +} + +/** + * Sets the next router. + * @param router The ConnectionRouter + * @since 2.0 + */ +public void setNextRouter(ConnectionRouter router) { + nextRouter = router; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Bendpoint.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Bendpoint.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Bendpoint; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; + +/** + * A helper used to calculate the point at which a {@link dwtx.draw2d.Connection} + * should bend. A bendpoint returns a point relative to the connection figure on + * which it is being used. This was chosen so that fixed bendpoints would be easy to + * implement. A fixed bendpoint will have a fixed x and y value. Although the absolute x + * and y location change during zoom and scrolling, the relative values stay the same. + */ +public interface Bendpoint { + +/** + * Returns the location of the bendpoint relative to the connection. The returned + * value may be by reference. The caller should NOT modify the returned value. + * + * @return the location of the bendpoint relative to the Connection + */ +Point getLocation(); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/BendpointConnectionRouter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/BendpointConnectionRouter.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.BendpointConnectionRouter; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.PrecisionPoint; +import dwtx.draw2d.AbstractRouter; +import dwtx.draw2d.Connection; +import dwtx.draw2d.Bendpoint; + +/** + * Routes {@link Connection}s through a List of {@link Bendpoint Bendpoints}. + */ +public class BendpointConnectionRouter + : AbstractRouter +{ + +private Map constraints; +private static const PrecisionPoint A_POINT; + +static this(){ + A_POINT = new PrecisionPoint(); +} + +public this(){ + constraints = new HashMap(11); +} + +/** + * Gets the constraint for the given {@link Connection}. + * + * @param connection The connection whose constraint we are retrieving + * @return The constraint + */ +public Object getConstraint(Connection connection) { + return constraints.get(cast(Object)connection); +} + +/** + * Removes the given connection from the map of constraints. + * + * @param connection The connection to remove + */ +public void remove(Connection connection) { + constraints.remove(cast(Object)connection); +} + +/** + * Routes the {@link Connection}. Expects the constraint to be a List + * of {@link dwtx.draw2d.Bendpoint Bendpoints}. + * + * @param conn The connection to route + */ +public void route(Connection conn) { + PointList points = conn.getPoints(); + points.removeAllPoints(); + + List bendpoints = cast(List)getConstraint(conn); + if (bendpoints is null) + bendpoints = Collections.EMPTY_LIST; + + Point ref1, ref2; + + if (bendpoints.isEmpty()) { + ref1 = conn.getTargetAnchor().getReferencePoint(); + ref2 = conn.getSourceAnchor().getReferencePoint(); + } else { + ref1 = new Point((cast(Bendpoint)bendpoints.get(0)).getLocation()); + conn.translateToAbsolute(ref1); + ref2 = new Point((cast(Bendpoint)bendpoints.get(bendpoints.size() - 1)).getLocation()); + conn.translateToAbsolute(ref2); + } + + A_POINT.setLocation(conn.getSourceAnchor().getLocation(ref1)); + conn.translateToRelative(A_POINT); + points.addPoint(A_POINT); + + for (int i = 0; i < bendpoints.size(); i++) { + Bendpoint bp = cast(Bendpoint)bendpoints.get(i); + points.addPoint(bp.getLocation()); + } + + A_POINT.setLocation(conn.getTargetAnchor().getLocation(ref2)); + conn.translateToRelative(A_POINT); + points.addPoint(A_POINT); + conn.setPoints(points); +} + +/** + * Sets the constraint for the given {@link Connection}. + * + * @param connection The connection whose constraint we are setting + * @param constraint The constraint + */ +public void setConstraint(Connection connection, Object constraint) { + constraints.put(cast(Object)connection, constraint); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/BendpointLocator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/BendpointLocator.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.BendpointLocator; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.ConnectionLocator; +import dwtx.draw2d.Connection; + +/** + * Places a figure relative to a specified bend in a {@link Connection}. A bendpoint is + * one of the points returned in the connection's {@link Connection#getPoints()} method. + * It is not related to the bendpoint class used as routing constraints. + */ +public class BendpointLocator + : ConnectionLocator +{ + +private int index; + +/** + * Creates a BendpointLocator associated with passed Connection c and index i. + * + * @param c Connection associated with BendpointLocator + * @param i Index of bendpoint, represents the position of the bendpoint on Connection c + * @since 2.0 + */ +public this(Connection c, int i) { + super(c); + index = i; +} + +/** + * Returns the index of this BendpointLocator. This index is the position of the reference + * point in this BendpointLocator's {@link Connection}. + * + * @return The index + * @since 2.0 + */ +protected int getIndex() { + return index; +} + +/** + * Returns reference point associated with the BendpointLocator. This Point is taken from + * the BendpointLocator's connection and is point number 'index'. + * + * @return The reference point + * @since 2.0 + */ +protected Point getReferencePoint() { + Point p = getConnection().getPoints().getPoint(Point.SINGLETON, getIndex()); + getConnection().translateToAbsolute(p); + return p; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Border.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Border.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Border; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; + +/** + * A decoration on a Figure. A border may paint itself within the bounds of a figure, and + * it may provide Insets which can affect how the figures children are posiiton and + * painted. + *

+ * A border instance may be used with multiple figure instances. + */ +public interface Border { + +/** + * Returns the Insets for this Border for the given Figure. + * @param figure The figure this border belongs to + * @return The insets + */ +Insets getInsets(IFigure figure); + +/** + * Returns the preferred width and height that this border would like to display itself + * properly. + * @param figure The figure + * @return The preferred size + */ +Dimension getPreferredSize(IFigure figure); + +/** + * Returns true if the Border completely fills the region defined in + * {@link #paint(IFigure, Graphics, Insets)}. + * @return true if this border is opaque + */ +bool isOpaque(); + +/** + * Paints the border. The border should paint inside figure's {@link IFigure#getBounds()}, + * inset by the parameter insets. The border generally should not paint inside its + * own insets. More specifically, Border b should paint inside the rectangle: + * figure.getBounds().getCropped(insets) and outside of the rectangle: + * figure.getBounds().getCropped(insets).getCropped(getInsets()) where inside is + * defined as {@link Rectangle#contains(int, int)}. + * + * @param figure The figure this border belongs to + * @param graphics The graphics object used for painting + * @param insets The insets + */ +void paint(IFigure figure, Graphics graphics, Insets insets); + + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/BorderLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/BorderLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,327 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.BorderLayout; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.AbstractHintLayout; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.PositionConstants; + +/** + * @author Pratik Shah + */ +public class BorderLayout + : AbstractHintLayout +{ + +/** + * Constant to be used as a constraint for child figures + */ +public static const Integer CENTER; +/** + * Constant to be used as a constraint for child figures + */ +public static const Integer TOP; +/** + * Constant to be used as a constraint for child figures + */ +public static const Integer BOTTOM; +/** + * Constant to be used as a constraint for child figures + */ +public static const Integer LEFT; +/** + * Constant to be used as a constraint for child figures + */ +public static const Integer RIGHT; + +static this(){ + CENTER = new Integer(PositionConstants.CENTER); + TOP = new Integer(PositionConstants.TOP); + BOTTOM = new Integer(PositionConstants.BOTTOM); + LEFT = new Integer(PositionConstants.LEFT); + RIGHT = new Integer(PositionConstants.RIGHT); +} + +private IFigure center, left, top, bottom, right; +private int vGap = 0, hGap = 0; + +/** + * @see dwtx.draw2d.AbstractHintLayout#calculateMinimumSize(IFigure, int, int) + */ +protected Dimension calculateMinimumSize(IFigure container, int wHint, int hHint) { + int minWHint = 0, minHHint = 0; + if (wHint < 0) { + minWHint = -1; + } + if (hHint < 0) { + minHHint = -1; + } + Insets border = container.getInsets(); + wHint = Math.max(minWHint, wHint - border.getWidth()); + hHint = Math.max(minHHint, hHint - border.getHeight()); + Dimension minSize = new Dimension(); + int middleRowWidth = 0, middleRowHeight = 0; + int rows = 0, columns = 0; + + if (top !is null && top.isVisible()) { + Dimension childSize = top.getMinimumSize(wHint, hHint); + hHint = Math.max(minHHint, hHint - (childSize.height + vGap)); + minSize.setSize(childSize); + rows += 1; + } + if (bottom !is null && bottom.isVisible()) { + Dimension childSize = bottom.getMinimumSize(wHint, hHint); + hHint = Math.max(minHHint, hHint - (childSize.height + vGap)); + minSize.width = Math.max(minSize.width, childSize.width); + minSize.height += childSize.height; + rows += 1; + } + if (left !is null && left.isVisible()) { + Dimension childSize = left.getMinimumSize(wHint, hHint); + middleRowWidth = childSize.width; + middleRowHeight = childSize.height; + wHint = Math.max(minWHint, wHint - (childSize.width + hGap)); + columns += 1; + } + if (right !is null && right.isVisible()) { + Dimension childSize = right.getMinimumSize(wHint, hHint); + middleRowWidth += childSize.width; + middleRowHeight = Math.max(childSize.height, middleRowHeight); + wHint = Math.max(minWHint, wHint - (childSize.width + hGap)); + columns += 1; + } + if (center !is null && center.isVisible()) { + Dimension childSize = center.getMinimumSize(wHint, hHint); + middleRowWidth += childSize.width; + middleRowHeight = Math.max(childSize.height, middleRowHeight); + columns += 1; + } + + rows += columns > 0 ? 1 : 0; + // Add spacing, insets, and the size of the middle row + minSize.height += middleRowHeight + border.getHeight() + ((rows - 1) * vGap); + minSize.width = Math.max(minSize.width, middleRowWidth) + border.getWidth() + + ((columns - 1) * hGap); + + return minSize; +} + +/** + * @see AbstractLayout#calculatePreferredSize(IFigure, int, int) + */ +protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) { + int minWHint = 0, minHHint = 0; + if (wHint < 0) + minWHint = -1; + + if (hHint < 0) + minHHint = -1; + + Insets border = container.getInsets(); + wHint = Math.max(minWHint, wHint - border.getWidth()); + hHint = Math.max(minHHint, hHint - border.getHeight()); + Dimension prefSize = new Dimension(); + int middleRowWidth = 0, middleRowHeight = 0; + int rows = 0, columns = 0; + + if (top !is null && top.isVisible()) { + Dimension childSize = top.getPreferredSize(wHint, hHint); + hHint = Math.max(minHHint, hHint - (childSize.height + vGap)); + prefSize.setSize(childSize); + rows += 1; + } + if (bottom !is null && bottom.isVisible()) { + Dimension childSize = bottom.getPreferredSize(wHint, hHint); + hHint = Math.max(minHHint, hHint - (childSize.height + vGap)); + prefSize.width = Math.max(prefSize.width, childSize.width); + prefSize.height += childSize.height; + rows += 1; + } + if (left !is null && left.isVisible()) { + Dimension childSize = left.getPreferredSize(wHint, hHint); + middleRowWidth = childSize.width; + middleRowHeight = childSize.height; + wHint = Math.max(minWHint, wHint - (childSize.width + hGap)); + columns += 1; + } + if (right !is null && right.isVisible()) { + Dimension childSize = right.getPreferredSize(wHint, hHint); + middleRowWidth += childSize.width; + middleRowHeight = Math.max(childSize.height, middleRowHeight); + wHint = Math.max(minWHint, wHint - (childSize.width + hGap)); + columns += 1; + } + if (center !is null && center.isVisible()) { + Dimension childSize = center.getPreferredSize(wHint, hHint); + middleRowWidth += childSize.width; + middleRowHeight = Math.max(childSize.height, middleRowHeight); + columns += 1; + } + + rows += columns > 0 ? 1 : 0; + // Add spacing, insets, and the size of the middle row + prefSize.height += middleRowHeight + border.getHeight() + ((rows - 1) * vGap); + prefSize.width = Math.max(prefSize.width, middleRowWidth) + border.getWidth() + + ((columns - 1) * hGap); + + return prefSize; +} + +/** + * @see dwtx.draw2d.LayoutManager#layout(IFigure) + */ +public void layout(IFigure container) { + Rectangle area = container.getClientArea(); + Rectangle rect = new Rectangle(); + + Dimension childSize; + + if (top !is null && top.isVisible()) { + childSize = top.getPreferredSize(area.width, -1); + rect.setLocation(area.x, area.y); + rect.setSize(childSize); + rect.width = area.width; + top.setBounds(rect); + area.y += rect.height + vGap; + area.height -= rect.height + vGap; + } + if (bottom !is null && bottom.isVisible()) { + childSize = bottom.getPreferredSize(Math.max(area.width, 0), -1); + rect.setSize(childSize); + rect.width = area.width; + rect.setLocation(area.x, area.y + area.height - rect.height); + bottom.setBounds(rect); + area.height -= rect.height + vGap; + } + if (left !is null && left.isVisible()) { + childSize = left.getPreferredSize(-1, Math.max(0, area.height)); + rect.setLocation(area.x, area.y); + rect.width = childSize.width; + rect.height = Math.max(0, area.height); + left.setBounds(rect); + area.x += rect.width + hGap; + area.width -= rect.width + hGap; + } + if (right !is null && right.isVisible()) { + childSize = right.getPreferredSize(-1, Math.max(0, area.height)); + rect.width = childSize.width; + rect.height = Math.max(0, area.height); + rect.setLocation(area.x + area.width - rect.width, area.y); + right.setBounds(rect); + area.width -= rect.width + hGap; + } + if (center !is null && center.isVisible()) { + if (area.width < 0) + area.width = 0; + if (area.height < 0) + area.height = 0; + center.setBounds(area); + } +} + +/** + * @see dwtx.draw2d.AbstractLayout#remove(IFigure) + */ +public void remove(IFigure child) { + if (center is child) { + center = null; + } else if (top is child) { + top = null; + } else if (bottom is child) { + bottom = null; + } else if (right is child) { + right = null; + } else if (left is child) { + left = null; + } +} + +/** + * Sets the location of hte given child in this layout. Valid constraints: + *

    + *
  • {@link #CENTER}
  • + *
  • {@link #TOP}
  • + *
  • {@link #BOTTOM}
  • + *
  • {@link #LEFT}
  • + *
  • {@link #RIGHT}
  • + *
  • null (to remove a child's constraint)
  • + *
+ * + *

+ * Ensure that the given Figure is indeed a child of the Figure on which this layout has + * been set. Proper behaviour cannot be guaranteed if that is not the case. Also ensure + * that every child has a valid constraint. + *

+ *

+ * Passing a null constraint will invoke {@link #remove(IFigure)}. + *

+ *

+ * If the given child was assigned another constraint earlier, it will be re-assigned to + * the new constraint. If there is another child with the given constraint, it will be + * over-ridden so that the given child now has that constraint. + *

+ * + * @see dwtx.draw2d.AbstractLayout#setConstraint(IFigure, Object) + */ +public void setConstraint(IFigure child, Object constraint) { + remove(child); + super.setConstraint(child, constraint); + if (constraint is null) { + return; + } + + switch ((cast(Integer) constraint).intValue()) { + case PositionConstants.CENTER : + center = child; + break; + case PositionConstants.TOP : + top = child; + break; + case PositionConstants.BOTTOM : + bottom = child; + break; + case PositionConstants.RIGHT : + right = child; + break; + case PositionConstants.LEFT : + left = child; + break; + default : + break; + } +} + +/** + * Sets the horizontal spacing to be used between the children. Default is 0. + * + * @param gap The horizonal spacing + */ +public void setHorizontalSpacing(int gap) { + hGap = gap; +} + +/** + * Sets the vertical spacing ot be used between the children. Default is 0. + * + * @param gap The vertical spacing + */ +public void setVerticalSpacing(int gap) { + vGap = gap; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/BufferedGraphicsSource.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/BufferedGraphicsSource.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.BufferedGraphicsSource; + +import dwt.dwthelper.utils; + + + +import dwt.DWT; +import dwt.DWTError; +import dwt.graphics.GC; +import dwt.graphics.Image; +static import dwt.graphics.Point; +import dwt.widgets.Canvas; +import dwt.widgets.Caret; +import dwt.widgets.Control; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.GraphicsSource; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.SWTGraphics; + +class BufferedGraphicsSource : GraphicsSource { + +private Image imageBuffer; +private GC imageGC; +private GC controlGC; +private Control control; +private Rectangle inUse; + +/** + * Constructs a new buffered graphics source using the given control. + * @since 2.1 + * @param c the control + */ +public this(Control c) { + control = c; +} + +/** + * @see dwtx.draw2d.GraphicsSource#flushGraphics(dwtx.draw2d.geometry.Rectangle) + */ +public void flushGraphics(Rectangle region) { + if (inUse.isEmpty()) + return; + + bool restoreCaret = false; + Canvas canvas = null; + if (auto canvas = cast(Canvas)control ) { + Caret caret = canvas.getCaret(); + if (caret !is null) + restoreCaret = caret.isVisible(); + if (restoreCaret) + caret.setVisible(false); + } + /* + * The imageBuffer may be null if double-buffering was not successful. + */ + if (imageBuffer !is null) { + imageGC.dispose(); + controlGC.drawImage(getImage(), + 0, 0, inUse.width, inUse.height, + inUse.x, inUse.y, inUse.width, inUse.height); + imageBuffer.dispose(); + imageBuffer = null; + imageGC = null; + } + controlGC.dispose(); + controlGC = null; + + if (restoreCaret) + canvas.getCaret().setVisible(true); +} + +/** + * @see dwtx.draw2d.GraphicsSource#getGraphics(dwtx.draw2d.geometry.Rectangle) + */ +public Graphics getGraphics(Rectangle region) { + if (control is null || control.isDisposed()) + return null; + + dwt.graphics.Point.Point ptSWT = control.getSize(); + inUse = new Rectangle(0, 0, ptSWT.x, ptSWT.y); + inUse.intersect(region); + if (inUse.isEmpty()) + return null; + + /* + * Bugzilla 53632 - Attempts to create large images on some platforms will fail. + * When this happens, do not use double-buffering for painting. + */ + try { + imageBuffer = new Image(null, inUse.width, inUse.height); + } catch (DWTError noMoreHandles) { + imageBuffer = null; + } catch (IllegalArgumentException tooBig) { + imageBuffer = null; + } + + controlGC = new GC(control, + control.getStyle() & (DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT)); + Graphics graphics; + if (imageBuffer !is null) { + imageGC = new GC(imageBuffer, + control.getStyle() & (DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT)); + imageGC.setBackground(controlGC.getBackground()); + imageGC.setForeground(controlGC.getForeground()); + imageGC.setFont(controlGC.getFont()); + imageGC.setLineStyle(controlGC.getLineStyle()); + imageGC.setLineWidth(controlGC.getLineWidth()); + imageGC.setXORMode(controlGC.getXORMode()); + graphics = new SWTGraphics(imageGC); + graphics.translate(inUse.getLocation().negate()); + } else { + graphics = new SWTGraphics(controlGC); + } + + graphics.setClip(inUse); + return graphics; +} + +/** + * Returns the current image buffer or null. + * @since 2.1 + * @return the current image buffer + */ +protected Image getImage() { + return imageBuffer; +} + +/** + * Returns the current GC used on the buffer or null. + * @since 2.1 + * @return the GC for the image buffer + */ +protected GC getImageGC() { + return imageGC; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Button.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Button.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Button; + +import dwt.dwthelper.utils; + +import dwt.graphics.Image; +import dwtx.draw2d.Clickable; +import dwtx.draw2d.ColorConstants; +import dwtx.draw2d.Label; + +/** + * A Button usually has a border and appears to move up and down in response to being + * pressed. It can contain an image and/or text in it. + */ +public class Button + : Clickable +{ + +/** + * Constructs a default Button with no icon or text. + * + * @since 2.0 + */ +public this() { + super(); + setStyle(STYLE_BUTTON); +} + +/** + * Contructs a Button containing the icon image. + * + * @param image Image to be used by the Button as its icon. + * @since 2.0 + */ +public this(Image image) { + super(new Label(image), STYLE_BUTTON); +} + +/** + * Constructs a Button containing the given text. + * + * @param text Text for the button. + * @since 2.0 + */ +public this(String text) { + super(new Label(text), STYLE_BUTTON); +} + +/** + * Constructs a Button with the given image and text. + * + * @param text Text for the button. + * @param image Image for the button. + * @since 2.0 + */ +public this(String text, Image image) { + super(new Label(text, image), STYLE_BUTTON); +} + +/** + * Initializes this button by setting its default border and setting its background color + * to {@link ColorConstants#button}. + * + * @since 2.0 + */ +protected void init() { + super.init(); + setBackgroundColor(ColorConstants.button); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ButtonBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ButtonBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,280 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ButtonBorder; + +import dwt.dwthelper.utils; + + + +import dwt.graphics.Color; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.SchemeBorder; +import dwtx.draw2d.Border; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.Clickable; +import dwtx.draw2d.ButtonModel; +import dwtx.draw2d.ColorConstants; + +/** + * Creates a border for a clickable type of figure, which works in conjunction with the + * Figure and its model. This border adjusts itself to the various states the model of the + * figure could be. This border uses an extended {@link SchemeBorder.Scheme Scheme} + * called {@link ButtonScheme} which provides more information required by border to + * handle the the states of the model. + * + * @see Scheme + * @see ButtonScheme + */ +public class ButtonBorder + : SchemeBorder +{ + alias SchemeBorder.paint paint; +/** + * Default button border. + * @see SCHEMES#BUTTON + */ +public static const Border BUTTON; +/** + * Inverted hightlight colors from BUTTON. + * @see SCHEMES#BUTTON_CONTRAST + */ +public static const Border BUTTON_CONTRAST; +/** + * Used for scrollbar buttons. + * @see SCHEMES#BUTTON_SCROLLBAR + */ +public static const Border BUTTON_SCROLLBAR; +/** + * Used for toolbar buttons. + * @see SCHEMES#TOOLBAR + */ +public static const Border TOOLBAR; + +static this(){ + BUTTON = new ButtonBorder(SCHEMES.BUTTON); + BUTTON_CONTRAST = new ButtonBorder(SCHEMES.BUTTON_CONTRAST); + BUTTON_SCROLLBAR = new ButtonBorder(SCHEMES.BUTTON_SCROLLBAR); + TOOLBAR = new ButtonBorder(SCHEMES.TOOLBAR); +} + +/** + * Provides for a scheme to represent the borders of clickable figures like buttons. + * Though similar to the {@link SchemeBorder.Scheme Scheme} it supports an extra set of + * borders for the pressed states. + */ +public static class ButtonScheme + : Scheme +{ + private Color[] + highlightPressed = null, + shadowPressed = null; + + /** + * Constructs a new button scheme where the input colors are the colors for the + * top-left and bottom-right sides of the border. These colors serve as the colors + * when the border is in a pressed state too. The width of each side is determined by + * the number of colors passed in as input. + * + * @param highlight Colors for the top-left sides of the border + * @param shadow Colors for the bottom-right sides of the border + * @since 2.0 + */ + public this(Color[] highlight, Color[] shadow) { + highlightPressed = this.highlight = highlight; + shadowPressed = this.shadow = shadow; + init(); + } + + /** + * Constructs a new button scheme where the input colors are the colors for the + * top-left and bottom-right sides of the border, for the normal and pressed states. + * The width of each side is determined by the number of colors passed in as input. + * + * @param hl Colors for the top-left sides of the border + * @param sh Colors for the bottom-right sides of the border + * @param hlp Colors for the top-left sides of the border when figure is pressed + * @param shp Colors for the bottom-right sides of the border when figure is pressed + * @since 2.0 + */ + public this(Color[] hl, Color[] sh, Color[] hlp, Color[] shp) { + highlight = hl; + shadow = sh; + highlightPressed = hlp; + shadowPressed = shp; + init(); + } + + /** + * Calculates and returns the Insets for this border. The calculations are based on + * the number of normal and pressed, highlight and shadow colors. + * + * @return The insets for this border + * @since 2.0 + */ + protected Insets calculateInsets() { + int br = 1 + Math.max(getShadow().length, getHighlightPressed().length); + int tl = Math.max(getHighlight().length, getShadowPressed().length); + return new Insets(tl, tl, br, br); + } + + /** + * Calculates and returns the opaque state of this border. + *

+ * Returns false in the following conditions: + *

    + *
  • The number of highlight colors is different than the the number of + * shadow colors. + *
  • The number of pressed highlight colors is different than the number of + * pressed shadow colors. + *
  • Any of the highlight and shadow colors are set to null + *
  • Any of the pressed highlight and shadow colors are set to + * null + *
+ * This is done so that the entire region under the figure is properly covered. + * + * @return The opaque state of this border + * @since 2.0 + */ + protected bool calculateOpaque() { + if (!super.calculateOpaque()) + return false; + if (getHighlight().length !is getShadowPressed().length) + return false; + if (getShadow().length !is getHighlightPressed().length) + return false; + Color [] colors = getHighlightPressed(); + for (int i = 0; i < colors.length; i++) + if (colors[i] is null) + return false; + colors = getShadowPressed(); + for (int i = 0; i < colors.length; i++) + if (colors[i] is null) + return false; + return true; + } + + /** + * Returns the pressed highlight colors of this border. + * + * @return Colors as an array of Colors + * @since 2.0 + */ + protected Color[] getHighlightPressed() { + return highlightPressed; + } + + /** + * Returns the pressed shadow colors of this border. + * + * @return Colors as an array of Colors + * @since 2.0 + */ + protected Color[] getShadowPressed() { + return shadowPressed; + } +} + +/** + * Interface defining commonly used schemes for the ButtonBorder. + */ +public struct SCHEMES { + + /** + * Contrast button scheme + */ + static const ButtonScheme BUTTON_CONTRAST; + /** + * Regular button scheme + */ + static const ButtonScheme BUTTON; + /** + * Toolbar button scheme + */ + static const ButtonScheme TOOLBAR; + /** + * Scrollbar button scheme + */ + static const ButtonScheme BUTTON_SCROLLBAR; +static this(){ + BUTTON_CONTRAST = new ButtonScheme( + [ColorConstants.button, ColorConstants.buttonLightest], + DARKEST_DARKER + ); + BUTTON = new ButtonScheme( + [ColorConstants.buttonLightest], + DARKEST_DARKER + ); + TOOLBAR = new ButtonScheme( + [ColorConstants.buttonLightest], + [ColorConstants.buttonDarker] + ); + BUTTON_SCROLLBAR = new ButtonScheme( + [ColorConstants.button, ColorConstants.buttonLightest], + DARKEST_DARKER, + [ColorConstants.buttonDarker], + [ColorConstants.buttonDarker] + ); +} +} + +/** + * Constructs a ButtonBorder with a predefined button scheme set as its default. + * + * @since 2.0 + */ +public this() { + setScheme(SCHEMES.BUTTON); +} + +/** + * Constructs a ButtonBorder with the input ButtonScheme set as its Scheme. + * + * @param scheme ButtonScheme for this ButtonBorder. + * @since 2.0 + */ +public this(ButtonScheme scheme) { + setScheme(scheme); +} + +/** + * Paints this border with the help of the set scheme, the model of the clickable figure, + * and other inputs. The scheme is used in conjunction with the state of the model to get + * the appropriate colors for the border. + * + * @param figure The Clickable that this border belongs to + * @param graphics The graphics used for painting + * @param insets The insets + */ +public void paint(IFigure figure, Graphics graphics, Insets insets) { + Clickable clickable = cast(Clickable)figure; + ButtonModel model = clickable.getModel(); + ButtonScheme colorScheme = cast(ButtonScheme)getScheme(); + + if (clickable.isRolloverEnabled() && !model.isMouseOver() + && !model.isSelected()) + return; + + Color[] tl, br; + if (model.isSelected() || model.isArmed()) { + tl = colorScheme.getShadowPressed(); + br = colorScheme.getHighlightPressed(); + } else { + tl = colorScheme.getHighlight(); + br = colorScheme.getShadow(); + } + + paint(graphics, figure, insets, tl, br); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ButtonGroup.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ButtonGroup.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,231 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ButtonGroup; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Bean; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.ButtonModel; + +/** + * A ButtonGroup holds a group of {@link Clickable Clickable's} models and provides unique + * selection in them. There is capability to add a default selection. Models who want to + * belong to the group should just add themselves to this group. By doing so they listen + * to this group for changes. + *

+ * Setting of the default selection results in its being selected any time + * {@link #setSelected(ButtonModel, bool)} is called. If no default selection is set, + * the last entry selected is not allowed to deselect. + */ +public class ButtonGroup { + +private ButtonModel selectedModel; +private ButtonModel defaultModel; +private List members; +private List listeners; + +/** + * Constructs a ButtonGroup with no default selection. + * + * @since 2.0 + */ +public this() { + members = new ArrayList(); + listeners = new ArrayList(); +} + +/** + * Adds the passed ButtonModel to the ButtonGroup. + * + * @param model ButtonModel to be added to this group + * @since 2.0 + */ +public void add(ButtonModel model) { + if (!members.contains(model)) { + model.setGroup(this); + members.add(model); + } +} + +/** + * Adds the passed listener. ButtonGroups use PropertyChangeListeners to react to + * selection changes in the ButtonGroup. + * + * @param listener Listener to be added to this group + * @since 2.0 + */ +public void addPropertyChangeListener(PropertyChangeListener listener) { + listeners.add(cast(Object)listener); +} + +/** + * Fires a PropertyChangeEvent to all PropertyChangeListeners added to this ButtonGroup. + * + * @param oldValue Old selection value + * @param newValue New selection value + * @since 2.0 + */ +protected void firePropertyChange(Object oldValue, Object newValue) { + PropertyChangeEvent event = new PropertyChangeEvent( + this, ButtonModel.SELECTED_PROPERTY, oldValue, newValue); + for (int i = 0; i < listeners.size(); i++) + (cast(PropertyChangeListener)listeners.get(i)).propertyChange(event); +} + +/** + * Returns the ButtonModel which is selected by default for this ButtonGroup. + * + * @return The default ButtonModel + * @since 2.0 + */ +public ButtonModel getDefault() { + return defaultModel; +} + +/** + * Returns a List which contains all of the {@link ButtonModel ButtonModels} added to this + * ButtonGroup. + * + * @return The List of ButtonModels in this ButtonGroup + * @since 2.0 + */ +public List getElements() { + return members; +} + +/** + * Returns the ButtonModel for the currently selected button. + * + * @return The ButtonModel for the currently selected button + * @since 2.0 + */ +public ButtonModel getSelected() { + return selectedModel; +} + +/** + * Determines if the given ButtonModel is selected or not. + * + * @param model Model being tested for selected status + * @return Selection state of the given model + * @since 2.0 + */ +public bool isSelected(ButtonModel model) { + return (model is getSelected()); +} + +/** + * Removes the given ButtonModel from this ButtonGroup. + * + * @param model ButtonModel being removed + * @since 2.0 + */ +public void remove(ButtonModel model) { + if (getSelected() is model) + setSelected(getDefault(), true); + if (defaultModel is model) + defaultModel = null; + members.remove(model); +} + +/** + * Removes the passed PropertyChangeListener from this ButtonGroup. + * + * @param listener PropertyChangeListener to be removed + * @since 2.0 + */ +public void removePropertyChangeListener(PropertyChangeListener listener) { + listeners.remove(cast(Object)listener); +} + +/** + * Sets the passed ButtonModel to be the currently selected ButtonModel of this + * ButtonGroup. Fires a property change. + * + * @param model ButtonModel to be selected + * @since 2.0 + */ +protected void selectNewModel(ButtonModel model) { + ButtonModel oldModel = selectedModel; + selectedModel = model; + if (oldModel !is null) + oldModel.setSelected(false); + firePropertyChange(oldModel, model); +} + +/** + * Sets the default selection of this ButtonGroup. Does nothing if it is not present in + * the group. Sets selection to the passed ButtonModel. + * + * @param model ButtonModel which is to be the default selection. + * @since 2.0 + */ +public void setDefault(ButtonModel model) { + defaultModel = model; + if (getSelected() is null && defaultModel !is null) + defaultModel.setSelected(true); +} + +/** + * Sets the button with the given ButtonModel to be selected. + * + * @param model The ButtonModel to be selected + * @since 2.0 + */ +public void setSelected(ButtonModel model) { + if (model is null) + selectNewModel(null); + else + model.setSelected(true); +} + +/** + * Sets model to the passed state. + *

+ * If value is + *

    + *
  • true: + *
      + *
    • The passed ButtonModel will own selection. + *
    + *
  • false: + *
      + *
    • If the passed model owns selection, it will lose selection, and + * selection will be given to the default ButonModel. If no default + * ButtonModel was set, selection will remain as it was, as one ButtonModel + * must own selection at all times. + *
    • If the passed model does not own selection, then selection will remain + * as it was. + *
    + *
+ * + * @param model The model to be affected + * @param value The selected state + * @since 2.0 + */ +public void setSelected(ButtonModel model, bool value) { + if (value) { + if (model is getSelected()) + return; + selectNewModel(model); + } else { + if (model !is getSelected()) + return; + if (getDefault() is null) + return; + getDefault().setSelected(true); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ButtonModel.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ButtonModel.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,572 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ButtonModel; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; +import dwtx.dwtxhelper.Collection; +import dwtx.dwtxhelper.Timer; +import dwtx.dwtxhelper.TimerTask; +static import dwt.widgets.Display; + +import dwtx.draw2d.ButtonGroup; +import dwtx.draw2d.ActionListener; +import dwtx.draw2d.ChangeListener; +import dwtx.draw2d.ButtonStateTransitionListener; +import dwtx.draw2d.EventListenerList; +import dwtx.draw2d.ActionEvent; +import dwtx.draw2d.ChangeEvent; + + +//import dwtx.draw2d.internal.Timer; + +/** + * A model for buttons containing several properties, including enabled, pressed, + * selected, rollover enabled and mouseover. + */ +public class ButtonModel { + +/** Enabled property */ +public static const String ENABLED_PROPERTY = "enabled"; //$NON-NLS-1$ +/** Pressed property */ +public static const String PRESSED_PROPERTY = "pressed"; //$NON-NLS-1$ +/** Selected property */ +public static const String SELECTED_PROPERTY = "selected"; //$NON-NLS-1$ +/** Rollover Enabled property */ +public static const String ROLLOVER_ENABLED_PROPERTY = "rollover enabled"; //$NON-NLS-1$ +/** Mouseover property */ +public static const String MOUSEOVER_PROPERTY = "mouseover"; //$NON-NLS-1$ + +/** Armed property */ +public static const String ARMED_PROPERTY = "armed"; //$NON-NLS-1$ + + +/** Flag for armed button state */ +protected static const int ARMED_FLAG = 1; +/** Flag for pressed button state */ +protected static const int PRESSED_FLAG = 2; +/** Flag for mouseOver state */ +protected static const int MOUSEOVER_FLAG = 4; +/** Flag for selected button state */ +protected static const int SELECTED_FLAG = 8; +/** Flag for enablement button state */ +protected static const int ENABLED_FLAG = 16; +/** Flag for rollover enablement button state */ +protected static const int ROLLOVER_ENABLED_FLAG = 32; +/** Flag that can be used by subclasses to define more states */ +protected static const int MAX_FLAG = ROLLOVER_ENABLED_FLAG; + +private int state = ENABLED_FLAG; +private Object data; + +/** + * Action performed events are not fired until the mouse button is released. + */ +public static const int DEFAULT_FIRING_BEHAVIOR = 0; + +/** + * Action performed events fire repeatedly until the mouse button is released. + */ +public static const int REPEAT_FIRING_BEHAVIOR = 1; + +/** + * The name of the action associated with this button. + */ +protected String actionName; + +/** + * The ButtonGroup this button belongs to (if any). + */ +protected ButtonGroup group = null; + +private EventListenerList listeners; + +/** + * Listens to button state transitions and fires action performed events based on the + * desired behavior ({@link #DEFAULT_FIRING_BEHAVIOR} or {@link #REPEAT_FIRING_BEHAVIOR}). + */ +protected ButtonStateTransitionListener firingBehavior; + +this(){ + listeners = new EventListenerList(); + installFiringBehavior(); +} + +/** + * Registers the given listener as an ActionListener. + * + * @param listener The ActionListener to add + * @since 2.0 + */ +public void addActionListener(ActionListener listener) { + if (listener is null) + throw new IllegalArgumentException(""); + listeners.addListener(ActionListener.classinfo, cast(Object)listener); +} + +/** + * Registers the given listener as a ChangeListener. + * + * @param listener The ChangeListener to add + * @since 2.0 + */ +public void addChangeListener(ChangeListener listener) { + if (listener is null) + throw new IllegalArgumentException(""); + listeners.addListener(ChangeListener.classinfo, cast(Object)listener); +} + +/** + * Registers the given listener as a ButtonStateTransitionListener. + * + * @param listener The ButtonStateTransitionListener to add + * @since 2.0 + */ +public void addStateTransitionListener(ButtonStateTransitionListener listener) { + if (listener is null) + throw new IllegalArgumentException(""); + listeners.addListener(ButtonStateTransitionListener.classinfo, cast(Object)listener); +} + +/** + * Notifies any ActionListeners on this ButtonModel that an action has been performed. + * + * @since 2.0 + */ +protected void fireActionPerformed() { + Iterator iter = listeners.getListeners(ActionListener.classinfo); + ActionEvent action = new ActionEvent(this); + while (iter.hasNext()) + (cast(ActionListener)iter.next()). + actionPerformed(action); +} + +/** + * Notifies any listening ButtonStateTransitionListener that the pressed state of this + * button has been cancelled. + * + * @since 2.0 + */ +protected void fireCanceled() { + Iterator iter = listeners.getListeners(ButtonStateTransitionListener.classinfo); + while (iter.hasNext()) + (cast(ButtonStateTransitionListener)iter.next()). + canceled(); +} + +/** + * Notifies any listening ButtonStateTransitionListener that this button has been pressed. + * + * @since 2.0 + */ +protected void firePressed() { + Iterator iter = listeners.getListeners(ButtonStateTransitionListener.classinfo); + while (iter.hasNext()) + (cast(ButtonStateTransitionListener)iter.next()). + pressed(); +} + +/** + * Notifies any listening ButtonStateTransitionListener that this button has been + * released. + * + * @since 2.0 + */ +protected void fireReleased() { + Iterator iter = listeners.getListeners(ButtonStateTransitionListener.classinfo); + while (iter.hasNext()) + (cast(ButtonStateTransitionListener)iter.next()). + released(); +} + +/** + * Notifies any listening ButtonStateTransitionListeners that this button has resumed + * activity. + * + * @since 2.0 + */ +protected void fireResume() { + Iterator iter = listeners.getListeners(ButtonStateTransitionListener.classinfo); + while (iter.hasNext()) + (cast(ButtonStateTransitionListener)iter.next()). + resume(); +} + +/** + * Notifies any listening ChangeListeners that this button's state has changed. + * + * @param property The name of the property that changed + * @since 2.0 + */ +protected void fireStateChanged(String property) { + Iterator iter = listeners.getListeners(ChangeListener.classinfo); + ChangeEvent change = new ChangeEvent(this, property); + while (iter.hasNext()) + (cast(ChangeListener)iter.next()). + handleStateChanged(change); +} + +/** + * Notifies any listening ButtonStateTransitionListeners that this button has suspended + * activity. + * + * @since 2.0 + */ +protected void fireSuspend() { + Iterator iter = listeners.getListeners(ButtonStateTransitionListener.classinfo); + while (iter.hasNext()) + (cast(ButtonStateTransitionListener)iter.next()). + suspend(); +} + +bool getFlag(int which) { + return (state & which) !is 0; +} + +/** + * Returns the group to which this model belongs. + * + * @return The ButtonGroup to which this model belongs + * @since 2.0 + */ +public ButtonGroup getGroup() { + return group; +} + +/** + * Returns an object representing user data. + * + * @return User data + * @since 2.0 + */ +public Object getUserData() { + return data; +} + +/** + * Sets the firing behavior for this button. + * + * @since 2.0 + */ +protected void installFiringBehavior() { + setFiringBehavior(DEFAULT_FIRING_BEHAVIOR); +} + +/** + * Returns true if this button is armed. If a button is armed, it will fire + * an ActionPerformed when released. + * + * @return true if this button is armed + * @since 2.0 + */ +public bool isArmed() { + return (state & ARMED_FLAG) !is 0; +} + +/** + * Returns true if this button is enabled. + * + * @return true if this button is enabled + * @since 2.0 + */ +public bool isEnabled() { + return (state & ENABLED_FLAG) !is 0; +} + +/** + * Returns true if the mouse is over this button. + * + * @return true if the mouse is over this button + * @since 2.0 + */ +public bool isMouseOver() { + return (state & MOUSEOVER_FLAG) !is 0; +} + +/** + * Returns true if this button is pressed. + * + * @return true if this button is pressed + * @since 2.0 + */ +public bool isPressed() { + return (state & PRESSED_FLAG) !is 0; +} + +/** + * Returns the selection state of this model. If this model belongs to any group, the + * group is queried for selection state, else the flags are used. + * + * @return true if this button is selected + * @since 2.0 + */ +public bool isSelected() { + if (group is null) { + return (state & SELECTED_FLAG) !is 0; + } else { + return group.isSelected(this); + } +} + +/** + * Removes the given ActionListener. + * + * @param listener The ActionListener to remove + * @since 2.0 + */ +public void removeActionListener(ActionListener listener) { + listeners.removeListener(ActionListener.classinfo, cast(Object)listener); +} + +/** + * Removes the given ChangeListener. + * + * @param listener The ChangeListener to remove + * @since 2.0 + */ +public void removeChangeListener(ChangeListener listener) { + listeners.removeListener(ChangeListener.classinfo, cast(Object)listener); +} + +/** + * Removes the given ButtonStateTransitionListener. + * + * @param listener The ButtonStateTransitionListener to remove + * @since 2.0 + */ +public void removeStateTransitionListener(ButtonStateTransitionListener listener) { + listeners.removeListener(ButtonStateTransitionListener.classinfo, cast(Object)listener); +} + +/** + * Sets this button to be armed. If a button is armed, it will fire an ActionPerformed + * when released. + * + *@param value The armed state + * @since 2.0 + */ +public void setArmed(bool value) { + if (isArmed() is value) + return; + if (!isEnabled()) + return; + setFlag(ARMED_FLAG, value); + fireStateChanged(ARMED_PROPERTY); +} + +/** + * Sets this button to be enabled. + * + * @param value The enabled state + * @since 2.0 + */ +public void setEnabled(bool value) { + if (isEnabled() is value) + return; + if (!value) { + setMouseOver(false); + setArmed(false); + setPressed(false); + } + setFlag(ENABLED_FLAG, value); + fireStateChanged(ENABLED_PROPERTY); +} + +/** + * Sets the firing behavior for this button. {@link #DEFAULT_FIRING_BEHAVIOR} is the + * default behavior, where action performed events are not fired until the mouse button is + * released. {@link #REPEAT_FIRING_BEHAVIOR} causes action performed events to fire + * repeatedly until the mouse button is released. + * + * @param type The firing behavior type + * @since 2.0 + * + */ +public void setFiringBehavior(int type) { + if (firingBehavior !is null) + removeStateTransitionListener(firingBehavior); + switch (type) { + case REPEAT_FIRING_BEHAVIOR: + firingBehavior = new RepeatFiringBehavior(); + break; + default: + firingBehavior = new DefaultFiringBehavior(); + } + addStateTransitionListener(firingBehavior); +} + +void setFlag(int flag, bool value) { + if (value) + state |= flag; + else + state &= ~flag; +} + +/** + * Sets the ButtonGroup to which this model belongs to. Adds this model as a listener to + * the group. + * + * @param bg The group to which this model belongs. + * @since 2.0 + */ +public void setGroup(ButtonGroup bg) { + if (group is bg) + return; + if (group !is null) + group.remove(this); + group = bg; + if (group !is null) + group.add(this); +} + +/** + * Sets the mouseover property of this button. + * + * @param value The value the mouseover property will be set to + * @since 2.0 + */ +public void setMouseOver(bool value) { + if (isMouseOver() is value) + return; + if (isPressed()) + if (value) + fireResume(); + else + fireSuspend(); + setFlag(MOUSEOVER_FLAG, value); + fireStateChanged(MOUSEOVER_PROPERTY); +} + +/** + * Sets the pressed property of this button. + * + * @param value The value the pressed property will be set to + * @since 2.0 + */ +public void setPressed(bool value) { + if (isPressed() is value) + return; + setFlag(PRESSED_FLAG, value); + if (value) + firePressed(); + else { + if (isArmed()) + fireReleased(); + else + fireCanceled(); + } + fireStateChanged(PRESSED_PROPERTY); +} + +/** + * Sets this button to be selected. + * + * @param value The value the selected property will be set to + * @since 2.0 + */ +public void setSelected(bool value) { + if (group is null) { + if (isSelected() is value) + return; + } else { + group.setSelected(this, value); + if (getFlag(SELECTED_FLAG) is isSelected()) + return; + } + setFlag(SELECTED_FLAG, value); + fireStateChanged(SELECTED_PROPERTY); +} + +/** + * Sets user data. + * + * @param data The user data + * @since 2.0 + */ +public void setUserData(Object data) { + this.data = data; +} + +class DefaultFiringBehavior + : ButtonStateTransitionListener +{ + public void released() { + fireActionPerformed(); + } +} + +class RepeatFiringBehavior + : ButtonStateTransitionListener +{ + protected static const int + INITIAL_DELAY = 250, + STEP_DELAY = 40; + + protected int + stepDelay = INITIAL_DELAY, + initialDelay = STEP_DELAY; + + protected Timer timer; + + public void pressed() { + fireActionPerformed(); + if (!isEnabled()) + return; + + timer = new Timer(); + TimerTask runAction = new Task(timer); + + timer.scheduleAtFixedRate(runAction, INITIAL_DELAY, STEP_DELAY); + } + + public void canceled() { + suspend(); + } + public void released() { + suspend(); + } + + public void resume() { + timer = new Timer(); + + TimerTask runAction = new Task(timer); + + timer.scheduleAtFixedRate(runAction, STEP_DELAY, STEP_DELAY); + } + + public void suspend() { + if (timer is null) return; + timer.cancel(); + timer = null; + } +} + +class Task + : TimerTask { + + private Timer timer; + + public this(Timer timer) { + this.timer = timer; + } + + public void run() { + dwt.widgets.Display.Display.getDefault().syncExec(dgRunnable( { + if (!isEnabled()) + timer.cancel(); + fireActionPerformed(); + })); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ButtonStateTransitionListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ButtonStateTransitionListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ButtonStateTransitionListener; + +import dwt.dwthelper.utils; + +class ButtonStateTransitionListener { + +protected final void cancel() { } +public void canceled() { } +final void cancelled() { } + +protected final void press() { } +public void pressed() { } + +protected final void release() { } +public void released() { } + +public void resume() { } + +public void suspend() { } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ChangeEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ChangeEvent.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ChangeEvent; + +import dwt.dwthelper.utils; + +/** + * An event for property changes. Includes the source of the event as well as the name of + * the property that has changed. + */ +public class ChangeEvent + : /+java.util.+/EventObject +{ + +private String property; + +/** + * Constructs a new ChangeEvent with the given object as the source of the event. + * @param source The source of the event + */ +public this(Object source) { + super(source); +} + +/** + * Constructs a new ChangeEvent with the given source object and property name. + * @param source The source of the event + * @param property The property name + */ +public this(Object source, String property) { + super(source); + setPropertyName(property); +} + +/** + * Returns the name of the property that has changed. + * @return String the name of the property that has changed + */ +public String getPropertyName() { + return property; +} + +/** + * Sets the name of the property that has changed. + * @param string The property name + */ +protected void setPropertyName(String string) { + property = string; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ChangeListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ChangeListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ChangeListener; + +import dwt.dwthelper.utils; +import dwtx.draw2d.ChangeEvent; +import tango.core.Traits; +import tango.core.Tuple; + +/** + * A generic state listener + */ +public interface ChangeListener { + +/** + * Called when the listened to object's state has changed. + * @param event the ChangeEvent + */ +void handleStateChanged(ChangeEvent event); + +} + +// DWT Helper +private class _DgChangeListenerT(Dg,T...) : ChangeListener { + + alias ParameterTupleOf!(Dg) DgArgs; + static assert( is(DgArgs == Tuple!(ChangeEvent,T)), + "Delegate args not correct" ); + + Dg dg; + T t; + + private this( Dg dg, T t ){ + this.dg = dg; + static if( T.length > 0 ){ + this.t = t; + } + } + + void handleStateChanged( ChangeEvent e ){ + dg(e,t); + } +} + +ChangeListener dgChangeListener( Dg, T... )( Dg dg, T args ){ + return new _DgChangeListenerT!( Dg, T )( dg, args ); +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/CheckBox.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/CheckBox.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.CheckBox; + +import dwt.dwthelper.utils; +import dwt.dwthelper.ByteArrayInputStream; + +import dwt.graphics.Image; +import dwt.graphics.ImageData; +import dwtx.draw2d.Toggle; +import dwtx.draw2d.Label; +import dwtx.draw2d.ChangeListener; +import dwtx.draw2d.ChangeEvent; +import dwtx.draw2d.ButtonModel; + +/** + * A Checkbox is a toggle figure which toggles between the checked and unchecked figures + * to simulate a check box. A check box contains a text label to represent it. + */ +public final class CheckBox + : Toggle +{ + +private Label label = null; + +static const Image + UNCHECKED, + CHECKED; + +static this(){ + UNCHECKED = createImage( getImportData!("dwtx.draw2d.checkboxenabledoff.gif")); //$NON-NLS-1$ + CHECKED = createImage( getImportData!("dwtx.draw2d.checkboxenabledon.gif")); //$NON-NLS-1$ +} + +private static Image createImage( ImportData importdata ) { + Image image = new Image(null, new ImageData(new ByteArrayInputStream( cast(byte[]) importdata.data))); + return image; +} + +/** + * Constructs a CheckBox with no text. + * + * @since 2.0 + */ +public this() { + this(""); //$NON-NLS-1$ +} + +/** + * Constructs a CheckBox with the passed text in its label. + * @param text The label text + * @since 2.0 + */ +public this(String text) { + setContents(label = new Label(text, UNCHECKED)); +} + +/** + * Adjusts CheckBox's icon depending on selection status. + * + * @since 2.0 + */ +protected void handleSelectionChanged() { + if (isSelected()) + label.setIcon(CHECKED); + else + label.setIcon(UNCHECKED); +} + +/** + * Initializes this Clickable by setting a default model and adding a clickable event + * handler for that model. Also adds a ChangeListener to update icon when selection + * status changes. + * + * @since 2.0 + */ +protected void init() { + super.init(); + addChangeListener(new class() ChangeListener { + public void handleStateChanged(ChangeEvent changeEvent) { + if (changeEvent.getPropertyName().equals(ButtonModel.SELECTED_PROPERTY)) + handleSelectionChanged(); + } + }); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ChopboxAnchor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ChopboxAnchor.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ChopboxAnchor; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.AbstractConnectionAnchor; + +/** + * The ChopboxAnchor's location is found by calculating the intersection of a line drawn + * from the center point of its owner's box to a reference point on that box. Thus + * {@link Connection Connections} using the ChopBoxAnchor will be oriented such that they + * point to their owner's center. + */ +public class ChopboxAnchor + : AbstractConnectionAnchor +{ + +/** + * Constructs a new ChopboxAnchor. + */ +protected this() { } + +/** + * Constructs a ChopboxAnchor with the given owner figure. + * + * @param owner The owner figure + * @since 2.0 + */ +public this(IFigure owner) { + super(owner); +} + +/** + * Gets a Rectangle from {@link #getBox()} and returns the Point where a line from the + * center of the Rectangle to the Point reference intersects the Rectangle. + * + * @param reference The reference point + * @return The anchor location + */ +public Point getLocation(Point reference) { + Rectangle r = Rectangle.SINGLETON; + r.setBounds(getBox()); + r.translate(-1, -1); + r.resize(1, 1); + + getOwner().translateToAbsolute(r); + float centerX = r.x + 0.5f * r.width; + float centerY = r.y + 0.5f * r.height; + + if (r.isEmpty() || (reference.x is cast(int)centerX && reference.y is cast(int)centerY)) + return new Point(cast(int)centerX, cast(int)centerY); //This avoids divide-by-zero + + float dx = reference.x - centerX; + float dy = reference.y - centerY; + + //r.width, r.height, dx, and dy are guaranteed to be non-zero. + float scale = 0.5f / Math.max(Math.abs(dx) / r.width, Math.abs(dy) / r.height); + + dx *= scale; + dy *= scale; + centerX += dx; + centerY += dy; + + return new Point(Math.round(centerX), Math.round(centerY)); +} + +/** + * Returns the bounds of this ChopboxAnchor's owner. Subclasses can override this method + * to adjust the box the anchor can be placed on. For instance, the owner figure may have + * a drop shadow that should not be included in the box. + * + * @return The bounds of this ChopboxAnchor's owner + * @since 2.0 + */ +protected Rectangle getBox() { + return getOwner().getBounds(); +} + +/** + * Returns the anchor's reference point. In the case of the ChopboxAnchor, this is the + * center of the anchor's owner. + * + * @return The reference point + */ +public Point getReferencePoint() { + Point ref_ = getBox().getCenter(); + getOwner().translateToAbsolute(ref_); + return ref_; +} + + +/** + * Returns true if the other anchor has the same owner and box. + * @param obj the other anchor + * @return true if equal + */ +public override int opEquals(Object obj) { + if (auto other = cast(ChopboxAnchor)obj ) { + return other.getOwner() is getOwner() && other.getBox().opEquals(getBox()); + } + return false; +} + +/** + * The owning figure's hashcode is used since equality is approximately based on the + * owner. + * @return the hash code. + */ +public override hash_t toHash() { + if (getOwner() !is null) + return (cast(Object)getOwner()).toHash(); + else + return super.toHash(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Clickable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Clickable.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,508 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Clickable; + +import dwt.dwthelper.utils; + +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Figure; +import dwtx.draw2d.ActionListener; +import dwtx.draw2d.ActionEvent; +import dwtx.draw2d.ChangeListener; +import dwtx.draw2d.ClickableEventHandler; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.ButtonModel; +import dwtx.draw2d.ChangeEvent; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.ToggleModel; +import dwtx.draw2d.StackLayout; +import dwtx.draw2d.ColorConstants; +import dwtx.draw2d.ButtonBorder; + + +/** + * A Clickable responds to mouse clicks in some way (determined by a ClickBehavior) and + * fires action events. No visual appearance of feedback is offered. Depends on a model + * holder and an event handler which understands the model and updates the model + * accordingly. {@link ButtonModel} is used by default. Any figure can be set as contents + * to a Clickable. Clickable->EventHandler->Model->ModelObserver->Listeners of actions. + */ +public class Clickable + : Figure +{ + +private static const int + ROLLOVER_ENABLED_FLAG = Figure.MAX_FLAG << 1, + STYLE_BUTTON_FLAG = Figure.MAX_FLAG << 2, + STYLE_TOGGLE_FLAG = Figure.MAX_FLAG << 3; + +/** + * The highest reserved flag used by this class + */ +protected static int + MAX_FLAG = STYLE_TOGGLE_FLAG; + +/** + * Style constant that defines a push button. The button will be pressed when the mouse is + * pressed on top of it. The button will be released when the mouse is released or is move + * off of the button. + * + */ +public static const int STYLE_BUTTON = STYLE_BUTTON_FLAG; + +/** + * Style constant that defines a toggle button. The button will toggle between 2 states + * when the mouse is clicked on the button. + */ +public static const int STYLE_TOGGLE = STYLE_TOGGLE_FLAG; + +/** + * An action is performed every time the mouse is released. + */ +public static const int DEFAULT_FIRING = 0; + +/** + * Firing starts as soon as the mouse is pressed on this Clickable, and keeps firing at + * prefixed intervals until the mouse is released. + */ +public static const int REPEAT_FIRING = 1; + +/** + * Observes the model for action and state changes. + * + * @see ActionListener + * @see ChangeListener + */ +/+static+/ interface ModelObserver + : ActionListener, ChangeListener +{ } + +private ClickableEventHandler eventHandler; + +private ButtonModel model; + +private ModelObserver modelObserver; + +private void instanceInit(){ + init(); + setRequestFocusEnabled(true); + setFocusTraversable(true); +} + +/** + * Constructs a Clickable with no contents. + */ +public this() { + instanceInit(); +} + +/** + * Constructs a Clickable whose contents are provided as input. The content figure + * occupies the entire region of the Clickable. + * + * @param contents The content figure + */ +public this(IFigure contents) { + this(contents, 0); +} + +/** + * Constructs a Clickable whose contents are provided as input. The content figure + * occupies the entire region of the Clickable. Sets the style to the given style + * (either {@link #STYLE_BUTTON} or {@link #STYLE_TOGGLE}). + * + * @param contents The content figure + * @param style The button style + */ +public this(IFigure contents, int style) { + instanceInit(); + setContents(contents); + setStyle(style); +} + +/** + * Adds the given listener to the list of action listeners of this Figure. Listener is + * called whenever an action is performed. + * + * @param listener The ActionListener to be added + * @since 2.0 + */ +public void addActionListener(ActionListener listener) { + addListener(ActionListener.classinfo, cast(Object)listener); +} + +/** + * Adds the given listener to the list of state change listeners of this figure. A + * ChangeListener is informed if there is any state change in the model requiring action + * by the listener. + * + * @param listener The ChangeListener to be added + * @since 2.0 + */ +public void addChangeListener(ChangeListener listener) { + addListener(ChangeListener.classinfo, cast(Object)listener); +} + +/** + * Returns a newly created ButtonModel as the default model to be used by this Clickable + * based on the button style. + * + * @return The model to be used by default + * @since 2.0 + */ +protected ButtonModel createDefaultModel() { + if (isStyle(STYLE_TOGGLE)) + return new ToggleModel(); + else + return new ButtonModel(); +} + +/** + * Returns a newly created event handler for this Clickable and its model. + * + * @return The event handler + * @since 2.0 + */ +protected ClickableEventHandler createEventHandler() { + return new ClickableEventHandler(); +} + +/** + * Returns a newly created model observer which listens to the model, and fires any action + * or state changes. A ModelObserver holds both an action listener and a state change + * listener. + * + * @return The newly created model observer + * @since 2.0 + */ +protected ModelObserver createModelObserver() { + return new class() ModelObserver { + public void actionPerformed(ActionEvent action) { + fireActionPerformed(); + } + public void handleStateChanged(ChangeEvent change) { + fireStateChanged(change); + } + }; +} + +/** + * Fires an action performed event. + * + * @since 2.0 + */ +public void doClick() { + fireActionPerformed(); +} + +/** + * Called when there has been an action performed by this Clickable, which is determined + * by the model. Notifies all ActionListener type listeners of an action performed. + * + * @since 2.0 + */ +protected void fireActionPerformed() { + ActionEvent action = new ActionEvent(this); + Iterator listeners = getListeners(ActionListener.classinfo); + while (listeners.hasNext()) + (cast(ActionListener)listeners.next()) //Leave newline for debug stepping + .actionPerformed(action); +} + +/** + * Called when there has been a change of state in the model of this clickable. Notifies + * all ChangeListener type listeners of the state change. + * + * @param modelChange The ChangeEvent + * @since 2.0 + */ +protected void fireStateChanged(ChangeEvent modelChange) { + ChangeEvent change = new ChangeEvent(this, modelChange.getPropertyName()); + Iterator listeners = getListeners(ChangeListener.classinfo); + while (listeners.hasNext()) + (cast(ChangeListener)listeners.next()) //Leave newline for debug stepping + .handleStateChanged(change); +} + +/** + * Returns the behavior model used by this Clickable. + * + * @return The model used by this Clickable + * @since 2.0 + */ +public ButtonModel getModel() { + return model; +} + +/** + * Adds the given ClickableEventHandler to this clickable. A ClickableEventHandler + * should be a MouseListener, MouseMotionListener, ChangeListener, KeyListener, and + * FocusListener. + * + * @param handler The new event handler + * @since 2.0 + */ +protected void hookEventHandler(ClickableEventHandler handler) { + if (handler is null) + return; + addMouseListener(handler); + addMouseMotionListener(handler); + addChangeListener(handler); + addKeyListener(handler); + addFocusListener(handler); +} + +/** + * Initializes this Clickable by setting a default model and adding a clickable event + * handler for that model. + * + * @since 2.0 + */ +protected void init() { + setModel(createDefaultModel()); + setEventHandler(createEventHandler()); +} + +/** + * Returns true if rollover feedback is enabled. + * + * @return true rollover feedback is enabled + * @since 2.0 + */ +public bool isRolloverEnabled() { + return (flags & ROLLOVER_ENABLED_FLAG) !is 0; +} + +/** + * Returns true if this Clickable is in a selected state. The model is the + * one which holds all this state based information. + * + * @return true if this Clickable is in a selected state + * @since 2.0 + */ +public bool isSelected() { + return getModel().isSelected(); +} + +/** + * Returns true if this Clickable's style is the same as the passed style. + * + * @param style The style to be checked + * @return true if this Clickable's style is the same as the passed style + * @since 2.0 + */ +public bool isStyle(int style) { + return ((style & flags) is style); +} + +/** + * If this Clickable has focus, this method paints a focus rectangle. + * + * @param graphics Graphics handle for painting + */ +protected void paintBorder(Graphics graphics) { + super.paintBorder(graphics); + if (hasFocus()) { + graphics.setForegroundColor(ColorConstants.black); + graphics.setBackgroundColor(ColorConstants.white); + + Rectangle area = getClientArea(); + if (isStyle(STYLE_BUTTON)) + graphics.drawFocus(area.x, area.y, area.width, area.height); + else + graphics.drawFocus(area.x, area.y, area.width - 1, area.height - 1); + } +} + +/** + * Paints the area of this figure excluded by the borders. Induces a (1,1) pixel shift in + * the painting if the mouse is armed, giving it the pressed appearance. + * + * @param graphics Graphics handle for painting + * @since 2.0 + */ +protected void paintClientArea(Graphics graphics) { + if (isStyle(STYLE_BUTTON) && (getModel().isArmed() || getModel().isSelected())) { + graphics.translate(1, 1); + graphics.pushState(); + super.paintClientArea(graphics); + graphics.popState(); + graphics.translate(-1, -1); + } else + super.paintClientArea(graphics); +} + +/** + * Removes the given listener from the list of ActionListener's of this Clickable. + * + * @param listener Listener to be removed from this figure + * @since 2.0 + */ +public void removeActionListener(ActionListener listener) { + removeListener(ActionListener.classinfo, cast(Object)listener); +} + +/** + * Removes the given listener from the list of ChangeListener's of this clickable. + * + * @param listener Listener to be removed from this figure + * @since 2.0 + */ +public void removeChangeListener(ChangeListener listener) { + removeListener(ChangeListener.classinfo, cast(Object)listener); +} + +/** + * Sets the Figure which is the contents of this Clickable. This Figure occupies the + * entire clickable region. + * + * @param contents Contents of the clickable + * @since 2.0 + */ +protected void setContents(IFigure contents) { + setLayoutManager(new StackLayout()); + if (getChildren().size() > 0) + remove(cast(IFigure)getChildren().get(0)); + add(contents); +} + +/** + * @see dwtx.draw2d.IFigure#setEnabled(bool) + */ +public void setEnabled(bool value) { + if (isEnabled() is value) + return; + super.setEnabled(value); + getModel().setEnabled(value); + setChildrenEnabled(value); +} + +/** + * Sets the event handler which interacts with the model to determine the behavior of this + * Clickable. + * + * @param h Event handler for this clickable + * @since 2.0 + */ +public void setEventHandler(ClickableEventHandler h) { + if (eventHandler !is null) + unhookEventHandler(eventHandler); + eventHandler = h; + if (eventHandler !is null) + hookEventHandler(eventHandler); +} + +/** + * Determines how this clickable is to fire notifications to its listeners. In the default + * firing method ({@link #DEFAULT_FIRING}), an action is performed every time the mouse + * is released. In the repeat firing method ({@link #REPEAT_FIRING}), firing starts as + * soon as it is pressed on this clickable, and keeps firing at prefixed intervals until + * the mouse is released. + * + * @param type Type of firing + * @since 2.0 + */ +public void setFiringMethod(int type) { + getModel().setFiringBehavior(type); +} + +/** + * Sets the model to be used by this clickable for its state and behavior determination. + * This clickable removes any observers from the previous model before adding new ones to + * the new model. + * + * @param model The new model of this Clickable + * @since 2.0 + */ +public void setModel(ButtonModel model) { + if (this.model !is null) { + this.model.removeChangeListener(modelObserver); + this.model.removeActionListener(modelObserver); + modelObserver = null; + } + this.model = model; + if (model !is null) { + modelObserver = createModelObserver(); + model.addActionListener(modelObserver); + model.addChangeListener(modelObserver); + } +} + +/** + * Enables or disables rollover feedback of this figure. Generally used in conjunction + * with the model to determine if feedback is to be shown. + * + * @param value The rollover state to be set + * @since 2.0 + */ +public void setRolloverEnabled(bool value) { + if (isRolloverEnabled() is value) + return; + setFlag(ROLLOVER_ENABLED_FLAG, value); + repaint(); +} + +/** + * Sets the selected state of this Clickable. Since the model is responsible for all state + * based information, it is informed of the state change. Extending classes can choose + * selection information, if they do not represent any selection. + * + * @param value New selected state of this clickable. + * @see #isSelected() + * @since 2.0 + */ +public void setSelected(bool value) { + getModel().setSelected(value); +} + +/** + * Sets this Clickable's style to the passed value, either {@link #STYLE_BUTTON} or + * {@link #STYLE_TOGGLE}. + * + * @param style The button style + * @since 2.0 + */ +public void setStyle(int style) { + if ((style & STYLE_BUTTON) !is 0) { + setFlag(STYLE_BUTTON_FLAG, true); + if (!(null !is cast(ButtonBorder)getBorder() )) + setBorder(new ButtonBorder()); + setOpaque(true); + } else { + setFlag(STYLE_BUTTON_FLAG, false); + setOpaque(false); + } + + if ((style & STYLE_TOGGLE) !is 0) { + setFlag(STYLE_TOGGLE_FLAG, true); + setModel(createDefaultModel()); + } +} + +/** + * Removes the given ClickableEventHandler containing listeners from this Clickable. + * + * @param handler The event handler to be removed + * @since 2.0 + */ +protected void unhookEventHandler(ClickableEventHandler handler) { + if (handler is null) + return; + removeMouseListener(handler); + removeMouseMotionListener(handler); + removeChangeListener(handler); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ClickableEventHandler.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ClickableEventHandler.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ClickableEventHandler; + +import dwt.dwthelper.utils; +import dwtx.draw2d.MouseMotionListener; +import dwtx.draw2d.MouseEvent; +import dwtx.draw2d.KeyEvent; +import dwtx.draw2d.FocusEvent; +import dwtx.draw2d.KeyListener; +import dwtx.draw2d.FocusListener; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.ChangeEvent; +import dwtx.draw2d.ChangeListener; +import dwtx.draw2d.MouseListener; +import dwtx.draw2d.FigureListener; +import dwtx.draw2d.Clickable; +import dwtx.draw2d.ButtonModel; + + +class ClickableEventHandler + : MouseMotionListener.Stub + , + MouseListener, + FigureListener, + ChangeListener, + KeyListener, + FocusListener +{ + +private MouseEvent lastEvent; + +public void focusLost(FocusEvent fe) { + Clickable loser = cast(Clickable)fe.loser; + loser.repaint(); + loser.getModel().setArmed(false); + loser.getModel().setPressed(false); +} + +public void focusGained(FocusEvent fe) { + Clickable clickable = cast(Clickable)fe.gainer; + clickable.repaint(); +} + +public void figureMoved(IFigure source) { + if (lastEvent is null) + return; + mouseDragged(lastEvent); +} + +public void handleStateChanged(ChangeEvent change) { + Clickable clickable = cast(Clickable)change.getSource(); + if (change.getPropertyName() is ButtonModel.MOUSEOVER_PROPERTY + && !clickable.isRolloverEnabled()) + return; + clickable.repaint(); +} + +public void mouseDoubleClicked(MouseEvent me) { } + +public void mouseDragged(MouseEvent me) { + lastEvent = me; + Clickable click = cast(Clickable)me.getSource(); + ButtonModel model = click.getModel(); + if (model.isPressed()) { + bool over = click.containsPoint(me.getLocation()); + model.setArmed(over); + model.setMouseOver(over); + } +} + +public void mouseEntered(MouseEvent me) { + Clickable click = cast(Clickable)me.getSource(); + click.getModel().setMouseOver(true); + click.addFigureListener(this); +} + +public void mouseExited(MouseEvent me) { + Clickable click = cast(Clickable)me.getSource(); + click.getModel().setMouseOver(false); + click.removeFigureListener(this); +} + +public void mouseMoved(MouseEvent me) { } + +public void mousePressed(MouseEvent me) { + if (me.button !is 1) + return; + lastEvent = me; + Clickable click = cast(Clickable)me.getSource(); + ButtonModel model = click.getModel(); + click.requestFocus(); + model.setArmed(true); + model.setPressed(true); + me.consume(); +} + +public void mouseReleased(MouseEvent me) { + if (me.button !is 1) + return; + ButtonModel model = (cast(Clickable)me.getSource()).getModel(); + if (!model.isPressed()) + return; + model.setPressed(false); + model.setArmed(false); + me.consume(); +} + +public void keyPressed(KeyEvent ke) { + ButtonModel model = (cast(Clickable)ke.getSource()).getModel(); + if (ke.character is ' ' || ke.character is '\r') { + model.setPressed(true); + model.setArmed(true); + } +} + +public void keyReleased(KeyEvent ke) { + ButtonModel model = (cast(Clickable)ke.getSource()).getModel(); + if (ke.character is ' ' || ke.character is '\r') { + model.setPressed(false); + model.setArmed(false); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ColorConstants.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ColorConstants.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,196 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.ColorConstants; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; +import tango.core.sync.Mutex; + +import dwt.DWT; +import dwt.graphics.Color; +import dwt.widgets.Display; + +/** + * A collection of color-related constants. + */ +public struct ColorConstants { + +class SystemColorFactory { + private static Color getColor(int which) { + Display display = Display.getCurrent(); + if (display !is null) + return display.getSystemColor(which); + display = Display.getDefault(); + Color result; + scope Mutex mutex = new Mutex; + display.syncExec(dgRunnable( { + synchronized (mutex) { + result = Display.getCurrent().getSystemColor(which); + } + } )); + synchronized (mutex) { + return result; + } + } +} + +/** + * @see DWT#COLOR_WIDGET_HIGHLIGHT_SHADOW + */ +static const Color buttonLightest; +/** + * @see DWT#COLOR_WIDGET_BACKGROUND + */ +static const Color button; +/** + * @see DWT#COLOR_WIDGET_NORMAL_SHADOW + */ +static const Color buttonDarker; +/** + * @see DWT#COLOR_WIDGET_DARK_SHADOW + */ +static const Color buttonDarkest; + +/** + * @see DWT#COLOR_LIST_BACKGROUND + */ +static const Color listBackground; +/** + * @see DWT#COLOR_LIST_FOREGROUND + */ +static const Color listForeground; + +/** + * @see DWT#COLOR_WIDGET_BACKGROUND + */ +static const Color menuBackground; +/** + * @see DWT#COLOR_WIDGET_FOREGROUND + */ +static const Color menuForeground; +/** + * @see DWT#COLOR_LIST_SELECTION + */ +static const Color menuBackgroundSelected; +/** + * @see DWT#COLOR_LIST_SELECTION_TEXT + */ +static const Color menuForegroundSelected; + +/** + * @see DWT#COLOR_TITLE_BACKGROUND + */ +static const Color titleBackground; +/** + * @see DWT#COLOR_TITLE_BACKGROUND_GRADIENT + */ +static const Color titleGradient; +/** + * @see DWT#COLOR_TITLE_FOREGROUND + */ +static const Color titleForeground; +/** + * @see DWT#COLOR_TITLE_INACTIVE_FOREGROUND + */ +static const Color titleInactiveForeground; +/** + * @see DWT#COLOR_TITLE_INACTIVE_BACKGROUND + */ +static const Color titleInactiveBackground; +/** + * @see DWT#COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT + */ +static const Color titleInactiveGradient; + +/** + * @see DWT#COLOR_INFO_FOREGROUND + */ +static const Color tooltipForeground; +/** + * @see DWT#COLOR_INFO_BACKGROUND + */ +static const Color tooltipBackground; + +/* + * Misc. colors + */ +/** One of the pre-defined colors */ +static const Color white; +/** One of the pre-defined colors */ +static const Color lightGray; +/** One of the pre-defined colors */ +static const Color gray; +/** One of the pre-defined colors */ +static const Color darkGray; +/** One of the pre-defined colors */ +static const Color black; +/** One of the pre-defined colors */ +static const Color red; +/** One of the pre-defined colors */ +static const Color orange; +/** One of the pre-defined colors */ +static const Color yellow; +/** One of the pre-defined colors */ +static const Color green; +/** One of the pre-defined colors */ +static const Color lightGreen; +/** One of the pre-defined colors */ +static const Color darkGreen; +/** One of the pre-defined colors */ +static const Color cyan; +/** One of the pre-defined colors */ +static const Color lightBlue; +/** One of the pre-defined colors */ +static const Color blue; +/** One of the pre-defined colors */ +static const Color darkBlue; + + +static this(){ + buttonLightest = SystemColorFactory.getColor(DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW); + button = SystemColorFactory.getColor(DWT.COLOR_WIDGET_BACKGROUND); + buttonDarker = SystemColorFactory.getColor(DWT.COLOR_WIDGET_NORMAL_SHADOW); + buttonDarkest = SystemColorFactory.getColor(DWT.COLOR_WIDGET_DARK_SHADOW); + listBackground = SystemColorFactory.getColor(DWT.COLOR_LIST_BACKGROUND); + listForeground = SystemColorFactory.getColor(DWT.COLOR_LIST_FOREGROUND); + menuBackground = SystemColorFactory.getColor(DWT.COLOR_WIDGET_BACKGROUND); + menuForeground = SystemColorFactory.getColor(DWT.COLOR_WIDGET_FOREGROUND); + menuBackgroundSelected = SystemColorFactory.getColor(DWT.COLOR_LIST_SELECTION); + menuForegroundSelected = SystemColorFactory.getColor(DWT.COLOR_LIST_SELECTION_TEXT); + titleBackground = SystemColorFactory.getColor(DWT.COLOR_TITLE_BACKGROUND); + titleGradient = SystemColorFactory.getColor(DWT.COLOR_TITLE_BACKGROUND_GRADIENT); + titleForeground = SystemColorFactory.getColor(DWT.COLOR_TITLE_FOREGROUND); + titleInactiveForeground = SystemColorFactory.getColor(DWT.COLOR_TITLE_INACTIVE_FOREGROUND); + titleInactiveBackground = SystemColorFactory.getColor(DWT.COLOR_TITLE_INACTIVE_BACKGROUND); + titleInactiveGradient = SystemColorFactory.getColor(DWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT); + tooltipForeground = SystemColorFactory.getColor(DWT.COLOR_INFO_FOREGROUND); + tooltipBackground = SystemColorFactory.getColor(DWT.COLOR_INFO_BACKGROUND); + white = new Color(null, 255, 255, 255); + lightGray = new Color(null, 192, 192, 192); + gray = new Color(null, 128, 128, 128); + darkGray = new Color(null, 64, 64, 64); + black = new Color(null, 0, 0, 0); + red = new Color(null, 255, 0, 0); + orange = new Color(null, 255, 196, 0); + yellow = new Color(null, 255, 255, 0); + green = new Color(null, 0, 255, 0); + lightGreen = new Color(null, 96, 255, 96); + darkGreen = new Color(null, 0, 127, 0); + cyan = new Color(null, 0, 255, 255); + lightBlue = new Color(null, 127, 127, 255); + blue = new Color(null, 0, 0, 255); + darkBlue = new Color(null, 0, 0, 127); +} +} + + diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/CompoundBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/CompoundBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.CompoundBorder; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; + +import dwtx.draw2d.AbstractBorder; +import dwtx.draw2d.Border; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; + +/** + * CompoundBorder allows for the nesting of two borders. The nested borders are referred + * to as the inner and outer borders. + */ +public class CompoundBorder + : AbstractBorder +{ + +/** The inner Border. */ +protected Border inner; +/** The outer Border. */ +protected Border outer; + +/** + * Constructs a default CompoundBorder with no borders under it. + * + * @since 2.0 + */ +public this() { } + +/** + * Constructs a CompoundBorder with the two borders specified as input. + * + * @param outer Border which is drawn on the outside + * @param inner Border which is drawn inside the outer border + * + * @since 2.0 + */ +public this(Border outer, Border inner) { + this.outer = outer; + this.inner = inner; +} + +/** + * Returns the inner border of this CompoundBorder. + * + * @return The inner border + * @since 2.0 + */ +public Border getInnerBorder() { + return inner; +} + +/** + * Returns the total insets required to hold both the inner and outer borders of this + * CompoundBorder. + * + * @param figure Figure for which this is the border + * @return The total insets for this border + * @since 2.0 + */ +public Insets getInsets(IFigure figure) { + Insets insets = null; + if (inner !is null) + insets = inner.getInsets(figure); + else + insets = new Insets(); + if (outer !is null) { + Insets moreInsets = outer.getInsets(figure); + insets = insets.getAdded(moreInsets); + } + return insets; +} + +/** + * @see dwtx.draw2d.Border#getPreferredSize(IFigure) + */ +public Dimension getPreferredSize(IFigure fig) { + Dimension prefSize = new Dimension(inner.getPreferredSize(fig)); + Insets outerInsets = outer.getInsets(fig); + prefSize.expand(outerInsets.getWidth(), outerInsets.getHeight()); + prefSize.union_(outer.getPreferredSize(fig)); + return prefSize; +} + +/** + * Returns the outer border of this CompoundBorder. + * + * @return The outer border + * @since 2.0 + */ +public Border getOuterBorder() { + return outer; +} + +/** + * Returns true if this border is opaque. Return value is dependent on the + * opaque state of both the borders it contains. Both borders have to be opaque for this + * border to be opaque. In the absence of any of the borders, this border is not opaque. + * + * @return true if this border is opaque + */ +public bool isOpaque() { + return ((inner !is null) ? inner.isOpaque() : false) + && ((outer !is null) ? outer.isOpaque() : false); +} + +/** + * @see dwtx.draw2d.Border#paint(IFigure, Graphics, Insets) + */ +public void paint(IFigure figure, Graphics g, Insets insets) { + if (outer !is null) { + g.pushState(); + outer.paint(figure, g, insets); + g.popState(); + + insets = insets.getAdded(outer.getInsets(figure)); + } + if (inner !is null) + inner.paint(figure, g, insets); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Connection.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Connection.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Connection; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.ConnectionRouter; +import dwtx.draw2d.ConnectionAnchor; + +/** + * A Connection is a figure that connects two objects. + */ +public interface Connection : IFigure { + +/** + * The connection router property. Used to signify that the ConnectionRouter has changed. + */ +static const String PROPERTY_CONNECTION_ROUTER = "connectionRouter"; //$NON-NLS-1$ +/** + * The points property. Used to signify the points in the Connection have changed. + */ +static const String PROPERTY_POINTS = "points"; //$NON-NLS-1$ + +/** + * Returns the ConnectionRouter used to route this Connection. Does not return null. + * + * @return The ConnectionRouter for this Connection + */ +ConnectionRouter getConnectionRouter(); + +/** + * Sets the ConnectionRouter for this Connection. + * + * @param router The ConnectionRouter to set for this Connection + */ +void setConnectionRouter(ConnectionRouter router); + +/** + * Returns the ConnectionAnchor at the source end of this Connection. + * @return The ConnectionAnchor at the source end of this Connection + */ +ConnectionAnchor getSourceAnchor(); + +/** + * Returns the ConnectionAnchor at the target end of this Connection. + * @return The ConnectionAnchor at the target end of this Connection + */ +ConnectionAnchor getTargetAnchor(); + +/** + * Returns the routing constraint. May be null. + * @return The routing constraint + */ +Object getRoutingConstraint(); + +/** + * Sets the routing constraint used by the router. + * @param cons The routing constraint + */ +void setRoutingConstraint(Object cons); + +/** + * Sets the ConnectionAnchor to be used at the source end of this Connection. + * @param anchor The source anchor + */ +void setSourceAnchor(ConnectionAnchor anchor); + +/** + * Sets the ConnectionAnchor to be used at the target end of this Connection. + * @param anchor The target anchor + */ +void setTargetAnchor(ConnectionAnchor anchor); + +/** + * Returns the PointList containing the Points that make up this Connection. This may be + * returned by reference. + * @return The points for this Connection + */ +PointList getPoints(); + +/** + * Sets the PointList containing the Points that make up this Connection. + * @param list The points for this Connection + */ +void setPoints(PointList list); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ConnectionAnchor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ConnectionAnchor.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ConnectionAnchor; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.AnchorListener; +import dwtx.draw2d.IFigure; + +/** + * An object to which a {@link Connection} will be anchored. If the ConnectionAnchor + * moves, the Connection should move with it. + */ +public interface ConnectionAnchor { + +/** + * Adds a listener interested in the movement of this ConnectionAnchor. + * @param listener The AnchorListener to be added + */ +void addAnchorListener(AnchorListener listener); + +/** + * Returns the location where the Connection should be anchored in absolute coordinates. + * The anchor may use the given reference Point to calculate this location. + * @param reference The reference Point in absolute coordinates + * @return The anchor's location + */ +Point getLocation(Point reference); + +/** + * Returns the IFigure that contains this ConnectionAnchor. Moving this figure will cause + * the anchor to move with it. + * @return The IFigure that contains this ConnectionAnchor + */ +IFigure getOwner(); + +/** + * Returns the reference point for this anchor in absolute coordinates. This might be used + * by another anchor to determine its own location (i.e. {@link ChopboxAnchor}). + * @return The reference Point + */ +Point getReferencePoint(); + +/** + * Removes the listener. + * @param listener The AnchorListener to be removed + */ +void removeAnchorListener(AnchorListener listener); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ConnectionAnchorBase.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ConnectionAnchorBase.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ConnectionAnchorBase; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.ConnectionAnchor; +import dwtx.draw2d.AnchorListener; + +/** + * Provides support for a ConnectionAnchor. A ConnectionAnchor is one of the end points + * of a {@link Connection}. It holds listeners and notifies them if the anchor is moved. + */ +public abstract class ConnectionAnchorBase + : ConnectionAnchor +{ + +/** + * The list of listeners + */ +protected List listeners; + +this(){ + listeners = new ArrayList(1); +} + +/** + * @see dwtx.draw2d.ConnectionAnchor#addAnchorListener(AnchorListener) + */ +public void addAnchorListener(AnchorListener listener) { + listeners.add(cast(Object)listener); +} + +/** + * @see dwtx.draw2d.ConnectionAnchor#removeAnchorListener(AnchorListener) + */ +public void removeAnchorListener(AnchorListener listener) { + listeners.remove(cast(Object)listener); +} + +/** + * Notifies all the listeners in the list of a change in position of this anchor. This is + * called from one of the implementing anchors when its location is changed. + * + * @since 2.0 + */ +protected void fireAnchorMoved() { + Iterator iter = listeners.iterator(); + while (iter.hasNext()) + (cast(AnchorListener)iter.next()).anchorMoved(this); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ConnectionEndpointLocator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ConnectionEndpointLocator.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,282 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ConnectionEndpointLocator; + +import dwt.dwthelper.utils; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.Transposer; +import dwtx.draw2d.Locator; +import dwtx.draw2d.Connection; +import dwtx.draw2d.IFigure; + +/** + * Used to place IFigures along the endpoint or starting point of a {@link Connection}. + * uDistance represents the distance from the Connection's owner to the + * IFigure. vDistance represents the distance from the IFigure to the + * Connection itself. + */ +public class ConnectionEndpointLocator + : Locator +{ + +private bool end; +private Connection conn; +private int uDistance; +private int vDistance; +private static Rectangle figureBounds; + +/** + * Transposes the location if the connection point is along the top or bottom of its owner + * figure. + */ +protected Transposer transposer; + +/** + * Constructs a ConnectionEndpointLocator using the given {@link Connection}. If + * isEnd is true, the location is relative to the Connection's end (or + * target) point. If isEnd is false, the location is relative to the + * Connection's start (or source) point. + * + * @param c The Connection + * @param isEnd true is location is relative to end point + * @since 2.0 + */ +public this(Connection c, bool isEnd) { + transposer = new Transposer(); + end = isEnd; + conn = c; + uDistance = 14; + vDistance = 4; + figureBounds = new Rectangle(); +} + +/* + * Returns an integer representing the side of the passed Rectangle that a point lies on. + * 1 is Top + * 2 is Right + * 3 is Bottom + * 4 is Left + * + * @param loc The point that is to be located + */ +private int calculateConnectionLocation(Point loc, Point topLeft, Point center) { + double m1, m2 = 0; + m1 = cast(double)(topLeft.y - center.y) / cast(double)(topLeft.x - center.x); + + if (loc.x - center.x !is 0) + m2 = cast(double)(loc.y - center.y) / cast(double)(loc.x - center.x); + + if (loc.x is center.x) { + // Case where m2 is vertical + if (loc.y < center.y) + return 3; + else + return 1; + } else if (Math.abs(m2) <= Math.abs(m1)) { + // Connection start point along left or right side + if (loc.x < center.x) + return 4; + else + return 2; + } else { + // Connection start point along top or bottom + if (loc.y < center.y) + return 3; + else + return 1; + } +} + +/* + * This method is used to calculate the "quadrant" value of a connection that does not + * have an owner on its starting point. + * + * 1 is Top + * 2 is Right + * 3 is Bottom + * 4 is Left + * + * @param startPoint The starting point of the connection. + * @param endPoint The end point of the connection. + */ +private int calculateConnectionLocation(Point startPoint, Point endPoint) { + if (Math.abs(endPoint.x - startPoint.x) > Math.abs(endPoint.y - startPoint.y)) { + if (endPoint.x > startPoint.x) + return 2; + else + return 4; + } else { + if (endPoint.y > startPoint.y) + return 1; + else + return 3; + } +} + +/* + * Calculates 'tan' which is used as a factor for y adjustment when placing the connection + * label. 'tan' is capped at 1.0 in the positive direction and -1.0 in the negative + * direction. + * + * @param startPoint The starting point of the connection. + * @param endPoint The end point of the connection. + * @since 2.0 + */ +private double calculateTan(Point startPoint, Point endPoint) { + double tan = 0; + if (endPoint.x is startPoint.x) + tan = 1.0; + else + tan = cast(double)(endPoint.y - startPoint.y) + / cast(double)(endPoint.x - startPoint.x); + if (tan > 1) + tan = 1.0; + else if (tan < -1) + tan = -1.0; + + return tan; +} + +private int calculateYShift(int figureWidth, int figureHeight) { + int yShift = 0; + if (vDistance < 0) + yShift = -figureHeight; + else if (vDistance is 0) + yShift = -figureHeight / 2; + return yShift; +} + +private Connection getConnection() { + return conn; +} + +private IFigure getConnectionOwner() { + IFigure connOwner; + if (isEnd()) + connOwner = conn.getTargetAnchor().getOwner(); + else + connOwner = conn.getSourceAnchor().getOwner(); + + return connOwner; +} + +/** + * Returns the distance in pixels from the anchor's owner. + * @return the offset distance from the endpoint figure + */ +public int getUDistance() { + return uDistance; +} + +/** + * Returns the distance in pixels from the connection + * @return the offset from the connection itself + */ +public int getVDistance() { + return vDistance; +} + +private bool isEnd() { + return end; +} + +/** + * Relocates the given IFigure at either the source or target end of the Connection, + * based on the bool given in the constructor + * {@link #ConnectionEndpointLocator(Connection, bool)}. + * + * @param figure The figure to relocate + */ +public void relocate(IFigure figure) { + Connection conn = getConnection(); + Point startPoint = Point.SINGLETON; + Point endPoint = new Point(); + + int startPointPosition = 0; + int endPointPosition = 1; + if (isEnd()) { + startPointPosition = conn.getPoints().size() - 1; + endPointPosition = startPointPosition - 1; + } + + conn.getPoints().getPoint(startPoint, startPointPosition); + conn.getPoints().getPoint(endPoint, endPointPosition); + + IFigure connOwner = getConnectionOwner(); + + int quadrant; + if (connOwner !is null) { + Rectangle connOwnerBounds = connOwner.getBounds(); + Point connOwnerCenter = connOwnerBounds.getCenter(); + Point connOwnerTL = connOwnerBounds.getTopLeft(); + quadrant = calculateConnectionLocation(startPoint, connOwnerTL, connOwnerCenter); + } else + quadrant = calculateConnectionLocation(startPoint, endPoint); + + int cos = 1; + transposer.setEnabled(false); + + /* + * Label placement calculations are done as if the connection point is along the left + * or right side of the figure. If the connection point is along the top or bottom, + * values are transposed. + */ + if (quadrant is 1 || quadrant is 3) + transposer.setEnabled(true); + + if (quadrant is 3 || quadrant is 4) + cos = -1; + + Dimension figureSize = transposer.t(figure.getPreferredSize()); + startPoint = transposer.t(startPoint); + endPoint = transposer.t(endPoint); + + double tan = calculateTan(startPoint, endPoint); + + int figureWidth = figureSize.width; + int figureHeight = figureSize.height; + int yShift = calculateYShift(figureWidth, figureHeight); + + Point figurePoint = + new Point(startPoint.x + (uDistance * cos) + figureWidth * ((cos - 1) / 2), + cast(int)(startPoint.y + cos * uDistance * tan + vDistance + yShift)); + + figureBounds.setSize(transposer.t(figureSize)); + figureBounds.setLocation(transposer.t(figurePoint)); + figure.setBounds(figureBounds); +} + +/** + * Sets the distance in pixels from the Connection's owner. + * + * @param distance Number of pixels to place the ConnectionEndpointLocator from its owner. + * @since 2.0 + */ +public void setUDistance(int distance) { + uDistance = distance; +} + +/** + * Sets the distance in pixels from the Connection. + * + * @param distance Number of pixels to place the ConnectionEndpointLocator from its + * Connection. + * @since 2.0 + */ +public void setVDistance(int distance) { + vDistance = distance; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ConnectionLayer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ConnectionLayer.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ConnectionLayer; + +import dwt.dwthelper.utils; + +import dwt.DWT; +import dwtx.draw2d.FreeformLayer; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.ConnectionRouter; +import dwtx.draw2d.Connection; + +/** + * Layer designed specifically to handle the presence of connections. This is done due to + * the necessity of having a router for the connections added. + */ +public class ConnectionLayer + : FreeformLayer +{ + +int antialias = DWT.DEFAULT; + +/** + * The ConnectionRouter used to route all connections on this layer. + */ +protected ConnectionRouter connectionRouter; + +/** + * Adds the given figure with the given contraint at the given index. If the figure is a + * {@link Connection}, its {@link ConnectionRouter} is set. + * + * @param figure Figure being added + * @param constraint Constraint of the figure being added + * @param index Index where the figure is to be added + * @since 2.0 + */ +public void add(IFigure figure, Object constraint, int index) { + super.add(figure, constraint, index); + + // If the connection layout manager is set, then every + // figure added should use this layout manager. + if (null !is cast(Connection )figure && getConnectionRouter() !is null) + (cast(Connection)figure).setConnectionRouter(getConnectionRouter()); +} + +/** + * Returns the ConnectionRouter being used by this layer. + * + * @return ConnectionRouter being used by this layer + * @since 2.0 + */ +public ConnectionRouter getConnectionRouter() { + return connectionRouter; +} + +/** + * @see IFigure#paint(Graphics) + */ +public void paint(Graphics graphics) { + if (antialias !is DWT.DEFAULT) + graphics.setAntialias(antialias); + super.paint(graphics); +} + +/** + * Removes the figure from this Layer. If the figure is a {@link Connection}, that + * Connection's {@link ConnectionRouter} is set to null. + * + * @param figure The figure to remove + */ +public void remove(IFigure figure) { + if ( auto f = cast(Connection)figure ) + f.setConnectionRouter(null); + super.remove(figure); +} + +/** + * Sets the ConnectionRouter for this layer. This router is set as the ConnectionRouter + * for all the child connections of this Layer. + * + * @param router The ConnectionRouter to set for this Layer + * @since 2.0 + */ +public void setConnectionRouter(ConnectionRouter router) { + connectionRouter = router; + FigureIterator iter = new FigureIterator(this); + IFigure figure; + while (iter.hasNext()) { + figure = iter.nextFigure(); + if ( auto f = cast(Connection)figure ) + f.setConnectionRouter(router); + } +} + +/** + * Sets whether antialiasing should be enabled for the connection layer. If this value is + * set to something other than {@link DWT#DEFAULT}, {@link Graphics#setAntialias(int)} + * will be called with the given value when painting this layer. + * @param antialias the antialias setting + * @since 3.1 + */ +public void setAntialias(int antialias) { + this.antialias = antialias; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ConnectionLocator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ConnectionLocator.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ConnectionLocator; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.AbstractLocator; +import dwtx.draw2d.Connection; + +/** + * Repositions a {@link Figure} attached to a {@link Connection} when the Connection is + * moved. Provides for alignment at the start (source), middle, or end (target) of the + * Connection. + */ +public class ConnectionLocator + : AbstractLocator +{ + +/** @deprecated Use {@link #SOURCE} */ +public static const int START = 2; +/** The start (or source) of the Connection */ +public static const int SOURCE = 2; + +/** @deprecated Use {@link #TARGET} */ +public static const int END = 3; +/** The end (or target) of the Connection */ +public static const int TARGET = 3; + +/** + * @deprecated Use {@link #MIDDLE} instead, since the location is not the midpoint of a + * line-segment, but the middle of a polyline. + */ +public static const int MIDPOINT = 4; +/** The middle of the Connection */ +public static const int MIDDLE = 4; + +private Connection connection; +private int alignment; + +/** + * Constructs a ConnectionLocator with the passed connection and {@link #MIDDLE} + * alignment. + * + * @param connection The Connection + * @since 2.0 + */ +public this(Connection connection) { + this(connection, MIDDLE); +} + +/** + * Constructs a ConnectionLocator with the passed Connection and alignment. Valid values + * for the alignment are integer constants {@link #SOURCE}, {@link #MIDDLE}, and + * {@link #TARGET}. + * + * @param connection The Connection + * @param align The alignment + * + * @since 2.0 + */ +public this(Connection connection, int align_) { + setConnection(connection); + setAlignment(align_); +} + +/** + * Returns the alignment of ConnectionLocator. + * + * @return The alignment + * @since 2.0 + */ +public int getAlignment() { + return alignment; +} + +/** + * Returns connection associated with ConnectionLocator. + * + * @return The Connection + * @since 2.0 + */ +protected Connection getConnection() { + return connection; +} + +/** + * Returns ConnectionLocator's reference point in absolute coordinates. + * + * @return The reference point + * @since 2.0 + */ +protected Point getReferencePoint() { + Point p = getLocation(getConnection().getPoints()); + getConnection().translateToAbsolute(p); + return p; +} + +/** + * Returns a point from the passed PointList, dependent on ConnectionLocator's alignment. + * If the alignment is {@link #SOURCE}, it returns the first point in points. If + * {@link #TARGET}, it returns the last point in points. If {@link #MIDDLE}, it + * returns the middle of line represented by points. + * + * @param points The points in the Connection + * @return The location + * @since 2.0 + */ +protected Point getLocation(PointList points) { + switch (getAlignment()) { + case SOURCE: + return points.getPoint(Point.SINGLETON, 0); + case TARGET: + return points.getPoint(Point.SINGLETON, points.size() - 1); + case MIDDLE: + if (points.size() % 2 is 0) { + int i = points.size() / 2; + int j = i - 1; + Point p1 = points.getPoint(j); + Point p2 = points.getPoint(i); + Dimension d = p2.getDifference(p1); + return Point.SINGLETON.setLocation(p1.x + d.width / 2, + p1.y + d.height / 2); + } + int i = (points.size() - 1) / 2; + return points.getPoint(Point.SINGLETON, i); + default: + return new Point(); + } +} + +/** + * Sets the alignment. Possible values are {@link #SOURCE}, {@link #MIDDLE}, and + * {@link #TARGET}. + * + * @param align The alignment + * @since 2.0 + */ +protected void setAlignment(int align_) { + alignment = align_; +} + +/** + * Sets the Connection to be associated with this ConnectionLocator. + * + * @param connection The Connection + * @since 2.0 + */ +protected void setConnection(Connection connection) { + this.connection = connection; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ConnectionRouter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ConnectionRouter.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ConnectionRouter; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.AnchorListener; +import dwtx.draw2d.AbstractRouter; +import dwtx.draw2d.Connection; + +/** + * Routes a {@link Connection}, possibly using a constraint. + */ +public interface ConnectionRouter { + +/** + * Returns the constraint for the Connection. + * @param connection The connection + * @return The constraint + */ +Object getConstraint(Connection connection); + +/** + * Invalidates the given Connection. + * @param connection The connection to be invalidated + */ +void invalidate(Connection connection); + +/** + * Routes the Connection. + * @param connection The Connection to route + */ +void route(Connection connection); + +/** + * Removes the Connection from this router. + * @param connection The Connection to remove + */ +void remove(Connection connection); + +/** + * Maps the given constraint to the given Connection. + * @param connection The Connection + * @param constraint The constraint + */ +void setConstraint(Connection connection, Object constraint); + + +} +/** + * Routes Connections directly from the source anchor to the target anchor with no + * bendpoints in between. + */ +class NullConnectionRouter + : AbstractRouter +{ + + /** + * Constructs a new NullConnectionRouter. + */ + this() { } + + /** + * Routes the given Connection directly between the source and target anchors. + * @param conn the connection to be routed + */ + public void route(Connection conn) { + PointList points = conn.getPoints(); + points.removeAllPoints(); + Point p; + conn.translateToRelative(p = getStartPoint(conn)); + points.addPoint(p); + conn.translateToRelative(p = getEndPoint(conn)); + points.addPoint(p); + conn.setPoints(points); + } +} + +/** + * The default router for Connections. + */ +public static const ConnectionRouter ConnectionRouter_NULL; + +static this(){ + ConnectionRouter_NULL = new NullConnectionRouter(); +} + diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/CoordinateListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/CoordinateListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.CoordinateListener; + +import dwt.dwthelper.utils; +import dwtx.draw2d.IFigure; + +/** + * @since 3.1 + */ +public interface CoordinateListener { + +/** + * Indicates that the coordinate system has changed in a way that affects the absolute + * locations of contained figures. + * @param source the figure whose coordinate system changed + * @since 3.1 + */ +void coordinateSystemChanged(IFigure source); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Cursors.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Cursors.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Cursors; + +import dwt.dwthelper.utils; + +import dwt.DWT; +import dwt.graphics.Cursor; +import dwtx.draw2d.PositionConstants; + +/** + * A collection of cursors. + */ +public class Cursors { + +/** + * Returns the cursor corresponding to the given direction, defined in + * {@link PositionConstants}. Note that {@link #getDirectionalCursor(int, bool)} should + * be used for applications which want to run properly when running in a mirrored + * environment. The behavior is the same as calling {@link #getDirectionalCursor(int, + * bool) getDirectionalCursor(direction, false)}. + * + * @param direction the relative direction of the desired cursor + * @return The appropriate directional cursor + */ +public static Cursor getDirectionalCursor(int direction) { + return getDirectionalCursor(direction, false); +} + +/** + * Returns the cursor corresponding to the given direction and mirroring. The direction + * must be one of: + *
    + *
  • {@link PositionConstants#NORTH} + *
  • {@link PositionConstants#SOUTH} + *
  • {@link PositionConstants#EAST} + *
  • {@link PositionConstants#WEST} + *
  • {@link PositionConstants#NORTH_EAST} + *
  • {@link PositionConstants#NORTH_WEST} + *
  • {@link PositionConstants#SOUTH_EAST} + *
  • {@link PositionConstants#SOUTH_WEST} + *
+ *

The behavior is undefined for other values. If isMirrored is set to + * true, EAST and WEST will be inverted. + * @param direction the relative direction of the desired cursor + * @param isMirrored true if EAST and WEST should be inverted + * @return The appropriate directional cursor + */ +public static Cursor getDirectionalCursor(int direction, bool isMirrored) { + if (isMirrored && (direction & PositionConstants.EAST_WEST) !is 0) + direction = direction ^ PositionConstants.EAST_WEST; + switch (direction) { + case PositionConstants.NORTH : + return SIZEN; + case PositionConstants.SOUTH: + return SIZES; + case PositionConstants.EAST : + return SIZEE; + case PositionConstants.WEST: + return SIZEW; + case PositionConstants.SOUTH_EAST: + return SIZESE; + case PositionConstants.SOUTH_WEST: + return SIZESW; + case PositionConstants.NORTH_EAST: + return SIZENE; + case PositionConstants.NORTH_WEST: + return SIZENW; + default: + break; + } + return null; +} + +/** + * @see DWT#CURSOR_ARROW + */ +public static const Cursor ARROW; +/** + * @see DWT#CURSOR_SIZEN + */ +public static const Cursor SIZEN; +/** + * @see DWT#CURSOR_SIZENE + */ +public static const Cursor SIZENE; +/** + * @see DWT#CURSOR_SIZEE + */ +public static const Cursor SIZEE; +/** + * @see DWT#CURSOR_SIZESE + */ +public static const Cursor SIZESE; +/** + * @see DWT#CURSOR_SIZES + */ +public static const Cursor SIZES; +/** + * @see DWT#CURSOR_SIZESW + */ +public static const Cursor SIZESW; +/** + * @see DWT#CURSOR_SIZEW + */ +public static const Cursor SIZEW; +/** + * @see DWT#CURSOR_SIZENW + */ +public static const Cursor SIZENW; +/** + * @see DWT#CURSOR_APPSTARTING + */ +public static const Cursor APPSTARTING; +/** + * @see DWT#CURSOR_CROSS + */ +public static const Cursor CROSS; +/** + * @see DWT#CURSOR_HAND + */ +public static const Cursor HAND; +/** + * @see DWT#CURSOR_HELP + */ +public static const Cursor HELP; +/** + * @see DWT#CURSOR_IBEAM + */ +public static const Cursor IBEAM; +/** + * @see DWT#CURSOR_NO + */ +public static const Cursor NO; +/** + * @see DWT#CURSOR_SIZEALL + */ +public static const Cursor SIZEALL; +/** + * @see DWT#CURSOR_SIZENESW + */ +public static const Cursor SIZENESW; +/** + * @see DWT#CURSOR_SIZENWSE + */ +public static const Cursor SIZENWSE; +/** + * @see DWT#CURSOR_SIZEWE + */ +public static const Cursor SIZEWE; +/** + * @see DWT#CURSOR_SIZENS + */ +public static const Cursor SIZENS; +/** + * @see DWT#CURSOR_UPARROW + */ +public static const Cursor UPARROW; +/** + * @see DWT#CURSOR_WAIT + */ +public static const Cursor WAIT; + +static this() { + ARROW = new Cursor(null, DWT.CURSOR_ARROW); + SIZEN = new Cursor(null, DWT.CURSOR_SIZEN); + SIZENE = new Cursor(null, DWT.CURSOR_SIZENE); + SIZEE = new Cursor(null, DWT.CURSOR_SIZEE); + SIZESE = new Cursor(null, DWT.CURSOR_SIZESE); + SIZES = new Cursor(null, DWT.CURSOR_SIZES); + SIZESW = new Cursor(null, DWT.CURSOR_SIZESW); + SIZEW = new Cursor(null, DWT.CURSOR_SIZEW); + SIZENW = new Cursor(null, DWT.CURSOR_SIZENW); + SIZENS = new Cursor(null, DWT.CURSOR_SIZENS); + SIZEWE = new Cursor(null, DWT.CURSOR_SIZEWE); + APPSTARTING = new Cursor(null, DWT.CURSOR_APPSTARTING); + CROSS = new Cursor(null, DWT.CURSOR_CROSS); + HAND = new Cursor(null, DWT.CURSOR_HAND); + HELP = new Cursor(null, DWT.CURSOR_HELP); + IBEAM = new Cursor(null, DWT.CURSOR_IBEAM); + NO = new Cursor(null, DWT.CURSOR_NO); + SIZEALL = new Cursor(null, DWT.CURSOR_SIZEALL); + SIZENESW = new Cursor(null, DWT.CURSOR_SIZENESW); + SIZENWSE = new Cursor(null, DWT.CURSOR_SIZENWSE); + UPARROW = new Cursor(null, DWT.CURSOR_UPARROW); + WAIT = new Cursor(null, DWT.CURSOR_WAIT); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/DefaultRangeModel.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/DefaultRangeModel.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.DefaultRangeModel; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Bean; +import tango.text.convert.Format; + +import dwtx.draw2d.RangeModel; + +/** + * Generic implementation for a RangeModel. + * + * + *

+ *                    |<----extent--->|
+ *    ----|-----------|---------------|---------------|----
+ *       min          |                              max
+ *                  value
+ * 
+ */ +public class DefaultRangeModel + : RangeModel +{ + +/** + * Listeners interested in the range model's property changes. + */ +protected PropertyChangeSupport propertyListeners; +private int minimum = 0; +private int maximum = 100; +private int extent = 20; +private int value = 0; + +public this(){ + propertyListeners = new PropertyChangeSupport(this); +} + +/** + * Registers the given listener as a PropertyChangeListener. + * + * @param listener the listener to be added + * @since 2.0 + */ +public void addPropertyChangeListener(PropertyChangeListener listener) { + propertyListeners.addPropertyChangeListener(listener); +} + +/** + * Notifies any listening PropertyChangeListeners that the property with the given id has + * changed. + * + * @param string the property name + * @param oldValue the old value + * @param newValue the new value + * @since 2.0 + */ +protected void firePropertyChange(String string, int oldValue, int newValue) { + propertyListeners.firePropertyChange(string, oldValue, newValue); +} + +/** + * @return the extent + */ +public int getExtent() { + return extent; +} + +/** + * @return the maximum value + */ +public int getMaximum() { + return maximum; +} + +/** + * @return the minimum value + */ +public int getMinimum() { + return minimum; +} + +/** + * @return the current value + */ +public int getValue() { + return value; +} + +/** + * @return whether the extent is between the minimum and maximum values + */ +public bool isEnabled() { + return (getMaximum() - getMinimum()) > getExtent(); +} + +/** + * Removes the given PropertyChangeListener from the list of listeners. + * + * @param listener the listener to be removed + */ +public void removePropertyChangeListener(PropertyChangeListener listener) { + propertyListeners.removePropertyChangeListener(listener); +} + +/** + * @see dwtx.draw2d.RangeModel#setAll(int, int, int) + */ +public void setAll(int min, int ext, int max) { + int oldMin = minimum; + int oldExtent = extent; + int oldMax = maximum; + maximum = max; + minimum = min; + extent = ext; + if (oldMax !is max) + firePropertyChange(PROPERTY_MAXIMUM, oldMax, max); + if (oldExtent !is ext) + firePropertyChange(PROPERTY_EXTENT, oldExtent, ext); + if (oldMin !is min) + firePropertyChange(PROPERTY_MINIMUM, oldMin, min); + setValue(getValue()); +} + +/** + * Sets this RangeModel's extent and fires a property change if the given value is + * different from the current extent. + * + * @param extent the new extent value + */ +public void setExtent(int extent) { + if (this.extent is extent) + return; + int oldValue = this.extent; + this.extent = extent; + firePropertyChange(PROPERTY_EXTENT, oldValue, extent); + setValue(getValue()); +} + +/** + * Sets this RangeModel's maximum value and fires a property change if the given value is + * different from the current maximum value. + * + * @param maximum the new maximum value + */ +public void setMaximum(int maximum) { + if (this.maximum is maximum) + return; + int oldValue = this.maximum; + this.maximum = maximum; + firePropertyChange(PROPERTY_MAXIMUM, oldValue, maximum); + setValue(getValue()); +} + +/** + * Sets this RangeModel's minimum value and fires a property change if the given value is + * different from the current minimum value. + * + * @param minimum the new minumum value + */ +public void setMinimum(int minimum) { + if (this.minimum is minimum) + return; + int oldValue = this.minimum; + this.minimum = minimum; + firePropertyChange(PROPERTY_MINIMUM, oldValue, minimum); + setValue(getValue()); +} + +/** + * Sets this RangeModel's current value. If the given value is greater than the maximum, + * the maximum value is used. If the given value is less than the minimum, the minimum + * value is used. If the adjusted value is different from the current value, a property + * change is fired. + * + * @param value the new value + */ +public void setValue(int value) { + value = Math.max(getMinimum(), Math.min(getMaximum() - getExtent(), value)); + if (this.value is value) + return; + int oldValue = this.value; + this.value = value; + firePropertyChange(PROPERTY_VALUE, oldValue, value); +} + +/** + * @see java.lang.Object#toString() + */ +public String toString() { + return Format( "{} ({}, {}, {}, {})", super.toString(), minimum, maximum //$NON-NLS-2$ //$NON-NLS-1$ + , extent, value ); //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/DeferredUpdateManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/DeferredUpdateManager.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,339 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.DeferredUpdateManager; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; +import dwtx.dwtxhelper.Collection; + +import dwt.DWT; +import dwt.DWTException; +import dwt.graphics.GC; +import dwt.widgets.Display; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.UpdateManager; +import dwtx.draw2d.GraphicsSource; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.SWTGraphics; + +/** + * An UpdateManager that asynchronously updates the affected figures. + */ +public class DeferredUpdateManager + : UpdateManager +{ +alias UpdateManager.addDirtyRegion addDirtyRegion; + +/** + * Calls {@link DeferredUpdateManager#performUpdate()}. + */ +protected class UpdateRequest + : Runnable +{ + /** + * Calls {@link DeferredUpdateManager#performUpdate()}. + */ + public void run() { + performUpdate(); + } +} +private Rectangle damage; +private Map dirtyRegions; + +private GraphicsSource graphicsSource; +private List invalidFigures; +private IFigure root; +private bool updateQueued; + +private bool updating; +private bool validating; +private RunnableChain afterUpdate; + +private static class RunnableChain { + RunnableChain next; + Runnable run_; + + this(Runnable run_, RunnableChain next) { + this.run_ = run_; + this.next = next; + } + + void run() { + if (next !is null) + next.run(); + run_.run(); + } +} + +/** + * Empty constructor. + */ +public this() { + invalidFigures = new ArrayList(); + dirtyRegions = new HashMap(); +} + +/** + * Constructs a new DererredUpdateManager with the given GraphicsSource. + * @param gs the graphics source + */ +public this(GraphicsSource gs) { + this(); + setGraphicsSource(gs); +} + +/** + * Adds a dirty region (defined by the rectangle x, y, w, h) to the update queue. + * If the figure isn't visible or either the width or height are 0, the method returns + * without queueing the dirty region. + * + * @param figure the figure that contains the dirty region + * @param x the x coordinate of the dirty region + * @param y the y coordinate of the dirty region + * @param w the width of the dirty region + * @param h the height of the dirty region + */ +public synchronized void addDirtyRegion(IFigure figure, int x, int y, int w, int h) { + if (w is 0 || h is 0 || !figure.isShowing()) + return; + + Rectangle rect = cast(Rectangle)dirtyRegions.get(cast(Object)figure); + if (rect is null) { + rect = new Rectangle(x, y, w, h); + dirtyRegions.put(cast(Object)figure, rect); + } else + rect.union_(x, y, w, h); + + queueWork(); +} + +/** + * Adds the given figure to the update queue. Invalid figures will be validated before + * the damaged regions are repainted. + * + * @param f the invalid figure + */ +public synchronized void addInvalidFigure(IFigure f) { + if (invalidFigures.contains(cast(Object)f)) + return; + queueWork(); + invalidFigures.add(cast(Object)f); +} + +/** + * Returns a Graphics object for the given region. + * @param region the region to be repainted + * @return the Graphics object + */ +protected Graphics getGraphics(Rectangle region) { + if (graphicsSource is null) + return null; + return graphicsSource.getGraphics(region); +} + +void paint(GC gc) { + if (!validating) { + SWTGraphics graphics = new SWTGraphics(gc); + if (!updating) { + /** + * If a paint occurs not as part of an update, we should notify that the region + * is being painted. Otherwise, notification already occurs in repairDamage(). + */ + Rectangle rect = graphics.getClip(new Rectangle()); + HashMap map = new HashMap(); + map.put(cast(Object)root, rect); + firePainting(rect, map); + } + performValidation(); + root.paint(graphics); + graphics.dispose(); + } else { + /* + * If figures are being validated then we can simply + * add a dirty region here and update will repaint this region with other + * dirty regions when it gets to painting. We can't paint if we're not sure + * that all figures are valid. + */ + addDirtyRegion(root, new Rectangle(gc.getClipping())); + } +} + +/** + * Performs the update. Validates the invalid figures and then repaints the dirty + * regions. + * @see #validateFigures() + * @see #repairDamage() + */ +public synchronized void performUpdate() { + if (isDisposed() || updating) + return; + updating = true; + try { + performValidation(); + updateQueued = false; + repairDamage(); + if (afterUpdate !is null) { + RunnableChain chain = afterUpdate; + afterUpdate = null; + chain.run(); //chain may queue additional Runnable. + if (afterUpdate !is null) + queueWork(); + } + } finally { + updating = false; + } +} + +/** + * @see UpdateManager#performValidation() + */ +public void performValidation() { + if (invalidFigures.isEmpty() || validating) + return; + try { + IFigure fig; + validating = true; + fireValidating(); + for (int i = 0; i < invalidFigures.size(); i++) { + fig = cast(IFigure) invalidFigures.get(i); + invalidFigures.set(i, null); + fig.validate(); + } + } finally { + invalidFigures.clear(); + validating = false; + } +} + +/** + * Adds the given exposed region to the update queue and then performs the update. + * + * @param exposed the exposed region + */ +public synchronized void performUpdate(Rectangle exposed) { + addDirtyRegion(root, exposed); + performUpdate(); +} + +/** + * Posts an {@link UpdateRequest} using {@link Display#asyncExec(Runnable)}. If work has + * already been queued, a new request is not needed. + */ +protected void queueWork() { + if (!updateQueued) { + sendUpdateRequest(); + updateQueued = true; + } +} + +/** + * Fires the UpdateRequest to the current display asynchronously. + * @since 3.2 + */ +protected void sendUpdateRequest() { + Display display = Display.getCurrent(); + if (display is null) { + throw new DWTException(DWT.ERROR_THREAD_INVALID_ACCESS); + } + display.asyncExec(new UpdateRequest()); +} + +/** + * Releases the graphics object, which causes the GraphicsSource to flush. + * @param graphics the graphics object + */ +protected void releaseGraphics(Graphics graphics) { + graphics.dispose(); + graphicsSource.flushGraphics(damage); +} + +/** + * Repaints the dirty regions on the update queue and calls + * {@link UpdateManager#firePainting(Rectangle, Map)}, unless there are no dirty regions. + */ +protected void repairDamage() { + Iterator keys = dirtyRegions.keySet().iterator(); + Rectangle contribution; + IFigure figure; + IFigure walker; + + while (keys.hasNext()) { + figure = cast(IFigure)keys.next(); + walker = figure.getParent(); + contribution = cast(Rectangle)dirtyRegions.get(cast(Object)figure); + //A figure can't paint beyond its own bounds + contribution.intersect(figure.getBounds()); + while (!contribution.isEmpty() && walker !is null) { + walker.translateToParent(contribution); + contribution.intersect(walker.getBounds()); + walker = walker.getParent(); + } + if (damage is null) + damage = new Rectangle(contribution); + else + damage.union_(contribution); + } + + if (!dirtyRegions.isEmpty()) { + Map oldRegions = dirtyRegions; + dirtyRegions = new HashMap(); + firePainting(damage, oldRegions); + } + + if (damage !is null && !damage.isEmpty()) { + //ystem.out.println(damage); + Graphics graphics = getGraphics(damage); + if (graphics !is null) { + root.paint(graphics); + releaseGraphics(graphics); + } + } + damage = null; +} + +/** + * Adds the given runnable and queues an update if an update is not under progress. + * @param runnable the runnable + */ +public synchronized void runWithUpdate(Runnable runnable) { + afterUpdate = new RunnableChain(runnable, afterUpdate); + if (!updating) + queueWork(); +} + +/** + * Sets the graphics source. + * @param gs the graphics source + */ +public void setGraphicsSource(GraphicsSource gs) { + graphicsSource = gs; +} + +/** + * Sets the root figure. + * @param figure the root figure + */ +public void setRoot(IFigure figure) { + root = figure; +} + +/** + * Validates all invalid figures on the update queue and calls + * {@link UpdateManager#fireValidating()} unless there are no invalid figures. + */ +protected void validateFigures() { + performValidation(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/DelegatingLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/DelegatingLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.DelegatingLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.AbstractLayout; +import dwtx.draw2d.Locator; + +/** + * Figures using a DelegatingLayout as their layout manager give + * location responsibilities to their children. The children + * of a Figure using a DelegatingLayout should have a + * {@link Locator Locator} as a constraint whose + * {@link Locator#relocate(IFigure target) relocate} method is + * responsible for placing the child. + */ +public class DelegatingLayout + : AbstractLayout +{ + +private Map constraints; + +this(){ + constraints = new HashMap(); +} +/** + * Calculates the preferred size of the given Figure. + * For the DelegatingLayout, this is the largest width and height + * values of the passed Figure's children. + * + * @param parent the figure whose preferred size is being calculated + * @param wHint the width hint + * @param hHint the height hint + * @return the preferred size + * @since 2.0 + */ +protected Dimension calculatePreferredSize(IFigure parent, int wHint, int hHint) { + List children = parent.getChildren(); + Dimension d = new Dimension(); + for (int i = 0; i < children.size(); i++) { + IFigure child = cast(IFigure)children.get(i); + d.union_(child.getPreferredSize()); + } + return d; +} + +/** + * @see dwtx.draw2d.LayoutManager#getConstraint(dwtx.draw2d.IFigure) + */ +public Object getConstraint(IFigure child) { + return constraints.get(cast(Object)child); +} + +/** + * Lays out the given figure's children based on their {@link Locator} constraint. + * @param parent the figure whose children should be layed out + */ +public void layout(IFigure parent) { + List children = parent.getChildren(); + for (int i = 0; i < children.size(); i++) { + IFigure child = cast(IFigure)children.get(i); + Locator locator = cast(Locator)constraints.get(cast(Object)child); + if (locator !is null) { + locator.relocate(child); + } + } +} + +/** + * Removes the locator for the given figure. + * @param child the child being removed + */ +public void remove(IFigure child) { + constraints.remove(cast(Object)child); +} + +/** + * Sets the constraint for the given figure. + * @param figure the figure whose contraint is being set + * @param constraint the new constraint + */ +public void setConstraint(IFigure figure, Object constraint) { + super.setConstraint(figure, constraint); + if (constraint !is null) + constraints.put(cast(Object)figure, constraint); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Ellipse.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Ellipse.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + * Alex Selkov - Fix for Bug# 22701 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.draw2d.Ellipse; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Shape; +import dwtx.draw2d.Graphics; + +/** + * An figure that draws an ellipse filling its bounds. + */ +public class Ellipse + : Shape +{ + +/** + * Constructs a new Ellipse with the default values of a Shape. + * @since 2.0 + */ +public this() { } + +/** + * Returns true if the given point (x,y) is contained within this ellipse. + * @param x the x coordinate + * @param y the y coordinate + * @return trueif the given point is contained + */ +public bool containsPoint(int x, int y) { + if (!super.containsPoint(x, y)) + return false; + Rectangle r = getBounds(); + long ux = x - r.x - r.width / 2; + long uy = y - r.y - r.height / 2; + return ((ux * ux) << 10) / (r.width * r.width) + + ((uy * uy) << 10) / (r.height * r.height) <= 256; +} + +/** + * Fills the ellipse. + * @see dwtx.draw2d.Shape#fillShape(dwtx.draw2d.Graphics) + */ +protected void fillShape(Graphics graphics) { + graphics.fillOval(getBounds()); +} + +/** + * Outlines the ellipse. + * @see dwtx.draw2d.Shape#outlineShape(dwtx.draw2d.Graphics) + */ +protected void outlineShape(Graphics graphics) { + Rectangle r = Rectangle.SINGLETON; + r.setBounds(getBounds()); + r.width--; + r.height--; + r.shrink((lineWidth - 1) / 2, (lineWidth - 1) / 2); + graphics.drawOval(r); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/EllipseAnchor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/EllipseAnchor.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.EllipseAnchor; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.AbstractConnectionAnchor; +import dwtx.draw2d.IFigure; + +/** + * Similar to a {@link dwtx.draw2d.ChopboxAnchor}, except this anchor is located on + * the ellipse defined by the owners bounding box. + * @author Alex Selkov + * Created 31.08.2002 23:11:43 + */ +public class EllipseAnchor : AbstractConnectionAnchor { + +/** + * @see dwtx.draw2d.AbstractConnectionAnchor#AbstractConnectionAnchor() + */ +public this() { } + +/** + * @see dwtx.draw2d.AbstractConnectionAnchor#AbstractConnectionAnchor(IFigure) + */ +public this(IFigure owner) { + super(owner); +} + +/** + * Returns a point on the ellipse (defined by the owner's bounding box) where the + * connection should be anchored. + * @see dwtx.draw2d.ConnectionAnchor#getLocation(Point) + */ +public Point getLocation(Point reference) { + Rectangle r = Rectangle.SINGLETON; + r.setBounds(getOwner().getBounds()); + r.translate(-1, -1); + r.resize(1, 1); + getOwner().translateToAbsolute(r); + + Point ref_ = r.getCenter().negate().translate(reference); + + if (ref_.x is 0) + return new Point(reference.x, (ref_.y > 0) ? r.bottom() : r.y); + if (ref_.y is 0) + return new Point((ref_.x > 0) ? r.right() : r.x, reference.y); + + float dx = (ref_.x > 0) ? 0.5f : -0.5f; + float dy = (ref_.y > 0) ? 0.5f : -0.5f; + + // ref.x, ref.y, r.width, r.height !is 0 => safe to proceed + + float k = cast(float)(ref_.y * r.width) / (ref_.x * r.height); + k = k * k; + + return r.getCenter().translate(cast(int)(r.width * dx / Math.sqrt(1 + k)), + cast(int)(r.height * dy / Math.sqrt(1 + 1 / k))); +} +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/EventDispatcher.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/EventDispatcher.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,190 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.EventDispatcher; + +import dwt.dwthelper.utils; + +import dwt.accessibility.AccessibleControlEvent; +import dwt.accessibility.AccessibleControlListener; +import dwt.accessibility.AccessibleListener; +import dwt.events.FocusEvent; +import dwt.events.KeyEvent; +import dwt.events.MouseEvent; +import dwt.events.TraverseEvent; +import dwt.widgets.Control; +import dwt.widgets.Event; + +import dwtx.draw2d.IFigure; + +/** + * Listens to various DWT events and dispatches these events to interested Draw2d objects. + */ +public abstract class EventDispatcher { + + /** + * Combines {@link AccessibleControlListener} and {@link AccessibleListener}. + * Implements {@link AccessibleControlListener#getChild(AccessibleControlEvent)} to do + * nothing. + */ + public abstract static class AccessibilityDispatcher + : AccessibleControlListener, AccessibleListener + { + /** @see AccessibleControlListener#getChild(AccessibleControlEvent) */ + public void getChild(AccessibleControlEvent e) { } + } + +/** + * Dispatches a focus gained event. + * @param e the event + */ +public abstract void dispatchFocusGained(FocusEvent e); + +/** + * Dispatches a focus lost event. + * @param e the event + */ +public abstract void dispatchFocusLost(FocusEvent e); + +/** + * Dispatches a key pressed event. + * @param e the event + */ +public abstract void dispatchKeyPressed(KeyEvent e); + +/** + * Dispatches a key released event. + * @param e the event + */ +public abstract void dispatchKeyReleased(KeyEvent e); + +/** + * Dispatches a key traversed event. + * @param e the event + */ +public abstract void dispatchKeyTraversed(TraverseEvent e); + +/** + * Dispatches a mouse double clicked event. + * @param me the event + */ +public abstract void dispatchMouseDoubleClicked(MouseEvent me); + +/** + * Dispatches a mouse entered event. + * @param e the event + */ +public abstract void dispatchMouseEntered(MouseEvent e); + +/** + * Dispatches a mouse exited event. + * @param e the event + */ +public abstract void dispatchMouseExited(MouseEvent e); + +/** + * Dispatches a mouse hover event. + * @param me the event + */ +public abstract void dispatchMouseHover(MouseEvent me); + +/** + * Dispatches a moved event event. + * @param me the event + */ +public abstract void dispatchMouseMoved(MouseEvent me); + +/** + * Dispatches a mouse pressed event. + * @param me the event + */ +public abstract void dispatchMousePressed(MouseEvent me); + +/** + * Dispatches a mouse released event. + * @param me the event + */ +public abstract void dispatchMouseReleased(MouseEvent me); + +/** + * Dispatches a MouseWheel event. Does nothing by default. + * @param event the DWT event + * @since 3.1 + */ +public void dispatchMouseWheelScrolled(Event event) { } + +/** + * Returns the AccessibilityDispatcher. + * @return the AccessibilityDispatcher + */ +protected abstract AccessibilityDispatcher getAccessibilityDispatcher(); +package AccessibilityDispatcher getAccessibilityDispatcher_package(){ + return getAccessibilityDispatcher(); +} + +/** + * @return the IFigure that currently has focus + */ +/*package*/ abstract IFigure getFocusOwner(); + +/** + * @return whether events are captured by a figure + */ +public abstract bool isCaptured(); + +/** + * Releases capture initiated by {@link #setCapture(IFigure)}. + */ +protected abstract void releaseCapture(); + +/** + * Requests focus for the given figure. + * @param fig the figure requesting focus + */ +public abstract void requestFocus(IFigure fig); + +/** + * Requests focus to be removed from the given figure. + * @param fig the figure requesting focus be removed + */ +public abstract void requestRemoveFocus(IFigure fig); + +/** + * Sets capture to the given figure. All subsequent events will be sent to the given + * figure until {@link #releaseCapture()} is called. + * + * @param figure the figure capturing the events + */ +protected abstract void setCapture(IFigure figure); + +/** + * Sets the contol associated with this event dispatcher. + * @param control the control + */ +public abstract void setControl(Control control); + +/** + * Sets the root figure for this dispatcher. + * @param figure the root figure + */ +public abstract void setRoot(IFigure figure); + +/** + * Updates the cursor. + */ +protected abstract void updateCursor(); + +package void updateCursor_package(){ + updateCursor(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/EventListenerList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/EventListenerList.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.EventListenerList; + +import dwt.dwthelper.utils; + +import dwtx.dwtxhelper.Collection; + +/** + * This class is intended for internal use only. + * TODO: If this is for internal use only, we should move it to the internal package. + */ +public final class EventListenerList { + +private /+volatile+/ Object array[]; + +/** + * Adds a listener of type c to the list. + * @param c the class + * @param listener the listener + */ +public synchronized void addListener(ClassInfo c, Object listener) { + if (listener is null || c is null) + throw new IllegalArgumentException(""); + + int oldSize = (array is null) ? 0 : array.length; + Object[] newArray = new Object[oldSize + 2]; + if (oldSize !is 0) + System.arraycopy(array, 0, newArray, 0, oldSize); + newArray[oldSize++] = c; + newArray[oldSize] = listener; + array = newArray; +} + +/** + * Returns true if this list of listeners contains a listener of type + * c. + * @param c the type + * @return whether this list contains a listener of type c + */ +public synchronized bool containsListener(ClassInfo c) { + if (array is null) + return false; + for (int i = 0; i < array.length; i += 2) + if (array[i] is c) + return true; + return false; +} + +static class TypeIterator : Iterator { + private final Object[] items; + private final ClassInfo type; + private int index; + this(Object items[], ClassInfo type) { + this.items = items; + this.type = type; + } + public Object next() { + Object result = items[index + 1]; + index += 2; + return result; + } + + public bool hasNext() { + if (items is null) + return false; + while (index < items.length && items[index] !is type) + index += 2; + return index < items.length; + } + + public void remove() { + throw new UnsupportedOperationException("Iterator removal not supported"); //$NON-NLS-1$ + } +} + +/** + * Returns an Iterator of all the listeners of type c. + * @param listenerType the type + * @return an Iterator of all the listeners of type c + */ +public synchronized Iterator getListeners(ClassInfo listenerType) { + return new TypeIterator(array, listenerType); +} + +/** + * Removes the first listener of the specified type by identity. + * @param c the type + * @param listener the listener + */ +public synchronized void removeListener(ClassInfo c, Object listener) { + if (array is null || array.length is 0) + return; + if (listener is null || c is null) + throw new IllegalArgumentException(""); + + int index = 0; + while (index < array.length) { + if (array[index] is c && array[index + 1] is listener) + break; + index += 2; + } + if (index is array.length) + return; //listener was not found + + Object newArray[] = new Object[array.length - 2]; + System.arraycopy(array, 0, newArray, 0, index); + System.arraycopy(array, index + 2, newArray, index, array.length - index - 2); + array = newArray; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ExclusionSearch.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ExclusionSearch.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ExclusionSearch; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.TreeSearch; +import dwtx.draw2d.IFigure; + +/** + * A TreeSearch that excludes figures contained in a {@link + * java.util.Collection}. + * @author hudsonr + * @since 2.1 + */ +public class ExclusionSearch : TreeSearch { + +private const Collection c; + +/** + * Constructs an Exclusion search using the given collection. + * @param collection the exclusion set + */ +public this(Collection collection) { + this.c = collection; +} + +/** + * @see dwtx.draw2d.TreeSearch#accept(IFigure) + */ +public bool accept(IFigure figure) { + //Prune is called before accept, so there is no reason to check the collection again. + return true; +} + +/** + * Returns true if the figure is a member of the Collection. + * @see dwtx.draw2d.TreeSearch#prune(IFigure) + */ +public bool prune(IFigure f) { + return c.contains(cast(Object)f); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FanRouter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FanRouter.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FanRouter; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.Ray; +import dwtx.draw2d.AutomaticRouter; +import dwtx.draw2d.PositionConstants; + +/** + * Automatic router that spreads its {@link Connection Connections} in a fan-like fashion + * upon collision. + */ +public class FanRouter + : AutomaticRouter +{ + +private int separation = 10; + +/** + * Returns the separation in pixels between fanned connections. + * + * @return the separation + * @since 2.0 + */ +public int getSeparation() { + return separation; +} + +/** + * Modifies a given PointList that collides with some other PointList. The given + * index indicates that this it the ith PointList in a group of + * colliding points. + * + * @param points the colliding points + * @param index the index + */ +protected void handleCollision(PointList points, int index) { + Point start = points.getFirstPoint(); + Point end = points.getLastPoint(); + + if (start.opEquals(end)) + return; + + Point midPoint = new Point((end.x + start.x) / 2, (end.y + start.y) / 2); + int position = end.getPosition(start); + Ray ray; + if (position is PositionConstants.SOUTH || position is PositionConstants.EAST) + ray = new Ray(start, end); + else + ray = new Ray(end, start); + double length = ray.length(); + + double xSeparation = separation * ray.x / length; + double ySeparation = separation * ray.y / length; + + Point bendPoint; + + if (index % 2 is 0) { + bendPoint = new Point( + midPoint.x + (index / 2) * (-1 * ySeparation), + midPoint.y + (index / 2) * xSeparation); + } else { + bendPoint = new Point( + midPoint.x + (index / 2) * ySeparation, + midPoint.y + (index / 2) * (-1 * xSeparation)); + } + if (!bendPoint.opEquals(midPoint)) + points.insertPoint(bendPoint, 1); +} + +/** + * Sets the colliding {@link Connection Connection's} separation in pixels. + * + * @param value the separation + * @since 2.0 + */ +public void setSeparation(int value) { + separation = value; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Figure.d --- /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 + *******************************************************************************/ +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. + *

+ * 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 null 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 null 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 clazz 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 null 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 + * true or null if none found. The Parameters x and + * y 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 x and + * y 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 true and returns that descendant or + * null 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 property 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 property 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 property 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 + * null and its parent is not null, 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 clazz that are listening to + * this Figure. If there are no listeners of type clazz, 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 null or the local background Color of this Figure. Does not + * inherit this Color from the parent. + * @return bgColor null or the local background Color + */ +public Color getLocalBackgroundColor() { + return bgColor; +} + +/** + * Returns null or the local font setting for this figure. Does not return + * values inherited from the parent figure. + * @return null or the local font + * @since 3.1 + */ +protected Font getLocalFont() { + return font; +} + +/** + * Returns null or the local foreground Color of this Figure. Does not + * inherit this Color from the parent. + * @return fgColor null 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 true if this Figure can receive {@link MouseEvent MouseEvents}. + * @return true 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 true if this Figure is valid. + * @return true if this Figure is valid + * @since 2.0 + */ +protected bool isValid() { + return (flags & FLAG_VALID) !is 0; +} + +/** + * Returns true if revalidating this Figure does not require revalidating its + * parent. + * @return true 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 graphics.restoreState() may be called + * safely, and doing so will return the graphics to its original state when the method was + * entered. + *

+ * 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 graphics.restoreState() 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 listener of type clazz 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 rect. Note that rect 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 + * dir 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 value. + * @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 + * orientation 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 value is true 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 true if this Figure uses local coordinates. This means its + * children are placed relative to this Figure's top-left corner. + * @return true 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 true. + * @see TreeSearch#accept(IFigure) + */ + public bool accept(IFigure f) { + return true; + } + /** + * Always returns false. + * @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 true if there's another Figure to iterate over. + * @return true 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) { } + }; +} +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FigureCanvas.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FigureCanvas.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,560 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.FigureCanvas; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Bean; +static import dwtx.dwtxhelper.Collection; + +import dwt.DWT; +import dwt.events.SelectionAdapter; +import dwt.events.SelectionEvent; +import dwt.graphics.Font; +import dwt.widgets.Canvas; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Viewport; +import dwtx.draw2d.LightweightSystem; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Border; +import dwtx.draw2d.RangeModel; +static import dwt.graphics.Point; +static import dwt.graphics.Rectangle; +import dwtx.draw2d.UpdateListener; +import dwtx.draw2d.ScrollPaneSolver; + +/** + * A Canvas that contains {@link Figure Figures}. + * + *

+ *
Required Styles (when using certain constructors):
+ *
V_SCROLL, H_SCROLL, NO_REDRAW_RESIZE
+ *
Optional Styles:
+ *
DOUBLE_BUFFERED, RIGHT_TO_LEFT, LEFT_TO_RIGHT, NO_BACKGROUND, BORDER
+ *
+ *

+ * Note: Only one of the styles RIGHT_TO_LEFT, LEFT_TO_RIGHT may be specified. + *

+ */ +public class FigureCanvas + : Canvas +{ + +private static final int ACCEPTED_STYLES = + DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT + | DWT.V_SCROLL | DWT.H_SCROLL + | DWT.NO_BACKGROUND + | DWT.NO_REDRAW_RESIZE + | DWT.DOUBLE_BUFFERED + | DWT.BORDER; + +/** + * The default styles are mixed in when certain constructors are used. This + * constant is a bitwise OR of the following DWT style constants: + *
    + *
  • {@link DWT#NO_REDRAW_RESIZE}
  • + *
  • {@link DWT#NO_BACKGROUND}
  • + *
  • {@link DWT#V_SCROLL}
  • + *
  • {@link DWT#H_SCROLL}
  • + *
+ */ +static const int DEFAULT_STYLES = + DWT.NO_REDRAW_RESIZE + | DWT.NO_BACKGROUND + | DWT.V_SCROLL + | DWT.H_SCROLL; + +private static const int REQUIRED_STYLES = + DWT.NO_REDRAW_RESIZE + | DWT.V_SCROLL + | DWT.H_SCROLL; + +/** Never show scrollbar */ +public static int NEVER = 0; +/** Automatically show scrollbar when needed */ +public static int AUTOMATIC = 1; +/** Always show scrollbar */ +public static int ALWAYS = 2; + +private int vBarVisibility; +private int hBarVisibility; +private Viewport viewport; +private Font font; +private int hBarOffset; +private int vBarOffset; + +private PropertyChangeListener horizontalChangeListener; +private PropertyChangeListener verticalChangeListener; +private const LightweightSystem lws; + +/** + * Creates a new FigureCanvas with the given parent and the {@link #DEFAULT_STYLES}. + * + * @param parent the parent + */ +public this(Composite parent) { + this(parent, DWT.DOUBLE_BUFFERED, new LightweightSystem()); +} + +/** + * Constructor which applies the default styles plus any optional styles indicated. + * @param parent the parent composite + * @param style see the class javadoc for optional styles + * @since 3.1 + */ +public this(Composite parent, int style) { + this(parent, style, new LightweightSystem()); +} + +/** + * Constructor which uses the given styles verbatim. Certain styles must be used + * with this class. Refer to the class javadoc for more details. + * @param style see the class javadoc for required and optional styles + * @param parent the parent composite + * @since 3.4 + */ +public this(int style, Composite parent) { + this(style, parent, new LightweightSystem()); +} + +/** + * Constructs a new FigureCanvas with the given parent and LightweightSystem, using + * the {@link #DEFAULT_STYLES}. + * @param parent the parent + * @param lws the LightweightSystem + */ +public this(Composite parent, LightweightSystem lws) { + this(parent, DWT.DOUBLE_BUFFERED, lws); +} + +/** + * Constructor taking a lightweight system and DWT style, which is used verbatim. + * Certain styles must be used with this class. Refer to the class javadoc for + * more details. + * @param style see the class javadoc for required and optional styles + * @param parent the parent composite + * @param lws the LightweightSystem + * @since 3.4 + */ +public this(int style, Composite parent, LightweightSystem lws) { + this.lws = lws; + vBarVisibility = AUTOMATIC; + hBarVisibility = AUTOMATIC; + + horizontalChangeListener = new class() PropertyChangeListener { + public void propertyChange(PropertyChangeEvent event) { + RangeModel model = getViewport().getHorizontalRangeModel(); + hBarOffset = Math.max(0, -model.getMinimum()); + getHorizontalBar().setValues( + model.getValue() + hBarOffset, + model.getMinimum() + hBarOffset, + model.getMaximum() + hBarOffset, + model.getExtent(), + Math.max(1, model.getExtent() / 20), + Math.max(1, model.getExtent() * 3 / 4)); + } + }; + + verticalChangeListener = new class() PropertyChangeListener { + public void propertyChange(PropertyChangeEvent event) { + RangeModel model = getViewport().getVerticalRangeModel(); + vBarOffset = Math.max(0, -model.getMinimum()); + getVerticalBar().setValues( + model.getValue() + vBarOffset, + model.getMinimum() + vBarOffset, + model.getMaximum() + vBarOffset, + model.getExtent(), + Math.max(1, model.getExtent() / 20), + Math.max(1, model.getExtent() * 3 / 4)); + } + }; + super(parent, checkStyle(style)); + getHorizontalBar().setVisible(false); + getVerticalBar().setVisible(false); + lws.setControl(this); + hook(); +} + +/** + * Constructor + * @param parent the parent composite + * @param style look at class javadoc for valid styles + * @param lws the lightweight system + * @since 3.1 + */ +public this(Composite parent, int style, LightweightSystem lws) { + this(style | DEFAULT_STYLES, parent, lws); +} + +private static int checkStyle(int style) { + if ((style & REQUIRED_STYLES) !is REQUIRED_STYLES) + throw new IllegalArgumentException("Required style missing on FigureCanvas"); //$NON-NLS-1$ + if ((style & ~ACCEPTED_STYLES) !is 0) + throw new IllegalArgumentException("Invalid style being set on FigureCanvas"); //$NON-NLS-1$ + return style; +} + +/** + * @see dwt.widgets.Composite#computeSize(int, int, bool) + */ +public dwt.graphics.Point.Point computeSize(int wHint, int hHint, bool changed) { + // TODO not accounting for scrollbars and trim + Dimension size = getLightweightSystem().getRootFigure().getPreferredSize(wHint, hHint); + size.union_(new Dimension(wHint, hHint)); + return new dwt.graphics.Point.Point(size.width, size.height); +} + +/** + * @return the contents of the {@link Viewport}. + */ +public IFigure getContents() { + return getViewport().getContents(); +} + +/** + * @see dwt.widgets.Control#getFont() + */ +public Font getFont() { + if (font is null) + font = super.getFont(); + return font; +} + +/** + * @return the horizontal scrollbar visibility. + */ +public int getHorizontalScrollBarVisibility() { + return hBarVisibility; +} + +/** + * @return the LightweightSystem + */ +public LightweightSystem getLightweightSystem() { + return lws; +} + +/** + * @return the vertical scrollbar visibility. + */ +public int getVerticalScrollBarVisibility() { + return vBarVisibility; +} + +/** + * Returns the Viewport. If it's null, a new one is created. + * @return the viewport + */ +public Viewport getViewport() { + if (viewport is null) + setViewport(new Viewport(true)); + return viewport; +} + +/** + * Adds listeners for scrolling. + */ +private void hook() { + getLightweightSystem().getUpdateManager().addUpdateListener(new class() UpdateListener { + public void notifyPainting(Rectangle damage, dwtx.dwtxhelper.Collection.Map dirtyRegions) { } + public void notifyValidating() { + if (!isDisposed()) + layoutViewport(); + } + }); + + getHorizontalBar().addSelectionListener(new class() SelectionAdapter { + public void widgetSelected(SelectionEvent event) { + scrollToX(getHorizontalBar().getSelection() - hBarOffset); + } + }); + + getVerticalBar().addSelectionListener(new class() SelectionAdapter { + public void widgetSelected(SelectionEvent event) { + scrollToY(getVerticalBar().getSelection() - vBarOffset); + } + }); +} + +private void hookViewport() { + getViewport() + .getHorizontalRangeModel() + .addPropertyChangeListener(horizontalChangeListener); + getViewport() + .getVerticalRangeModel() + .addPropertyChangeListener(verticalChangeListener); +} + +private void unhookViewport() { + getViewport() + .getHorizontalRangeModel() + .removePropertyChangeListener(horizontalChangeListener); + getViewport() + .getVerticalRangeModel() + .removePropertyChangeListener(verticalChangeListener); +} + +private void layoutViewport() { + ScrollPaneSolver.Result result; + result = ScrollPaneSolver.solve((new Rectangle(getBounds())).setLocation(0, 0), + getViewport(), + getHorizontalScrollBarVisibility(), + getVerticalScrollBarVisibility(), + computeTrim(0, 0, 0, 0).width, + computeTrim(0, 0, 0, 0).height); + getLightweightSystem().setIgnoreResize(true); + try { + if (getHorizontalBar().getVisible() !is result.showH) + getHorizontalBar().setVisible(result.showH); + if (getVerticalBar().getVisible() !is result.showV) + getVerticalBar().setVisible(result.showV); + Rectangle r = new Rectangle(getClientArea()); + r.setLocation(0, 0); + getLightweightSystem().getRootFigure().setBounds(r); + } finally { + getLightweightSystem().setIgnoreResize(false); + } +} + +/** + * Scrolls in an animated way to the new x and y location. + * @param x the x coordinate to scroll to + * @param y the y coordinate to scroll to + */ +public void scrollSmoothTo(int x, int y) { + // Ensure newHOffset and newVOffset are within the appropriate ranges + x = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), x); + y = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), y); + + int oldX = getViewport().getViewLocation().x; + int oldY = getViewport().getViewLocation().y; + int dx = x - oldX; + int dy = y - oldY; + + if (dx is 0 && dy is 0) + return; //Nothing to do. + + Dimension viewingArea = getViewport().getClientArea().getSize(); + + int minFrames = 3; + int maxFrames = 6; + if (dx is 0 || dy is 0) { + minFrames = 6; + maxFrames = 13; + } + int frames = (Math.abs(dx) + Math.abs(dy)) / 15; + frames = Math.max(frames, minFrames); + frames = Math.min(frames, maxFrames); + + int stepX = Math.min((dx / frames), (viewingArea.width / 3)); + int stepY = Math.min((dy / frames), (viewingArea.height / 3)); + + for (int i = 1; i < frames; i++) { + scrollTo(oldX + i * stepX, oldY + i * stepY); + getViewport().getUpdateManager().performUpdate(); + } + scrollTo(x, y); +} + +/** + * Scrolls the contents to the new x and y location. If this scroll operation only + * consists of a vertical or horizontal scroll, a call will be made to + * {@link #scrollToY(int)} or {@link #scrollToX(int)}, respectively, to increase + * performance. + * + * @param x the x coordinate to scroll to + * @param y the y coordinate to scroll to + */ +public void scrollTo(int x, int y) { + x = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), x); + y = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), y); + if (x is getViewport().getViewLocation().x) + scrollToY(y); + else if (y is getViewport().getViewLocation().y) + scrollToX(x); + else + getViewport().setViewLocation(x, y); +} +/** + * Scrolls the contents horizontally so that they are offset by hOffset. + * + * @param hOffset the new horizontal offset + */ +public void scrollToX(int hOffset) { + hOffset = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), hOffset); + int hOffsetOld = getViewport().getViewLocation().x; + if (hOffset is hOffsetOld) + return; + int dx = -hOffset + hOffsetOld; + + Rectangle clientArea = getViewport().getBounds().getCropped(getViewport().getInsets()); + Rectangle blit = clientArea.getResized(-Math.abs(dx), 0); + Rectangle expose = clientArea.getCopy(); + Point dest = clientArea.getTopLeft(); + expose.width = Math.abs(dx); + if (dx < 0) { //Moving left? + blit.translate(-dx, 0); //Move blit area to the right + expose.x = dest.x + blit.width; + } else //Moving right + dest.x += dx; //Move expose area to the right + + // fix for bug 41111 + Control[] children = getChildren(); + bool[] manualMove = new bool[children.length]; + for (int i = 0; i < children.length; i++) { + dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds(); + manualMove[i] = blit.width <= 0 || bounds.x > blit.x + blit.width + || bounds.y > blit.y + blit.height || bounds.x + bounds.width < blit.x + || bounds.y + bounds.height < blit.y; + } + scroll(dest.x, dest.y, blit.x, blit.y, blit.width, blit.height, true); + for (int i = 0; i < children.length; i++) { + if (children[i].isDisposed ()) + continue; + dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds(); + if (manualMove[i]) + children[i].setBounds(bounds.x + dx, bounds.y, bounds.width, bounds.height); + } + + getViewport().setIgnoreScroll(true); + getViewport().setHorizontalLocation(hOffset); + getViewport().setIgnoreScroll(false); + redraw(expose.x, expose.y, expose.width, expose.height, true); +} + +/** + * Scrolls the contents vertically so that they are offset by vOffset. + * + * @param vOffset the new vertical offset + */ +public void scrollToY(int vOffset) { + vOffset = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), vOffset); + int vOffsetOld = getViewport().getViewLocation().y; + if (vOffset is vOffsetOld) + return; + int dy = -vOffset + vOffsetOld; + + Rectangle clientArea = getViewport().getBounds().getCropped(getViewport().getInsets()); + Rectangle blit = clientArea.getResized(0, -Math.abs(dy)); + Rectangle expose = clientArea.getCopy(); + Point dest = clientArea.getTopLeft(); + expose.height = Math.abs(dy); + if (dy < 0) { //Moving up? + blit.translate(0, -dy); //Move blit area down + expose.y = dest.y + blit.height; //Move expose area down + } else //Moving down + dest.y += dy; + + // fix for bug 41111 + Control[] children = getChildren(); + bool[] manualMove = new bool[children.length]; + for (int i = 0; i < children.length; i++) { + dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds(); + manualMove[i] = blit.height <= 0 || bounds.x > blit.x + blit.width + || bounds.y > blit.y + blit.height || bounds.x + bounds.width < blit.x + || bounds.y + bounds.height < blit.y; + } + scroll(dest.x, dest.y, blit.x, blit.y, blit.width, blit.height, true); + for (int i = 0; i < children.length; i++) { + if (children[i].isDisposed ()) + continue; + dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds(); + if (manualMove[i]) + children[i].setBounds(bounds.x, bounds.y + dy, bounds.width, bounds.height); + } + + getViewport().setIgnoreScroll(true); + getViewport().setVerticalLocation(vOffset); + getViewport().setIgnoreScroll(false); + redraw(expose.x, expose.y, expose.width, expose.height, true); +} + +/** + * Sets the given border on the LightweightSystem's root figure. + * + * @param border The new border + */ +public void setBorder(Border border) { + getLightweightSystem().getRootFigure().setBorder(border); +} + +/** + * Sets the contents of the {@link Viewport}. + * + * @param figure the new contents + */ +public void setContents(IFigure figure) { + getViewport().setContents(figure); +} + +/** + * @see dwt.widgets.Control#setFont(dwt.graphics.Font) + */ +public void setFont(Font font) { + this.font = font; + super.setFont(font); +} + +/** + * Sets the horizontal scrollbar visibility. Possible values are {@link #AUTOMATIC}, + * {@link #ALWAYS}, and {@link #NEVER}. + * + * @param v the new visibility + */ +public void setHorizontalScrollBarVisibility(int v) { + hBarVisibility = v; +} + +/** + * Sets both the horizontal and vertical scrollbar visibility to the given value. + * Possible values are {@link #AUTOMATIC}, {@link #ALWAYS}, and {@link #NEVER}. + * @param both the new visibility + */ +public void setScrollBarVisibility(int both) { + setHorizontalScrollBarVisibility(both); + setVerticalScrollBarVisibility(both); +} + +/** + * Sets the vertical scrollbar visibility. Possible values are {@link #AUTOMATIC}, + * {@link #ALWAYS}, and {@link #NEVER}. + * + * @param v the new visibility + */ +public void setVerticalScrollBarVisibility(int v) { + vBarVisibility = v; +} + +/** + * Sets the Viewport. The given Viewport must use "fake" scrolling. That is, it must be + * constructed using new Viewport(true). + * + * @param vp the new viewport + */ +public void setViewport(Viewport vp) { + if (viewport !is null) + unhookViewport(); + viewport = vp; + lws.setContents(viewport); + hookViewport(); +} + +private int verifyScrollBarOffset(RangeModel model, int value) { + value = Math.max(model.getMinimum(), value); + return Math.min(model.getMaximum() - model.getExtent(), value); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FigureListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FigureListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FigureListener; + +import dwt.dwthelper.utils; +import dwtx.draw2d.IFigure; + +/** + * A listener interface for receiving notification that an IFigure has moved. + */ +public interface FigureListener { + +/** + * Called when the given IFigure has moved. + * @param source The IFigure that has moved. + */ +void figureMoved(IFigure source); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FigureUtilities.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FigureUtilities.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,399 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +module dwtx.draw2d.FigureUtilities; + +import dwt.dwthelper.utils; + +import dwtx.dwtxhelper.Collection; + +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.FontMetrics; +import dwt.graphics.GC; +import dwt.widgets.Shell; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Shape; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; +static import dwtx.draw2d.geometry.Point; +/** + * Provides miscellaneous Figure operations. + */ +public class FigureUtilities { + +private static final float RGB_VALUE_MULTIPLIER = 0.6f; +private static GC gc; +private static Font appliedFont; +private static FontMetrics metrics; +private static Color ghostFillColor; + +static this(){ + ghostFillColor = new Color(null, 31, 31, 31); +} + +/** + * Returns a new Color the same as the passed color in a darker hue. + * + * @param color the color to darken + * @return the darkened color + * @since 2.0 + */ +public static Color darker(Color color) { + return new Color(null, + cast(int)(color.getRed() * RGB_VALUE_MULTIPLIER), + cast(int)(color.getGreen() * RGB_VALUE_MULTIPLIER), + cast(int)(color.getBlue() * RGB_VALUE_MULTIPLIER)); +} + +/** + * Returns the FontMetrics associated with the passed Font. + * + * @param f the font + * @return the FontMetrics for the given font + * @see GC#getFontMetrics() + * @since 2.0 + */ +public static FontMetrics getFontMetrics(Font f) { + setFont(f); + if (metrics is null) + metrics = getGC().getFontMetrics(); + return metrics; +} + +/** + * Returns the GC used for various utilities. Advanced graphics must not be switched on by + * clients using this GC. + * @deprecated do not mess with this GC + * @return the GC + */ +protected static GC getGC() { + if (gc is null) { + gc = new GC(new Shell()); + appliedFont = gc.getFont(); + } + return gc; +} + +/** + * Returns the dimensions of the String s using the font f. Tab expansion + * and carriage return processing are performed. + * @param s the string + * @param f the font + * @return the text's dimensions + * @see GC#textExtent(String) + */ +protected static dwt.graphics.Point.Point getTextDimension(String s, Font f) { + setFont(f); + return getGC().textExtent(s); +} + +/** + * Returns the highest ancestor for the given figure + * @since 3.0 + * @param figure a figure + * @return the root ancestor + */ +public static IFigure getRoot(IFigure figure) { + while (figure.getParent() !is null) + figure = figure.getParent(); + return figure; +} + +/** + * Returns the dimensions of the String s using the font f. No tab + * expansion or carriage return processing will be performed. + * @param s the string + * @param f the font + * @return the string's dimensions + * @see GC#stringExtent(java.lang.String) + */ +protected static dwt.graphics.Point.Point getStringDimension(String s, Font f) { + setFont(f); + return getGC().stringExtent(s); +} + +/** + * Returns the Dimensions of the given text, converting newlines and tabs appropriately. + * + * @param text the text + * @param f the font + * @return the dimensions of the given text + * @since 2.0 + */ +public static Dimension getTextExtents(String text, Font f) { + return new Dimension(getTextDimension(text, f)); +} + +/** + * Returns the Dimensions of s in Font f. + * + * @param s the string + * @param f the font + * @return the dimensions of the given string + * @since 2.0 + */ +public static Dimension getStringExtents(String s, Font f) { + return new Dimension(getStringDimension(s, f)); +} + +/** + * Returns the Dimensions of the given text, converting newlines and tabs appropriately. + * + * @param s the string + * @param f the font + * @param result the Dimension that will contain the result of this calculation + * @since 2.0 + */ +public static void getTextExtents(String s, Font f, Dimension result) { + dwt.graphics.Point.Point pt = getTextDimension(s, f); + result.width = pt.x; + result.height = pt.y; +} + +/** + * Returns the width of s in Font f. + * + * @param s the string + * @param f the font + * @return the width + * @since 2.0 + */ +public static int getTextWidth(String s, Font f) { + return getTextDimension(s, f).x; +} + +/** + * Returns a Color the same as the passed color in a lighter hue. + * + * @param rgb the color + * @return the lighter color + * @since 2.0 + */ +public static Color lighter(Color rgb) { + int r = rgb.getRed(), + g = rgb.getGreen(), + b = rgb.getBlue(); + + return new Color(null, + Math.max(2, Math.min(cast(int)(r / RGB_VALUE_MULTIPLIER), 255)), + Math.max(2, Math.min(cast(int)(g / RGB_VALUE_MULTIPLIER), 255)), + Math.max(2, Math.min(cast(int)(b / RGB_VALUE_MULTIPLIER), 255)) + ); +} + +/** + * Produces a ghosting effect on the shape s. + * + * @param s the shape + * @return the ghosted shape + * @since 2.0 + */ +public static Shape makeGhostShape(Shape s) { + s.setBackgroundColor(ghostFillColor); + s.setFillXOR(true); + s.setOutlineXOR(true); + return s; +} + +/** + * Mixes the passed Colors and returns the resulting Color. + * + * @param c1 the first color + * @param c2 the second color + * @param weight the first color's weight from 0-1 + * @return the new color + * @since 2.0 + */ +public static Color mixColors(Color c1, Color c2, double weight) { + return new Color(null, + cast(int)(c1.getRed() * weight + c2.getRed() * (1 - weight)), + cast(int)(c1.getGreen() * weight + c2.getGreen() * (1 - weight)), + cast(int)(c1.getBlue() * weight + c2.getBlue() * (1 - weight))); +} + + +/** + * Mixes the passed Colors and returns the resulting Color. + * + * @param c1 the first color + * @param c2 the second color + * @return the new color + * @since 2.0 + */ +public static Color mixColors(Color c1, Color c2) { + return new Color(null, + (c1.getRed() + c2.getRed()) / 2, + (c1.getGreen() + c2.getGreen()) / 2, + (c1.getBlue() + c2.getBlue()) / 2); +} + +/** + * Paints a border with an etching effect, having a shadow of Color shadow and + * highlight of Color highlight. + * + * @param g the graphics object + * @param r the bounds of the border + * @param shadow the shadow color + * @param highlight the highlight color + * @since 2.0 + */ +public static void paintEtchedBorder(Graphics g, Rectangle r, + Color shadow, Color highlight) { + int x = r.x, + y = r.y, + w = r.width, + h = r.height; + + g.setLineStyle(Graphics.LINE_SOLID); + g.setLineWidth(1); + g.setXORMode(false); + + w -= 2; + h -= 2; + + g.setForegroundColor(shadow); + g.drawRectangle(x, y, w, h); + + x++; + y++; + g.setForegroundColor(highlight); + g.drawRectangle(x, y, w, h); +} + +/** + * Helper method to paint a grid. Painting is optimized as it is restricted to the + * Graphics' clip. + * + * @param g The Graphics object to be used for painting + * @param f The figure in which the grid is to be painted + * @param origin Any point where the grid lines are expected to intersect + * @param distanceX Distance between vertical grid lines; if 0 or less, vertical grid + * lines will not be drawn + * @param distanceY Distance between horizontal grid lines; if 0 or less, horizontal + * grid lines will not be drawn + * + * @since 3.0 + */ +public static void paintGrid(Graphics g, IFigure f, + dwtx.draw2d.geometry.Point.Point origin, int distanceX, int distanceY) { + Rectangle clip = g.getClip(Rectangle.SINGLETON); + + if (distanceX > 0) { + if (origin.x >= clip.x) + while (origin.x - distanceX >= clip.x) + origin.x -= distanceX; + else + while (origin.x < clip.x) + origin.x += distanceX; + for (int i = origin.x; i < clip.x + clip.width; i += distanceX) + g.drawLine(i, clip.y, i, clip.y + clip.height); + } + + if (distanceY > 0) { + if (origin.y >= clip.y) + while (origin.y - distanceY >= clip.y) + origin.y -= distanceY; + else + while (origin.y < clip.y) + origin.y += distanceY; + for (int i = origin.y; i < clip.y + clip.height; i += distanceY) + g.drawLine(clip.x, i, clip.x + clip.width, i); + } +} + +/** + * Paints a border with an etching effect, having a shadow of a darker version of g's + * background color, and a highlight a lighter version of g's background color. + * + * @param g the graphics object + * @param r the bounds of the border + * @since 2.0 + */ +public static void paintEtchedBorder(Graphics g, Rectangle r) { + Color rgb = g.getBackgroundColor(), + shadow = darker(rgb), + highlight = lighter(rgb); + paintEtchedBorder(g, r, shadow, highlight); +} + +/** + * Sets Font to passed value. + * + * @param f the new font + * @since 2.0 + */ +protected static void setFont(Font f) { + if (appliedFont is f || f.opEquals(appliedFont)) + return; + getGC().setFont(f); + appliedFont = f; + metrics = null; +} + +/** + * Returns the figure which is the ancestor of both figures, or null. A + * figure is an ancestor if it is the parent of another figure, or if it is the ancestor + * of that figure's parent. If one figure contains the other, null is + * returned. + * @since 3.1 + * @param l left + * @param r right + * @return the common ancestor + */ +public static IFigure findCommonAncestor(IFigure l, IFigure r) { + if (l is r) + return l; + ArrayList left = new ArrayList(); + ArrayList right = new ArrayList(); + while (l !is null) { + left.add(cast(Object)l); + l = l.getParent(); + } + while (r !is null) { + right.add(cast(Object)r); + r = r.getParent(); + } + if (left.isEmpty() || right.isEmpty()) + return null; + + int il = left.size() - 1; + int ir = right.size() - 1; + do { + if (left.get(il) !is right.get(ir)) + break; + il--; + ir--; + } while (il >= 0 && ir >= 0); + + return cast(IFigure) left.get(il + 1); +} + +/** + * Returns true if the ancestor contains the descendant, or is the ancestor + * of the descendant's parent. + * @param ancestor the ancestor + * @param descendant the descendant + * @return true if ancestor + * @since 3.2 + */ +public static bool isAncestor(IFigure ancestor, IFigure descendant) { + while (descendant !is null) { + descendant = descendant.getParent(); + if (descendant is ancestor) + return true; + } + return false; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FlowLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FlowLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,466 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FlowLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.Transposer; +import dwtx.draw2d.AbstractHintLayout; +import dwtx.draw2d.IFigure; + +/** + * Lays out children in rows or columns, wrapping when the current row/column is filled. + * The aligment and spacing of rows in the parent can be configured. The aligment and + * spacing of children within a row can be configured. + */ +public class FlowLayout + : AbstractHintLayout +{ + +/** Constant to specify components to be aligned in the center */ +public static const int ALIGN_CENTER = 0; +/** Constant to specify components to be aligned on the left/top */ +public static const int ALIGN_LEFTTOP = 1; +/** Constant to specify components to be aligned on the right/bottom */ +public static const int ALIGN_RIGHTBOTTOM = 2; + +/** Constant to specify components should be layed out horizontally */ +public static const bool HORIZONTAL = true; +/** Constant to specify components should be layed out vertically */ +public static const bool VERTICAL = false; + +/** The horizontal property. */ +protected bool horizontal = true; +/** + * The property that determines whether leftover space at the end of a row/column should + * be filled by the last item in that row/column. + */ +protected bool fill = false; + +/** The transposer used in converting horizontal layout to vertical. */ +protected Transposer transposer; +private void instanceInit(){ + transposer = new Transposer(); + transposer.setEnabled(!horizontal); +} + +/** The alignment along the major axis. */ +protected int majorAlignment = ALIGN_LEFTTOP; +/** The alignment along the minor axis. */ +protected int minorAlignment = ALIGN_LEFTTOP; +/** The spacing along the minor axis. */ +protected int minorSpacing = 5; +/** The spacing along the major axis. */ +protected int majorSpacing = 5; +protected WorkingData data = null; + +/** + * Holds the necessary information for layout calculations. + */ +protected class WorkingData { + public int rowHeight, rowWidth, rowCount, rowX, rowY, maxWidth; + public Rectangle[] bounds; + public Rectangle area; + public IFigure row[]; +} + +/** + * Constructs a FlowLayout with horizontal orientation. + * @since 2.0 + */ +public this() { + instanceInit(); +} + +/** + * Constructs a FlowLayout whose orientation is given in the input. + * @param isHorizontal true if the layout should be horizontal + * @since 2.0 + */ +public this(bool isHorizontal) { + instanceInit(); + setHorizontal(isHorizontal); +} + +/** + * @see dwtx.draw2d.AbstractLayout#calculatePreferredSize(IFigure, int, int) + */ +protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) { + // Subtract out the insets from the hints + if (wHint > -1) + wHint = Math.max(0, wHint - container.getInsets().getWidth()); + if (hHint > -1) + hHint = Math.max(0, hHint - container.getInsets().getHeight()); + + // Figure out the new hint that we are interested in based on the orientation + // Ignore the other hint (by setting it to -1). NOTE: The children of the + // parent figure will then be asked to ignore that hint as well. + int maxWidth; + if (isHorizontal()) { + maxWidth = wHint; + hHint = -1; + } else { + maxWidth = hHint; + wHint = -1; + } + if (maxWidth < 0) { + maxWidth = Integer.MAX_VALUE; + } + + // The preferred dimension that is to be calculated and returned + Dimension prefSize = new Dimension(); + + List children = container.getChildren(); + int width = 0; + int height = 0; + IFigure child; + Dimension childSize; + + //Build the sizes for each row, and update prefSize accordingly + for (int i = 0; i < children.size(); i++) { + child = cast(IFigure)children.get(i); + childSize = transposer.t(getChildSize(child, wHint, hHint)); + if (i is 0) { + width = childSize.width; + height = childSize.height; + } else if (width + childSize.width + getMinorSpacing() > maxWidth) { + // The current row is full, start a new row. + prefSize.height += height + getMajorSpacing(); + prefSize.width = Math.max(prefSize.width, width); + width = childSize.width; + height = childSize.height; + } else { + // The current row can fit another child. + width += childSize.width + getMinorSpacing(); + height = Math.max(height, childSize.height); + } + } + + // Flush out the last row's data + prefSize.height += height; + prefSize.width = Math.max(prefSize.width, width); + + // Transpose the dimension back, and compensate for the border. + prefSize = transposer.t(prefSize); + prefSize.width += container.getInsets().getWidth(); + prefSize.height += container.getInsets().getHeight(); + prefSize.union_(getBorderPreferredSize(container)); + + return prefSize; +} + +/** + * Provides the given child's preferred size. + * + * @param child the Figure whose preferred size needs to be calculated + * @param wHint the width hint + * @param hHint the height hint + * @return the child's preferred size + */ +protected Dimension getChildSize(IFigure child, int wHint, int hHint) { + return child.getPreferredSize(wHint, hHint); +} + + +/** + * Returns the alignment used for an entire row/column. + *

+ * Possible values are : + *

    + *
  • {@link #ALIGN_CENTER} + *
  • {@link #ALIGN_LEFTTOP} + *
  • {@link #ALIGN_RIGHTBOTTOM} + *
+ * + * @return the major alignment + * @since 2.0 + */ +public int getMajorAlignment() { + return majorAlignment; +} + +/** + * Returns the spacing in pixels to be used between children in the direction parallel to + * the layout's orientation. + * @return the major spacing + */ +public int getMajorSpacing() { + return majorSpacing; +} + +/** + * Returns the alignment used for children within a row/column. + *

+ * Possible values are : + *

    + *
  • {@link #ALIGN_CENTER} + *
  • {@link #ALIGN_LEFTTOP} + *
  • {@link #ALIGN_RIGHTBOTTOM} + *
+ * + * @return the minor alignment + * @since 2.0 + */ +public int getMinorAlignment() { + return minorAlignment; +} + +/** + * Returns the spacing to be used between children within a row/column. + * @return the minor spacing + */ +public int getMinorSpacing() { + return minorSpacing; +} + +/** + * Initializes the state of row data, which is internal to the layout process. + */ +protected void initRow() { + data.rowX = 0; + data.rowHeight = 0; + data.rowWidth = 0; + data.rowCount = 0; +} + +/** + * Initializes state data for laying out children, based on the Figure given as input. + * + * @param parent the parent figure + * @since 2.0 + */ +protected void initVariables(IFigure parent) { + data.row = new IFigure[parent.getChildren().size()]; + data.bounds = new Rectangle[data.row.length]; + data.maxWidth = data.area.width; +} + +/** + * Returns true if the orientation of the layout is horizontal. + * + * @return true if the orientation of the layout is horizontal + * @since 2.0 + */ +public bool isHorizontal() { + return horizontal; +} + +/** + * @see dwtx.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure) + */ +protected bool isSensitiveHorizontally(IFigure parent) { + return isHorizontal(); +} + +/** + * @see dwtx.draw2d.AbstractHintLayout#isSensitiveVertically(IFigure) + */ +protected bool isSensitiveVertically(IFigure parent) { + return !isHorizontal(); +} + +/** + * @see dwtx.draw2d.LayoutManager#layout(IFigure) + */ +public void layout(IFigure parent) { + data = new WorkingData(); + Rectangle relativeArea = parent.getClientArea(); + data.area = transposer.t(relativeArea); + + Iterator iterator = parent.getChildren().iterator(); + int dx; + + //Calculate the hints to be passed to children + int wHint = -1; + int hHint = -1; + if (isHorizontal()) + wHint = parent.getClientArea().width; + else + hHint = parent.getClientArea().height; + + initVariables(parent); + initRow(); + int i = 0; + while (iterator.hasNext()) { + IFigure f = cast(IFigure)iterator.next(); + Dimension pref = transposer.t(getChildSize(f, wHint, hHint)); + Rectangle r = new Rectangle(0, 0, pref.width, pref.height); + + if (data.rowCount > 0) { + if (data.rowWidth + pref.width > data.maxWidth) + layoutRow(parent); + } + r.x = data.rowX; + r.y = data.rowY; + dx = r.width + getMinorSpacing(); + data.rowX += dx; + data.rowWidth += dx; + data.rowHeight = Math.max(data.rowHeight, r.height); + data.row [data.rowCount] = f; + data.bounds[data.rowCount] = r; + data.rowCount++; + i++; + } + if (data.rowCount !is 0) + layoutRow(parent); + data = null; +} + +/** + * Layouts one row of components. This is done based on the layout's orientation, minor + * alignment and major alignment. + * + * @param parent the parent figure + * @since 2.0 + */ +protected void layoutRow(IFigure parent) { + int majorAdjustment = 0; + int minorAdjustment = 0; + int correctMajorAlignment = majorAlignment; + int correctMinorAlignment = minorAlignment; + + majorAdjustment = data.area.width - data.rowWidth + getMinorSpacing(); + + switch (correctMajorAlignment) { + case ALIGN_LEFTTOP: + majorAdjustment = 0; + break; + case ALIGN_CENTER: + majorAdjustment /= 2; + break; + case ALIGN_RIGHTBOTTOM: + break; + } + + for (int j = 0; j < data.rowCount; j++) { + if (fill) { + data.bounds[j].height = data.rowHeight; + } else { + minorAdjustment = data.rowHeight - data.bounds[j].height; + switch (correctMinorAlignment) { + case ALIGN_LEFTTOP: + minorAdjustment = 0; + break; + case ALIGN_CENTER: + minorAdjustment /= 2; + break; + case ALIGN_RIGHTBOTTOM: + break; + } + data.bounds[j].y += minorAdjustment; + } + data.bounds[j].x += majorAdjustment; + + setBoundsOfChild(parent, data.row[j], transposer.t(data.bounds[j])); + } + data.rowY += getMajorSpacing() + data.rowHeight; + initRow(); +} + +/** + * Sets the given bounds for the child figure input. + * + * @param parent the parent figure + * @param child the child figure + * @param bounds the size of the child to be set + * @since 2.0 + */ +protected void setBoundsOfChild(IFigure parent, IFigure child, Rectangle bounds) { + parent.getClientArea(Rectangle.SINGLETON); + bounds.translate(Rectangle.SINGLETON.x, Rectangle.SINGLETON.y); + child.setBounds(bounds); +} + +/** + * Sets flag based on layout orientation. If in horizontal orientation, all figures will + * have the same height. If in vertical orientation, all figures will have the same width. + * + * @param value fill state desired + * @since 2.0 + */ +public void setStretchMinorAxis(bool value) { + fill = value; +} + +/** + * Sets the orientation of the layout. + * + * @param flag true if this layout should be horizontal + * @since 2.0 + */ +public void setHorizontal(bool flag) { + if (horizontal is flag) return; + invalidate(); + horizontal = flag; + transposer.setEnabled(!horizontal); +} + +/** + * Sets the alignment for an entire row/column within the parent figure. + *

+ * Possible values are : + *

    + *
  • {@link #ALIGN_CENTER} + *
  • {@link #ALIGN_LEFTTOP} + *
  • {@link #ALIGN_RIGHTBOTTOM} + *
+ * + * @param align the major alignment + * @since 2.0 + */ +public void setMajorAlignment(int align_) { + majorAlignment = align_; +} + +/** + * Sets the spacing in pixels to be used between children in the direction parallel to the + * layout's orientation. + * + * @param n the major spacing + * @since 2.0 + */ +public void setMajorSpacing(int n) { + majorSpacing = n; +} + +/** + * Sets the alignment to be used within a row/column. + *

+ * Possible values are : + *

    + *
  • {@link #ALIGN_CENTER} + *
  • {@link #ALIGN_LEFTTOP} + *
  • {@link #ALIGN_RIGHTBOTTOM} + *
+ * + * @param align the minor alignment + * @since 2.0 + */ +public void setMinorAlignment(int align_) { + minorAlignment = align_; +} + +/** + * Sets the spacing to be used between children within a row/column. + * + * @param n the minor spacing + * @since 2.0 + */ +public void setMinorSpacing(int n) { + minorSpacing = n; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FocusBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FocusBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FocusBorder; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.AbstractBorder; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.ColorConstants; + +/** + * A Border that looks like the system's focus rectangle. + */ +public class FocusBorder + : AbstractBorder +{ + +/** + * Constructs a new FocusBorder. + */ +public this() { } + +/** + * @see dwtx.draw2d.Border#getInsets(IFigure) + */ +public Insets getInsets(IFigure figure) { + return new Insets(1); +} + +/** + * @see dwtx.draw2d.Border#isOpaque() + */ +public bool isOpaque() { + return true; +} + +/** + * Paints a focus rectangle. + * @see dwtx.draw2d.Border#paint(IFigure, Graphics, Insets) + */ +public void paint(IFigure figure, Graphics graphics, Insets insets) { + tempRect.setBounds(getPaintRectangle(figure, insets)); + tempRect.width--; + tempRect.height--; + graphics.setForegroundColor(ColorConstants.black); + graphics.setBackgroundColor(ColorConstants.white); + graphics.drawFocus(tempRect); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FocusEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FocusEvent.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FocusEvent; + +import dwt.dwthelper.utils; +import dwtx.draw2d.IFigure; + +/** + * An event that occurs when an {@link dwtx.draw2d.IFigure} gains or loses focus. + */ +public class FocusEvent { + +/** The figure losing focus */ +public IFigure loser; +/** The figure gaining focus */ +public IFigure gainer; + +/** + * Constructs a new FocusEvent. + * @param loser the figure losing focus + * @param gainer the figure gaining focus + */ +public this(IFigure loser, IFigure gainer) { + this.loser = loser; + this.gainer = gainer; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FocusListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FocusListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FocusListener; + +import dwt.dwthelper.utils; +import dwtx.draw2d.FocusEvent; + +/** + * A listener interface for receiving {@link FocusEvent FocusEvents}. + */ +public interface FocusListener { + +/** + * Called when the listened to object has gained focus. + * @param fe The FocusEvent object + */ +void focusGained(FocusEvent fe); + +/** + * Called when the listened to object has lost focus. + * @param fe The FocusEvent object + */ +void focusLost(FocusEvent fe); + +/** + * An empty implementation of FocusListener for convenience. + */ +public class Stub + : FocusListener +{ + /** + * @see dwtx.draw2d.FocusListener#focusGained(FocusEvent) + */ + public void focusGained(FocusEvent fe) { } + /** + * @see dwtx.draw2d.FocusListener#focusLost(FocusEvent) + */ + public void focusLost(FocusEvent fe) { } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FocusTraverseManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FocusTraverseManager.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,195 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FocusTraverseManager; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.IFigure; + +/** + * This class is a helper to the {@link SWTEventDispatcher}. It handles the task of + * determining which Figure will gain focus upon a tab/shift-tab. It also keeps track of + * the Figure with current focus. + *

+ * Note: When a Canvas with a {@link LightweightSystem} gains focus, it gives focus to the + * child Figure who had focus when this Canvas lost focus. If the canvas is gaining focus + * for the first time, focus is given to its first child Figure. + */ +public class FocusTraverseManager { + +IFigure currentFocusOwner; + +/** + * Default constructor. + */ +public this() { } + +private IFigure findDeepestRightmostChildOf(IFigure fig) { + while (fig.getChildren().size() !is 0) { + fig = cast(IFigure)fig.getChildren().get(fig.getChildren().size() - 1); + } + return fig; +} + +/** + * Returns the IFigure that will receive focus upon a 'tab' traverse event. + * + * @param root the {@link LightweightSystem LightweightSystem's} root figure + * @param prevFocus the IFigure who currently owns focus + * @return the next focusable figure + */ +public IFigure getNextFocusableFigure(IFigure root, IFigure prevFocus) { + bool found = false; + IFigure nextFocus = prevFocus; + + /* + * If no Figure currently has focus, apply focus to root's first focusable child. + */ + if (prevFocus is null) { + if (root.getChildren().size() !is 0) { + nextFocus = (cast(IFigure)root.getChildren().get(0)); + if (isFocusEligible(nextFocus)) + return nextFocus; + } else + return null; + } + + int siblingPos = nextFocus.getParent().getChildren().indexOf(cast(Object)nextFocus); + while (!found) { + IFigure parent = nextFocus.getParent(); + + /* + * Figure traversal is implemented using the pre-order left to right + * tree traversal algorithm. + * + * If the focused sibling has children, traverse to its leftmost child. + * If the focused sibling has no children, traverse to the sibling + * to its right. + * If there is no sibling to the right, go up the tree until a node + * with un-traversed siblings is found. + */ + List siblings = parent.getChildren(); + + if (nextFocus.getChildren().size() !is 0) { + nextFocus = cast(IFigure)nextFocus.getChildren().get(0); + siblingPos = 0; + if (isFocusEligible(nextFocus)) + found = true; + } else if (siblingPos < siblings.size() - 1) { + nextFocus = (cast(IFigure)(siblings.get(++siblingPos))); + if (isFocusEligible(nextFocus)) + found = true; + } else { + bool untraversedSiblingFound = false; + while (!untraversedSiblingFound) { + IFigure p = nextFocus.getParent(); + IFigure gp = p.getParent(); + + if (gp !is null) { + int parentSiblingCount = gp.getChildren().size(); + int parentIndex = gp.getChildren().indexOf(cast(Object)p); + if (parentIndex < parentSiblingCount - 1) { + nextFocus = (cast(IFigure)p.getParent() + .getChildren().get(parentIndex + 1)); + siblingPos = parentIndex + 1; + untraversedSiblingFound = true; + if (isFocusEligible(nextFocus)) + found = true; + } else + nextFocus = p; + } else { + nextFocus = null; + untraversedSiblingFound = true; + found = true; + } + } + } + } + return nextFocus; +} + +/** + * Returns the IFigure that will receive focus upon a 'shift-tab' traverse event. + * + * @param root The {@link LightweightSystem LightweightSystem's} root figure + * @param prevFocus The IFigure who currently owns focus + * @return the previous focusable figure + */ +public IFigure getPreviousFocusableFigure(IFigure root, IFigure prevFocus) { + if (prevFocus is null) + return null; + + bool found = false; + IFigure nextFocus = prevFocus; + while (!found) { + IFigure parent = nextFocus.getParent(); + + /* + * At root, return null to indicate traversal + * is complete. + */ + if (parent is null) + return null; + + List siblings = parent.getChildren(); + int siblingPos = siblings.indexOf(cast(Object)nextFocus); + + /* + * Figure traversal is implemented using the post-order right to left + * tree traversal algorithm. + * + * Find the rightmost child. + * If this child is focusable, return it + * If not focusable, traverse to its sibling and repeat. + * If there is no sibling, traverse its parent. + */ + if (siblingPos !is 0) { + IFigure child = + findDeepestRightmostChildOf(cast(IFigure)siblings.get(siblingPos - 1)); + if (isFocusEligible(child)) { + found = true; + nextFocus = child; + } else if ((cast(Object)child).opEquals(cast(Object)nextFocus)) { + if (isFocusEligible(nextFocus)) + found = true; + } else + nextFocus = child; + } else { + nextFocus = parent; + if (isFocusEligible(nextFocus)) + found = true; + } + } + return nextFocus; +} + +/** + * @return the figure that currently has focus + */ +public IFigure getCurrentFocusOwner() { + return currentFocusOwner; +} + +private bool isFocusEligible(IFigure fig) { + return (fig !is null && fig.isFocusTraversable() && fig.isShowing()); +} + +/** + * Sets the currently focused figure. + * @param fig the figure to get focus + */ +public void setCurrentFocusOwner(IFigure fig) { + currentFocusOwner = fig; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FrameBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FrameBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FrameBorder; + +import dwt.dwthelper.utils; + +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwtx.draw2d.CompoundBorder; +import dwtx.draw2d.LabeledBorder; +import dwtx.draw2d.SchemeBorder; +import dwtx.draw2d.ColorConstants; +import dwtx.draw2d.TitleBarBorder; + + +/** + * Provides for a frame-like border which contains a title bar for holding the title of a + * Figure. + */ +public class FrameBorder + : CompoundBorder + , LabeledBorder +{ + +/** + * The border scheme that determines the border highlight and shadow colors, as well as + * the border width (3). + */ +protected static const SchemeBorder.Scheme + SCHEME_FRAME; +static this(){ + SCHEME_FRAME = new SchemeBorder.Scheme( + [ + ColorConstants.button, + ColorConstants.buttonLightest, + ColorConstants.button + ], + [ + ColorConstants.buttonDarkest, + ColorConstants.buttonDarker, + ColorConstants.button + ] + ); +} + +private void instanceInit(){ + createBorders(); +} + +/** + * Constructs a FrameBorder with its label set to the name of the {@link TitleBarBorder} + * class. + * + * @since 2.0 + */ +public this() { + instanceInit(); +} + +/** + * Constructs a FrameBorder with the title set to the passed String. + * + * @param label label or title of the frame. + * @since 2.0 + */ +public this(String label) { + instanceInit(); + setLabel(label); +} + +/** + * Creates the necessary borders for this FrameBorder. The inner border is a + * {@link TitleBarBorder}. The outer border is a {@link SchemeBorder}. + * + * @since 2.0 + */ +protected void createBorders() { + inner = new TitleBarBorder(); + outer = new SchemeBorder(SCHEME_FRAME); +} + +/** + * Returns the inner border of this FrameBorder, which contains the label for the + * FrameBorder. + * + * @return the border holding the label. + * @since 2.0 + */ +protected LabeledBorder getLabeledBorder() { + return cast(LabeledBorder)inner; +} + +/** + * @return the label for this border + */ +public String getLabel() { + return getLabeledBorder().getLabel(); +} + +/** + * Sets the label for this border. + * @param label the label + */ +public void setLabel(String label) { + getLabeledBorder().setLabel(label); +} + +/** + * Sets the font for this border's label. + * @param font the font + */ +public void setFont(Font font) { + getLabeledBorder().setFont(font); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FreeformFigure.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FreeformFigure.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FreeformFigure; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.FreeformListener; + + +/** + * A figure that allows its children to extend into negative coordinates. This figure + * must be placed in a {@link dwtx.draw2d.FreeformViewport}. Also, you shouldn't + * call {@link IFigure#setBounds(Rectangle)} on this figure. The bounds will be + * calculated based on the extent of its children. A FreeformFigure's bounds will be the + * smallest rectangle that will contain all of its children. + */ +public interface FreeformFigure + : IFigure +{ + +/** + * Adds a FreeformListener to this FreeformFigure. + * @param listener the listener + */ +void addFreeformListener(FreeformListener listener); + +/** + * Notifies listeners that the freeform extent (i.e. bounds) has changed. + */ +void fireExtentChanged(); + +/** + * Returns the freeform extent, essentially the bounds of the FreeformFigure. This is + * based on the extent of its children. + * @return the freeform extent + */ +Rectangle getFreeformExtent(); + +/** + * Removes the given listener from this FreeformFigure. + * @param listener the listener + */ +void removeFreeformListener(FreeformListener listener); + +/** + * Sets the freeform bounds of this FreeformFigure. + * @param bounds the new freeform bounds + */ +void setFreeformBounds(Rectangle bounds); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FreeformHelper.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FreeformHelper.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FreeformHelper; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.FreeformListener; +import dwtx.draw2d.FreeformFigure; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.FigureListener; + +class FreeformHelper + : FreeformListener +{ + +class ChildTracker : FigureListener { + public void figureMoved(IFigure source) { + invalidate(); + } +} + +private FreeformFigure host; +private Rectangle freeformExtent; +private FigureListener figureListener; + +private void instanceInit(){ + figureListener = new ChildTracker(); +} +this(FreeformFigure host) { + instanceInit(); + this.host = host; +} + +public Rectangle getFreeformExtent() { + if (freeformExtent !is null) + return freeformExtent; + Rectangle r; + List children = host.getChildren(); + for (int i = 0; i < children.size(); i++) { + IFigure child = cast(IFigure)children.get(i); + if (null !is cast(FreeformFigure) child ) + r = (cast(FreeformFigure) child).getFreeformExtent(); + else + r = child.getBounds(); + if (freeformExtent is null) + freeformExtent = r.getCopy(); + else + freeformExtent.union_(r); + } + Insets insets = host.getInsets(); + if (freeformExtent is null) + freeformExtent = new Rectangle(0, 0, insets.getWidth(), insets.getHeight()); + else { + host.translateToParent(freeformExtent); + freeformExtent.expand(insets); + } +// System.out.println("New extent calculated for " + host + " = " + freeformExtent); + return freeformExtent; +} + +public void hookChild(IFigure child) { + invalidate(); + if (null !is cast(FreeformFigure)child ) + (cast(FreeformFigure)child).addFreeformListener(this); + else + child.addFigureListener(figureListener); +} + +void invalidate() { + freeformExtent = null; + host.fireExtentChanged(); + if (host.getParent() !is null) + host.getParent().revalidate(); + else + host.revalidate(); +} + +public void notifyFreeformExtentChanged() { + //A childs freeform extent has changed, therefore this extent must be recalculated + invalidate(); +} + +public void setFreeformBounds(Rectangle bounds) { + host.setBounds(bounds); + bounds = bounds.getCopy(); + host.translateFromParent(bounds); + List children = host.getChildren(); + for (int i = 0; i < children.size(); i++) { + IFigure child = cast(IFigure)children.get(i); + if (null !is cast(FreeformFigure)child ) + (cast(FreeformFigure) child).setFreeformBounds(bounds); + } +} + +public void unhookChild(IFigure child) { + invalidate(); + if (null !is cast(FreeformFigure)child ) + (cast(FreeformFigure)child).removeFreeformListener(this); + else + child.removeFigureListener(figureListener); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FreeformLayer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FreeformLayer.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FreeformLayer; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Rectangle; + +import dwtx.draw2d.Layer; +import dwtx.draw2d.FreeformFigure; +import dwtx.draw2d.FreeformListener; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.FreeformHelper; + +/** + * A Layer that can extend in all 4 directions. + */ +public class FreeformLayer + : Layer + , FreeformFigure +{ + +private FreeformHelper helper; + +private void instanceInit(){ + helper = new FreeformHelper(this); +} + +this(){ + helper = new FreeformHelper(this); +} +/** + * @see IFigure#add(IFigure, Object, int) + */ +public void add(IFigure child, Object constraint, int index) { + super.add(child, constraint, index); + helper.hookChild(child); +} + +/** + * @see FreeformFigure#addFreeformListener(FreeformListener) + */ +public void addFreeformListener(FreeformListener listener) { + addListener(FreeformListener.classinfo, cast(Object)listener); +} + +/** + * @see FreeformFigure#fireExtentChanged() + */ +public void fireExtentChanged() { + Iterator iter = getListeners(FreeformListener.classinfo); + while (iter.hasNext()) + (cast(FreeformListener)iter.next()) + .notifyFreeformExtentChanged(); +} + +/** + * Overrides to do nothing. + * @see Figure#fireMoved() + */ +protected void fireMoved() { } + +/** + * @see FreeformFigure#getFreeformExtent() + */ +public Rectangle getFreeformExtent() { + return helper.getFreeformExtent(); +} + +/** + * @see Figure#primTranslate(int, int) + */ +public void primTranslate(int dx, int dy) { + bounds.x += dx; + bounds.y += dy; +} + +/** + * @see IFigure#remove(IFigure) + */ +public void remove(IFigure child) { + helper.unhookChild(child); + super.remove(child); +} + +/** + * @see FreeformFigure#removeFreeformListener(FreeformListener) + */ +public void removeFreeformListener(FreeformListener listener) { + removeListener(FreeformListener.classinfo, cast(Object)listener); +} + +/** + * @see FreeformFigure#setFreeformBounds(Rectangle) + */ +public void setFreeformBounds(Rectangle bounds) { + helper.setFreeformBounds(bounds); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FreeformLayeredPane.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FreeformLayeredPane.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FreeformLayeredPane; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.LayeredPane; +import dwtx.draw2d.FreeformFigure; +import dwtx.draw2d.FreeformHelper; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.FreeformListener; + +/** + * A LayeredPane that contains {@link dwtx.draw2d.FreeformLayer FreeformLayers}. + */ +public class FreeformLayeredPane + : LayeredPane + , FreeformFigure +{ +private FreeformHelper helper; + +/** + * Constructs a new FreeformLayeredPane. + */ +public this() { + helper = new FreeformHelper(this); + setLayoutManager(null); +} + +/** + * @see IFigure#add(IFigure, Object, int) + */ +public void add(IFigure child, Object constraint, int index) { + super.add(child, constraint, index); + helper.hookChild(child); +} + +/** + * @see FreeformFigure#addFreeformListener(FreeformListener) + */ +public void addFreeformListener(FreeformListener listener) { + addListener(FreeformListener.classinfo, cast(Object)listener); +} + +/** + * @see FreeformFigure#fireExtentChanged() + */ +public void fireExtentChanged() { + Iterator iter = getListeners(FreeformListener.classinfo); + while (iter.hasNext()) + (cast(FreeformListener)iter.next()) + .notifyFreeformExtentChanged(); +} + +/** + * Overrides to do nothing. + * @see Figure#fireMoved() + */ +protected void fireMoved() { } + +/** + * Returns the FreeformHelper. + * @return the FreeformHelper + */ +protected FreeformHelper getFreeformHelper() { + return helper; +} + +/** + * @see FreeformFigure#getFreeformExtent() + */ +public Rectangle getFreeformExtent() { + return helper.getFreeformExtent(); +} + +/** + * @see Figure#primTranslate(int, int) + */ +protected void primTranslate(int dx, int dy) { + bounds.x += dx; + bounds.y += dy; +} + +/** + * @see IFigure#remove(IFigure) + */ +public void remove(IFigure child) { + helper.unhookChild(child); + super.remove(child); +} + +/** + * @see FreeformFigure#removeFreeformListener(FreeformListener) + */ +public void removeFreeformListener(FreeformListener listener) { + removeListener(FreeformListener.classinfo, cast(Object)listener); +} + +/** + * @see FreeformFigure#setFreeformBounds(Rectangle) + */ +public void setFreeformBounds(Rectangle bounds) { + helper.setFreeformBounds(bounds); +} + +/** + * Calls {@link Figure#fireMoved() super.fireMoved()}. + */ +protected void superFireMoved() { + super.fireMoved(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FreeformLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FreeformLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FreeformLayout; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.XYLayout; +import dwtx.draw2d.IFigure; + +/** + * A layout for {@link dwtx.draw2d.FreeformFigure FreeformFigures}. + */ +public class FreeformLayout + : XYLayout +{ + +/** + * Returns the point (0,0) as the origin. + * @see XYLayout#getOrigin(IFigure) + */ +public Point getOrigin(IFigure figure) { + return new Point(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FreeformListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FreeformListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FreeformListener; + +import dwt.dwthelper.utils; + +/** + * Listens for extent changes in {@link dwtx.draw2d.FreeformFigure FreeformFigures}. + * Created on :Oct 4, 2002 + * @author hudsonr + * @since 2.0 + */ +public interface FreeformListener { + +/** + * Called when the extent of the FreeformFigure has changed. + */ +void notifyFreeformExtentChanged(); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/FreeformViewport.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FreeformViewport.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.FreeformViewport; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Viewport; +import dwtx.draw2d.ViewportLayout; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.FreeformFigure; + +/** + * A viewport for {@link dwtx.draw2d.FreeformFigure FreeformFigures}. + * FreeformFigures can only reside in this type of viewport. + */ +public class FreeformViewport + : Viewport +{ + +class FreeformViewportLayout + : ViewportLayout +{ + protected Dimension calculatePreferredSize(IFigure parent, int wHint, int hHint) { + getContents().validate(); + wHint = Math.max(0, wHint); + hHint = Math.max(0, hHint); + return (cast(FreeformFigure)getContents()) + .getFreeformExtent() + .getExpanded(getInsets()) + .union_(0, 0) + .union_(wHint - 1, hHint - 1) + .getSize(); + } + + protected bool isSensitiveHorizontally(IFigure parent) { + return true; + } + + protected bool isSensitiveVertically(IFigure parent) { + return true; + } + + + public void layout(IFigure figure) { + //Do nothing, contents updates itself. + } +} + +/** + * Constructs a new FreeformViewport. This viewport must use graphics translation to + * scroll the FreeformFigures inside of it. + */ +public this() { + super(true); //Must use graphics translate to scroll freeforms. + setLayoutManager(new FreeformViewportLayout()); +} + +/** + * Readjusts the scrollbars. In doing so, it gets the freeform extent of the contents and + * unions this rectangle with this viewport's client area, then sets the contents freeform + * bounds to be this unioned rectangle. Then proceeds to set the scrollbar values based + * on this new information. + * @see Viewport#readjustScrollBars() + */ +protected void readjustScrollBars() { + if (getContents() is null) + return; + if (!( null !is cast(FreeformFigure)getContents() )) + return; + FreeformFigure ff = cast(FreeformFigure)getContents(); + Rectangle clientArea = getClientArea(); + Rectangle bounds = ff.getFreeformExtent().getCopy(); + bounds.union_(0, 0, clientArea.width, clientArea.height); + ff.setFreeformBounds(bounds); + + getVerticalRangeModel().setAll(bounds.y, clientArea.height, bounds.bottom()); + getHorizontalRangeModel().setAll(bounds.x, clientArea.width, bounds.right()); +} + +/** + * Returns true. + * @see Figure#useLocalCoordinates() + */ +protected bool useLocalCoordinates() { + return true; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Graphics.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Graphics.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,934 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Graphics; + +import dwt.dwthelper.utils; + + + +import dwt.DWT; +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.FontMetrics; +import dwt.graphics.GC; +import dwt.graphics.Image; +import dwt.graphics.Path; +import dwt.graphics.Pattern; +import dwt.graphics.TextLayout; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.Rectangle; + +/** + * The Graphics class allows you to draw to a surface. The drawXxx() methods that pertain + * to shapes draw an outline of the shape, whereas the fillXxx() methods fill in the shape. + * Also provides for drawing text, lines and images. + */ +public abstract class Graphics { + +/** + * @see DWT#LINE_CUSTOM + */ +public static final int LINE_CUSTOM = DWT.LINE_CUSTOM; + +/** + * @see DWT#LINE_DASH + */ +public static final int LINE_DASH = DWT.LINE_DASH; + +/** + * @see DWT#LINE_DASHDOT + */ +public static final int LINE_DASHDOT = DWT.LINE_DASHDOT; + +/** + * @see DWT#LINE_DASHDOTDOT + */ +public static final int LINE_DASHDOTDOT = DWT.LINE_DASHDOTDOT; + +/** + * @see DWT#LINE_DOT + */ +public static final int LINE_DOT = DWT.LINE_DOT; + +/** + * @see DWT#LINE_SOLID + */ +public static final int LINE_SOLID = DWT.LINE_SOLID; + +/** + * Sets the clip region to the given rectangle. Anything outside this rectangle will not + * be drawn. + * @param r the clip rectangle + */ +public abstract void clipRect(Rectangle r); + +/** + * Disposes this object, releasing any resources. + */ +public abstract void dispose(); + +/** + * Draws the outline of an arc located at (x,y) with width w and height h. + * The starting angle of the arc (specified in degrees) is offset and length + * is the arc's angle (specified in degrees). + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param offset the start angle + * @param length the length of the arc + */ +public abstract void drawArc(int x, int y, int w, int h, int offset, int length); + +/** + * @see #drawArc(int, int, int, int, int, int) + */ +public final void drawArc(Rectangle r, int offset, int length) { + drawArc(r.x, r.y, r.width, r.height, offset, length); +} + +/** + * Draws a focus rectangle. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ +public abstract void drawFocus(int x, int y, int w, int h); + +/** + * @see #drawFocus(int, int, int, int) + */ +public final void drawFocus(Rectangle r) { + drawFocus(r.x, r.y, r.width, r.height); +} + +/** + * Draws the given Image at the location (x,y). + * @param srcImage the Image + * @param x the x coordinate + * @param y the y coordinate + */ +public abstract void drawImage(Image srcImage, int x, int y); + +/** + * Draws a rectangular section of the given Image to the specified rectangular reagion on + * the canvas. The section of the image bounded by the rectangle (x1,y1,w1,h1) is copied + * to the section of the canvas bounded by the rectangle (x2,y2,w2,h2). If these two + * sizes are different, scaling will occur. + * + * @param srcImage the image + * @param x1 the x coordinate of the source + * @param y1 the y coordinate of the source + * @param w1 the width of the source + * @param h1 the height of the source + * @param x2 the x coordinate of the destination + * @param y2 the y coordinate of the destination + * @param w2 the width of the destination + * @param h2 the height of the destination + */ +public abstract void drawImage(Image srcImage, int x1, int y1, int w1, int h1, + int x2, int y2, int w2, int h2); + +/** + * Draws the given image at a point. + * @param image the image to draw + * @param p where to draw the image + * @see #drawImage(Image, int, int) + */ +public final void drawImage(Image image, Point p) { + drawImage(image, p.x, p.y); +} + +/** + * @see #drawImage(Image, int, int, int, int, int, int, int, int) + */ +public final void drawImage(Image srcImage, Rectangle src, Rectangle dest) { + drawImage(srcImage, src.x, src.y, src.width, src.height, + dest.x, dest.y, dest.width, dest.height); +} + +/** + * Draws a line between the points (x1,y1) and (x2,y2) using the + * foreground color. + * @param x1 the x coordinate for the first point + * @param y1 the y coordinate for the first point + * @param x2 the x coordinate for the second point + * @param y2 the y coordinate for the second point + */ +public abstract void drawLine(int x1, int y1, int x2, int y2); + +/** + * @see #drawLine(int, int, int, int) + */ +public final void drawLine(Point p1, Point p2) { + drawLine(p1.x, p1.y, p2.x, p2.y); +} + +/** + * Draws the outline of an ellipse that fits inside the rectangle with the given + * properties using the foreground color. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ +public abstract void drawOval(int x, int y, int w, int h); + +/** + * Draws an oval inside the given rectangle using the current foreground color. + * @param r the rectangle circumscribing the oval to be drawn + * @see #drawOval(int, int, int, int) + */ +public final void drawOval(Rectangle r) { + drawOval(r.x, r.y, r.width, r.height); +} + +/** + * Draws the given path. + * @param path the path to draw + * @since 3.1 + */ +public void drawPath(Path path) { + subclassFunctionMission(); +} + +/** + * Draws a pixel, using the foreground color, at the specified point (x, + * y). + *

+ * Note that the current line attributes do not affect this + * operation. + *

+ * + * @param x the point's x coordinate + * @param y the point's y coordinate + * + */ +public void drawPoint(int x, int y) { + drawLine(x, y, x, y); +} + +/** + * Draws a closed polygon defined by the given Integer array containing the vertices in + * x,y order. The first and last points in the list will be connected. + * @param points the vertices + */ +public void drawPolygon(int[] points) { + drawPolygon(getPointList(points)); +} + +/** + * Draws a closed polygon defined by the given PointList containing the + * vertices. The first and last points in the list will be connected. + * @param points the vertices + */ +public abstract void drawPolygon(PointList points); + +/** + * Draws a polyline defined by the given Integer array containing the vertices in x,y + * order. The first and last points in the list will not be connected. + * @param points the vertices + */ +public void drawPolyline(int[] points) { + drawPolyline(getPointList(points)); +} + +/** + * Draws a polyline defined by the given PointList containing the vertices. + * The first and last points in the list will not be connected. + * @param points the vertices + */ +public abstract void drawPolyline(PointList points); + +/** + * Draws a rectangle whose top-left corner is located at the point (x,y) with the given + * width and height. + * + * @param x the x coordinate + * @param y the y coordinate + * @param width the width + * @param height the height + */ +public abstract void drawRectangle(int x, int y, int width, int height); + +/** + * Draws the given rectangle using the current foreground color. + * @param r the rectangle to draw + * @see #drawRectangle(int, int, int, int) + */ +public final void drawRectangle(Rectangle r) { + drawRectangle(r.x, r.y, r.width, r.height); +} + +/** + * Draws a rectangle with rounded corners using the foreground color. arcWidth and + * arcHeight represent the horizontal and vertical diameter of the corners. + * + * @param r the rectangle + * @param arcWidth the arc width + * @param arcHeight the arc height + */ +public abstract void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight); + +/** + * Draws the given string using the current font and foreground color. No tab expansion or + * carriage return processing will be performed. The background of the string will be + * transparent. + * + * @param s the string + * @param x the x coordinate + * @param y the y coordinate + */ +public abstract void drawString(String s, int x, int y); + +/** + * @see #drawString(String, int, int) + */ +public final void drawString(String s, Point p) { + drawString(s, p.x, p.y); +} + + +/** + * Draws the given string using the current font and foreground color. Tab expansion and + * carriage return processing are performed. The background of the text will be + * transparent. + * + * @param s the text + * @param x the x coordinate + * @param y the y coordinate + */ +public abstract void drawText(String s, int x, int y); + +/** + * Draws a string using the specified styles. The styles are defined by {@link + * GC#drawText(String, int, int, int)}. + * @param s the String to draw + * @param x the x location + * @param y the y location + * @param style the styles used to render the string + * @since 3.0 + */ +public void drawText(String s, int x, int y, int style) { + subclassFunctionMission(); +} + +/** + * @see #drawText(String, int, int) + */ +public final void drawText(String s, Point p) { + drawText(s, p.x, p.y); +} + +/** + * Draws a string using the specified styles. The styles are defined by {@link + * GC#drawText(String, int, int, int)}. + * @param s the String to draw + * @param p the point at which to draw the string + * @param style the styles used to render the string + * @since 3.0 + */ +public final void drawText(String s, Point p, int style) { + drawText(s, p.x, p.y, style); +} + +/** + * Renders the specified TextLayout to this Graphics. + * @since 3.0 + * @param layout the TextLayout + * @param x the x coordinate + * @param y the y coordinate + */ +public final void drawTextLayout(TextLayout layout, int x, int y) { + drawTextLayout(layout, x, y, -1, -1, null, null); +} + +/** + * @param x the x location + * @param y the y location + * @param layout the TextLayout being rendered + * @param selectionStart the start of selection + * @param selectionEnd the end of selection + * @param selectionForeground the foreground selection color + * @param selectionBackground the background selection color + * @see Graphics#drawTextLayout(TextLayout, int, int) + */ +public void drawTextLayout(TextLayout layout, int x, int y, int selectionStart, + int selectionEnd, Color selectionForeground, Color selectionBackground) { + subclassFunctionMission(); +} + +/** + * Fills the interior of an arc located at (x,y) with width w and + * height h. The starting angle of the arc (specified in degrees) is offset + * and length is the arc's angle (specified in degrees). + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param offset the start angle + * @param length the length of the arc + */ +public abstract void fillArc(int x, int y, int w, int h, int offset, int length); + +/** + * @see #fillArc(int, int, int, int, int, int) + */ +public final void fillArc(Rectangle r, int offset, int length) { + fillArc(r.x, r.y, r.width, r.height, offset, length); +} + +/** + * Fills the the given rectangle with a gradient from the foreground color to the + * background color. If vertical is true, the gradient will go from + * top to bottom. Otherwise, it will go from left to right. + * background color. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param vertical whether the gradient should be vertical + */ +public abstract void fillGradient(int x, int y, int w, int h, bool vertical); + +/** + * @see #fillGradient(int, int, int, int, bool) + */ +public final void fillGradient(Rectangle r, bool vertical) { + fillGradient(r.x, r.y, r.width, r.height, vertical); +} + +/** + * Fills an ellipse that fits inside the rectangle with the given properties using the + * background color. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ +public abstract void fillOval(int x, int y, int w, int h); + +/** + * @see #fillOval(int, int, int, int) + */ +public final void fillOval(Rectangle r) { + fillOval(r.x, r.y, r.width, r.height); +} + +/** + * Fills the given path. + * @param path the path to fill + * @since 3.1 + */ +public void fillPath(Path path) { + subclassFunctionMission(); +} + +/** + * Fills a closed polygon defined by the given Integer array containing the + * vertices in x,y order. The first and last points in the list will be connected. + * @param points the vertices + */ +public void fillPolygon(int[] points) { + fillPolygon(getPointList(points)); +} + +/** + * Fills a closed polygon defined by the given PointList containing the + * vertices. The first and last points in the list will be connected. + * @param points the vertices + */ +public abstract void fillPolygon(PointList points); + +/** + * Fills a rectangle whose top-left corner is located at the point (x,y) with the given + * width and height. + * + * @param x the x coordinate + * @param y the y coordinate + * @param width the width + * @param height the height + */ +public abstract void fillRectangle(int x, int y, int width, int height); + +/** + * Fills the given rectangle using the current background color. + * @param r the rectangle to fill + * @see #fillRectangle(int, int, int, int) + */ +public final void fillRectangle(Rectangle r) { + fillRectangle(r.x, r.y, r.width, r.height); +} + +/** + * Fills a rectangle with rounded corners using the background color. arcWidth and + * arcHeight represent the horizontal and vertical diameter of the corners. + * + * @param r the rectangle + * @param arcWidth the arc width + * @param arcHeight the arc height + */ +public abstract void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight); + +/** + * Draws the given string using the current font and foreground color. No tab expansion or + * carriage return processing will be performed. The background of the string will be + * filled with the current background color. + * + * @param s the string + * @param x the x coordinate + * @param y the y coordinate + */ +public abstract void fillString(String s, int x, int y); + +/** + * @see #fillString(String, int, int) + */ +public final void fillString(String s, Point p) { + fillString(s, p.x, p.y); +} + +/** + * Draws the given string using the current font and foreground color. Tab expansion and + * carriage return processing are performed. The background of the text will be filled + * with the current background color. + * + * @param s the text + * @param x the x coordinate + * @param y the y coordinate + */ +public abstract void fillText(String s, int x, int y); + +/** + * @see #fillText(String, int, int) + */ +public final void fillText(String s, Point p) { + fillText(s, p.x, p.y); +} + +/** + * Returns the current absolute scaling which will be applied to the underlying Device + * when painting to this Graphics. The default value is 1.0. + * @since 3.0 + * @return the effective absolute scaling factor + */ +public double getAbsoluteScale() { + return 1.0; +} + +/** + * Returns the current alpha value of the graphics. + * @return the alpha value + * @since 3.1 + */ +public int getAlpha() { + subclassFunctionMission(); + return 255; +} + +/** + * Returns the anti-aliasing setting value, which will be one of DWT.DEFAULT, + * DWT.OFF or DWT.ON. Note that this controls anti-aliasing for + * all non-text drawing operations. + * @see #getTextAntialias() + * @return the anti-alias setting + * @since 3.1 + */ +public int getAntialias() { + subclassFunctionMission(); + return DWT.DEFAULT; +} + +/** + * Returns the background color used for filling. + * @return the background color + */ +public abstract Color getBackgroundColor(); + +/** + * Modifies the given rectangle to match the clip region and returns that rectangle. + * @param rect the rectangle to hold the clip region + * @return the clip rectangle + */ +public abstract Rectangle getClip(Rectangle rect); + +/** + * Returns the fill rule, which will be one of DWT.FILL_EVEN_ODD or + * DWT.FILL_WINDING. + * @return the fill rule + * @since 3.1 + */ +public int getFillRule() { + subclassFunctionMission(); + return 0; +} + +/** + * Returns the font used to draw and fill text. + * @return the font + */ +public abstract Font getFont(); + +/** + * Returns the font metrics for the current font. + * @return the font metrics + */ +public abstract FontMetrics getFontMetrics(); + +/** + * Returns the foreground color used to draw lines and text. + * @return the foreground color + */ +public abstract Color getForegroundColor(); + +/** + * Returns the interpolation setting, which will be one of DWT.DEFAULT, + * DWT.NONE, DWT.LOW or DWT.HIGH. + * @return the interpolation setting + * @since 3.1 + */ +public int getInterpolation() { + subclassFunctionMission(); + return 0; +} + +/** + * Returns the current line cap style, which will be one of the constants + * DWT.CAP_FLAT, DWT.CAP_ROUND, or DWT.CAP_SQUARE. + * + * @return the cap style used for drawing lines + * @since 3.1 + */ +public int getLineCap() { + subclassFunctionMission(); + return DWT.CAP_FLAT; +} + + +/** + * Returns the line join style, which will be one of the constants + * DWT.JOIN_MITER, DWT.JOIN_ROUND, or + * DWT.JOIN_BEVEL. + * + * @since 3.1 + * @return the join style used for drawing lines + */ +public int getLineJoin() { + subclassFunctionMission(); + return DWT.JOIN_MITER; +} + +/** + * Returns the line style. + * @return the line style + */ +public abstract int getLineStyle(); +/** + * Returns the current line width. + * @return the line width + */ +public abstract int getLineWidth(); + +/** + * Returns a pointlist containing all the points from the integer array. + * @param points an integer array of x,y points + * @return the corresponding pointlist + */ +private PointList getPointList(int[] points) { + PointList pointList = new PointList(points.length / 2); + for (int i = 0; (i + 1) < points.length; i += 2) + pointList.addPoint(points[i], points[i + 1]); + return pointList; +} + +/** + * Returns the textual anti-aliasing setting value, which will be one of + * DWT.DEFAULT, DWT.OFF or DWT.ON. Note that this + * controls anti-aliasing only for text drawing operations. + * + * @see #getAntialias() + * @return the anti-aliasing setting + * @since 3.1 + */ +public int getTextAntialias() { + subclassFunctionMission(); + return DWT.DEFAULT; +} + +/** + * Returns true if this graphics object should use XOR mode with painting. + * @return whether XOR mode is turned on + */ +public abstract bool getXORMode(); + +/** + * Pops the previous state of this graphics object off the stack (if {@link #pushState()} + * has previously been called) and restores the current state to that popped state. + */ +public abstract void popState(); + +/** + * Pushes the current state of this graphics object onto a stack. + */ +public abstract void pushState(); + +/** + * Restores the previous state of this graphics object. + */ +public abstract void restoreState(); + +/** + * Rotates the coordinates by the given counter-clockwise angle. All subsequent painting + * will be performed in the resulting coordinates. Some functions are illegal when a + * rotated coordinates system is in use. To restore access to those functions, it is + * necessary to call restore or pop to return to a non rotated state. + * @param degrees the degrees to rotate + * @since 3.1 + */ +public void rotate(float degrees) { + subclassFunctionMission(); +} + +/** + * Scales this graphics object by the given amount. + * @param amount the scale factor + */ +public abstract void scale(double amount); + +/** + * Scales the graphics by the given horizontal and vertical components. + * @param horizontal the horizontal scaling factor + * @param vertical the vertical scaling factor + * @since 3.1 + */ +public void scale(float horizontal, float vertical) { + subclassFunctionMission(); +} + +/** + * Sets the alpha to the given value. Values may range from 0 to 255. A value + * of 0 is completely transparent. + * + * @param alpha an alpha value (0-255) + * @since 3.1 + */ +public void setAlpha(int alpha) { + subclassFunctionMission(); +} + +/** + * Sets the anti-aliasing value to the parameter, which must be one of + * DWT.DEFAULT, DWT.OFF or DWT.ON. Note that this + * controls anti-aliasing for all non-text drawing operations. + * + * @param value the anti-alias value + */ +public void setAntialias(int value) { + subclassFunctionMission(); +} + +/** + * Sets the background color. + * @param rgb the new background color + */ +public abstract void setBackgroundColor(Color rgb); + +/** + * Sets the pattern used for fill-type graphics operations. The pattern must not be + * disposed while it is being used by the graphics. + * @param pattern the background pattern + * @since 3.1 + */ +public void setBackgroundPattern(Pattern pattern) { + subclassFunctionMission(); +} + +/** + * Sets the area which can be affected by drawing operations to the specified + * Path. + + * @param path the clipping path + * @since 3.1 + */ +public void setClip(Path path) { + subclassFunctionMission(); +} + +/** + * Sets the clip rectangle. Painting will not occur outside this area. + * @param r the new clip rectangle + */ +public abstract void setClip(Rectangle r); + +/** + * Sets the fill rule to the given value, which must be one of + * DWT.FILL_EVEN_ODD or DWT.FILL_WINDING. + * @param rule the fill rule + * @since 3.1 + */ +public void setFillRule(int rule) { + subclassFunctionMission(); +} + +/** + * Sets the font. + * @param f the new font + */ +public abstract void setFont(Font f); + +/** + * Sets the foreground color. + * @param rgb the new foreground color + */ +public abstract void setForegroundColor(Color rgb); + +/** + * Sets the foreground pattern for draw and text operations. The pattern must not be + * disposed while it is being referenced by the graphics. + * @param pattern the foreground pattern + * @since 3.1 + */ +public void setForegroundPattern(Pattern pattern) { + subclassFunctionMission(); +} + +/** + * Sets the interpolation setting to the given value, which must be one of + * DWT.DEFAULT, DWT.NONE, DWT.LOW or + * DWT.HIGH. This setting is relevant when working with Images. + * @param interpolation the interpolation + * @since 3.1 + */ +public void setInterpolation(int interpolation) { + subclassFunctionMission(); +} + +/** + * Sets the line cap style to the argument, which must be one of the constants + * DWT.CAP_FLAT, DWT.CAP_ROUND, or DWT.CAP_SQUARE. + * @param cap the line cap + * @since 3.1 + */ +public void setLineCap(int cap) { + subclassFunctionMission(); +} + +/** + * Sets the dash pattern when the custom line style is in use. Because this + * feature is rarely used, the dash pattern may not be preserved when calling + * {@link #pushState()} and {@link #popState()}. + * @param dash the pixel pattern + * @since 3.1 + */ +public void setLineDash(int dash[]) { + subclassFunctionMission(); +} + +/** + * Sets the line join style to the argument, which must be one of the constants + * DWT.JOIN_MITER, DWT.JOIN_ROUND, or + * DWT.JOIN_BEVEL. + * @param join the join type + * @since 3.1 + */ +public void setLineJoin(int join) { + subclassFunctionMission(); +} + +/** + * Sets the line style to the argument, which must be one of the constants + * DWT.LINE_SOLID, DWT.LINE_DASH, DWT.LINE_DOT, + * DWT.LINE_DASHDOT or DWT.LINE_DASHDOTDOT. + * @param style the new style + */ +public abstract void setLineStyle(int style); + +/** + * Sets the line width. + * + * @param width the new width + */ +public abstract void setLineWidth(int width); + +/** + * Sets the textual anti-aliasing value to the parameter, which must be one of + * DWT.DEFAULT, DWT.OFF or DWT.ON. Note that this + * controls anti-aliasing only for all text drawing operations. + * + * @param value the textual anti-alias setting + * @since 3.1 + */ +public void setTextAntialias(int value) { + subclassFunctionMission(); +} + +/** + * Modifies the current transformation by shearing the graphics in the specified + * horizontal and vertical amounts. Shearing can be used to produce effects like Italic + * fonts. + * @param horz the horizontal shearing amount + * @param vert the vertical shearming amount + * @since 3.1 + */ +public void shear(float horz, float vert) { + subclassFunctionMission(); +} + +/** + * Sets the XOR mode. + * @param b the new XOR mode + */ +public abstract void setXORMode(bool b); + +private void subclassFunctionMission() { + throw new RuntimeException( "The class: " ~ this.classinfo.name //$NON-NLS-1$ + ~ " has not implemented this new graphics function"); //$NON-NLS-1$ +} + +/** + * Translates the receiver's coordinates by the specified x and y amounts. All + * subsequent painting will be performed in the resulting coordinate system. Integer + * translation used by itself does not require or start the use of the advanced + * graphics system in DWT. It is emulated until advanced graphics are triggered. + * @param dx the horizontal offset + * @param dy the vertical offset + */ +public abstract void translate(int dx, int dy); + + +/** + * Modifies the current transform by translating the given x and y amounts. All + * subsequent painting will be performed in the resulting coordinate system. + * @param dx the horizontal offset + * @param dy the vertical offset + */ +public void translate(float dx, float dy) { + subclassFunctionMission(); +} + +/** + * @see #translate(int, int) + */ +public final void translate(Point pt) { + translate(pt.x, pt.y); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/GraphicsSource.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/GraphicsSource.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.GraphicsSource; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Graphics; + +/** + * Provides a {@link dwtx.draw2d.Graphics} object for painting. + */ +public interface GraphicsSource { + +/** + * Returns a Graphics for the rectangular region requested. May return null. + * @param region The rectangular region + * @return A new Graphics object for the given region + */ +Graphics getGraphics(Rectangle region); + +/** + * Tells the GraphicsSource that you have finished using that region. + * @param region The rectangular region that that no longer needs the Graphics + */ +void flushGraphics(Rectangle region); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/GridData.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/GridData.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,511 @@ +/******************************************************************************* + * 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 + * Asim Ullah - Ported for use in draw2d (c.f Bugzilla 71684).[Sep 10, 2004] + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.draw2d.GridData; + +import dwt.dwthelper.utils; + + +import dwt.DWT; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.IFigure; + +/** + * GridData is the layout data object associated with + * GridLayout. To set a GridData object into a + * Figure, you use the setConstraint() method of + * GridLayout to map the Figure to its layout + * GridData. + *

+ * There are two ways to create a GridData object with certain + * fields set. The first is to set the fields directly, like this: + * + *

+ * GridData gridData = new GridData();
+ * gridData.horizontalAlignment = GridData.FILL;
+ * gridData.grabExcessHorizontalSpace = true;
+ *
+ * // associate the figure to the GridData object
+ * myGridlayout.setConstraint(myFigure, gridData);
+ * 
+ * + * The second is to take advantage of convenience style bits defined by + * GridData: + * + *
+ * GridData gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL
+ *      | GridData.GRAB_HORIZONTAL);
+ * 
+ * + *

+ *

+ * NOTE: Do not reuse GridData objects. Every child in the parent + * Figure that is managed by the GridLayout must + * have a unique GridData object. If the layout data for a Grid + * member in a GridLayout is null at layout time, a unique + * GridData object is created for it. + *

+ * + * @see GridLayout + */ +public final class GridData { + /** + * verticalAlignment specifies how figures will be positioned vertically + * within a cell. + * + * The default value is CENTER. + * + * Possible values are: + * + * DWT.BEGINNING (or DWT.TOP): Position the figure at the top of the cell + * DWT.CENTER: Position the figure in the vertical center of the cell + * DWT.END (or DWT.BOTTOM): Position the figure at the bottom of the cell + * DWT.FILL: Resize the figure to fill the cell vertically + */ + public int verticalAlignment = CENTER; + + /** + * horizontalAlignment specifies how figures will be positioned horizontally + * within a cell. + * + * The default value is BEGINNING. + * + * Possible values are: + * + * DWT.BEGINNING (or DWT.LEFT): Position the figure at the left of the cell + * DWT.CENTER: Position the figure in the horizontal center of the cell + * DWT.END (or DWT.RIGHT): Position the figure at the right of the cell + * DWT.FILL: Resize the figure to fill the cell horizontally + */ + public int horizontalAlignment = BEGINNING; + + /** + * widthHint specifies a minimum width for the column. A value of + * DWT.DEFAULT indicates that no minimum width is specified. + * + * The default value is DWT.DEFAULT. + */ + public int widthHint = DWT.DEFAULT; + + /** + * heightHint specifies a minimum height for the row. A value of DWT.DEFAULT + * indicates that no minimum height is specified. + * + * The default value is DWT.DEFAULT. + */ + public int heightHint = DWT.DEFAULT; + + /** + * horizontalIndent specifies the number of pixels of indentation that will + * be placed along the left side of the cell. + * + * The default value is 0. + */ + public int horizontalIndent = 0; + + /** + * horizontalSpan specifies the number of column cells that the figure will + * take up. + * + * The default value is 1. + */ + public int horizontalSpan = 1; + + /** + * verticalSpan specifies the number of row cells that the figure will take + * up. + * + * The default value is 1. + */ + public int verticalSpan = 1; + + /** + * grabExcessHorizontalSpace specifies whether the cell will be made wide + * enough to fit the remaining horizontal space. + * + * The default value is false. + */ + public bool grabExcessHorizontalSpace = false; + + /** + * grabExcessVerticalSpace specifies whether the cell will be made tall + * enough to fit the remaining vertical space. + * + * The default value is false. + */ + public bool grabExcessVerticalSpace = false; + + /** + * Value for horizontalAlignment or verticalAlignment. Position the figure + * at the top or left of the cell. Not recommended. Use DWT.BEGINNING, + * DWT.TOP or DWT.LEFT instead. + */ + public static const int BEGINNING = DWT.BEGINNING; + + /** + * Value for horizontalAlignment or verticalAlignment. Position the figure + * in the vertical or horizontal center of the cell Not recommended. Use + * DWT.CENTER instead. + */ + public static const int CENTER = 2; + + /** + * Value for horizontalAlignment or verticalAlignment. Position the figure + * at the bottom or right of the cell Not recommended. Use DWT.END, + * DWT.BOTTOM or DWT.RIGHT instead. + */ + public static const int END = 3; + + /** + * Value for horizontalAlignment or verticalAlignment. Resize the figure to + * fill the cell horizontally or vertically. Not recommended. Use DWT.FILL + * instead. + */ + public static const int FILL = DWT.FILL; + + /** + * Style bit for new GridData(int). Position the figure at + * the top of the cell. Not recommended. Use + * new GridData(int, DWT.BEGINNING, bool, bool) + * instead. + */ + public static const int VERTICAL_ALIGN_BEGINNING = 1 << 1; + + /** + * Style bit for new GridData(int) to position the figure in + * the vertical center of the cell. Not recommended. Use + * new GridData(int, DWT.CENTER, bool, bool) instead. + */ + public static const int VERTICAL_ALIGN_CENTER = 1 << 2; + + /** + * Style bit for new GridData(int) to position the figure at + * the bottom of the cell. Not recommended. Use + * new GridData(int, DWT.END, bool, bool) instead. + */ + public static const int VERTICAL_ALIGN_END = 1 << 3; + + /** + * Style bit for new GridData(int) to resize the figure to + * fill the cell vertically. Not recommended. Use + * new GridData(int, DWT.FILL, bool, bool) instead + */ + public static const int VERTICAL_ALIGN_FILL = 1 << 4; + + /** + * Style bit for new GridData(int) to position the figure at + * the left of the cell. Not recommended. Use + * new GridData(DWT.BEGINNING, int, bool, bool) + * instead. + */ + public static const int HORIZONTAL_ALIGN_BEGINNING = 1 << 5; + + /** + * Style bit for new GridData(int) to position the figure in + * the horizontal center of the cell. Not recommended. Use + * new GridData(DWT.CENTER, int, bool, bool) instead. + */ + public static const int HORIZONTAL_ALIGN_CENTER = 1 << 6; + + /** + * Style bit for new GridData(int) to position the figure at + * the right of the cell. Not recommended. Use + * new GridData(DWT.END, int, bool, bool) instead. + */ + public static const int HORIZONTAL_ALIGN_END = 1 << 7; + + /** + * Style bit for new GridData(int) to resize the figure to + * fill the cell horizontally. Not recommended. Use + * new GridData(DWT.FILL, int, bool, bool) instead. + */ + public static const int HORIZONTAL_ALIGN_FILL = 1 << 8; + + /** + * Style bit for new GridData(int) to resize the figure to + * fit the remaining horizontal space. Not recommended. Use + * new GridData(int, int, true, bool) instead. + */ + public static const int GRAB_HORIZONTAL = 1 << 9; + + /** + * Style bit for new GridData(int) to resize the figure to + * fit the remaining vertical space. Not recommended. Use + * new GridData(int, int, bool, true) instead. + */ + public static const int GRAB_VERTICAL = 1 << 10; + + /** + * Style bit for new GridData(int) to resize the figure to + * fill the cell vertically and to fit the remaining vertical space. + * FILL_VERTICAL = VERTICAL_ALIGN_FILL | GRAB_VERTICAL Not recommended. Use + * new GridData(int, DWT.FILL, bool, true) instead. + */ + public static const int FILL_VERTICAL = VERTICAL_ALIGN_FILL | GRAB_VERTICAL; + + /** + * Style bit for new GridData(int) to resize the figure to + * fill the cell horizontally and to fit the remaining horizontal space. + * FILL_HORIZONTAL = HORIZONTAL_ALIGN_FILL | GRAB_HORIZONTAL Not + * recommended. Use new GridData(DWT.FILL, int, true, bool) + * instead. + */ + public static const int FILL_HORIZONTAL = HORIZONTAL_ALIGN_FILL + | GRAB_HORIZONTAL; + + /** + * Style bit for new GridData(int) to resize the figure to + * fill the cell horizontally and vertically and to fit the remaining + * horizontal and vertical space. FILL_BOTH = FILL_VERTICAL | + * FILL_HORIZONTAL Not recommended. Use + * new GridData(DWT.FILL, DWT.FILL, true, true) instead. + */ + public static const int FILL_BOTH = FILL_VERTICAL | FILL_HORIZONTAL; + + int cacheWidth = -1, cacheHeight = -1; + int[][] cache; + int cacheIndex = -1; + + /** + * Constructs a new instance of GridData using default values. + */ + public this() { + cache = new int[][](2,4); + } + + /** + * Constructs a new instance based on the GridData style. This constructor + * is not recommended. + * + * @param style + * the GridData style + */ + public this(int style) { + this(); + if ((style & VERTICAL_ALIGN_BEGINNING) !is 0) + verticalAlignment = BEGINNING; + if ((style & VERTICAL_ALIGN_CENTER) !is 0) + verticalAlignment = CENTER; + if ((style & VERTICAL_ALIGN_FILL) !is 0) + verticalAlignment = FILL; + if ((style & VERTICAL_ALIGN_END) !is 0) + verticalAlignment = END; + if ((style & HORIZONTAL_ALIGN_BEGINNING) !is 0) + horizontalAlignment = BEGINNING; + if ((style & HORIZONTAL_ALIGN_CENTER) !is 0) + horizontalAlignment = CENTER; + if ((style & HORIZONTAL_ALIGN_FILL) !is 0) + horizontalAlignment = FILL; + if ((style & HORIZONTAL_ALIGN_END) !is 0) + horizontalAlignment = END; + grabExcessHorizontalSpace = (style & GRAB_HORIZONTAL) !is 0; + grabExcessVerticalSpace = (style & GRAB_VERTICAL) !is 0; + } + + /** + * Constructs a new instance of GridData according to the parameters. + * + * @param horizontalAlignment + * how figure will be positioned horizontally within a cell + * @param verticalAlignment + * how figure will be positioned vertically within a cell + * @param grabExcessHorizontalSpace + * whether cell will be made wide enough to fit the remaining + * horizontal space + * @param grabExcessVerticalSpace + * whether cell will be made high enough to fit the remaining + * vertical space + * + */ + public this(int horizontalAlignment, int verticalAlignment, + bool grabExcessHorizontalSpace, bool grabExcessVerticalSpace) { + this(horizontalAlignment, verticalAlignment, grabExcessHorizontalSpace, + grabExcessVerticalSpace, 1, 1); + } + + /** + * Constructs a new instance of GridData according to the parameters. + * + * @param horizontalAlignment + * how figure will be positioned horizontally within a cell + * @param verticalAlignment + * how figure will be positioned vertically within a cell + * @param grabExcessHorizontalSpace + * whether cell will be made wide enough to fit the remaining + * horizontal space + * @param grabExcessVerticalSpace + * whether cell will be made high enough to fit the remaining + * vertical space + * @param horizontalSpan + * the number of column cells that the figure will take up + * @param verticalSpan + * the number of row cells that the figure will take up + * + */ + public this(int horizontalAlignment, int verticalAlignment, + bool grabExcessHorizontalSpace, bool grabExcessVerticalSpace, + int horizontalSpan, int verticalSpan) { + this(); + this.horizontalAlignment = horizontalAlignment; + this.verticalAlignment = verticalAlignment; + this.grabExcessHorizontalSpace = grabExcessHorizontalSpace; + this.grabExcessVerticalSpace = grabExcessVerticalSpace; + this.horizontalSpan = horizontalSpan; + this.verticalSpan = verticalSpan; + } + + /** + * Constructs a new instance of GridData according to the parameters. A + * value of DWT.DEFAULT indicates that no minimum width or no minumum height + * is specified. + * + * @param width + * a minimum width for the column + * @param height + * a minimum height for the row + * + */ + public this(int width, int height) { + this(); + this.widthHint = width; + this.heightHint = height; + } + + Dimension computeSize(IFigure figure, bool flushCache) { + if (cacheWidth !is -1 && cacheHeight !is -1) { + return new Dimension(cacheWidth, cacheHeight); + } + for (int i = 0; i < cacheIndex + 1; i++) { + if (cache[i][0] is widthHint && cache[i][1] is heightHint) { + cacheWidth = cache[i][2]; + cacheHeight = cache[i][3]; + return new Dimension(cacheWidth, cacheHeight); + } + } + + Dimension size = figure.getPreferredSize(widthHint, heightHint); + if (widthHint !is -1) + size.width = widthHint; + if (heightHint !is -1) + size.height = heightHint; + + if (cacheIndex < cache.length - 1) + cacheIndex++; + cache[cacheIndex][0] = widthHint; + cache[cacheIndex][1] = heightHint; + cacheWidth = cache[cacheIndex][2] = size.width; + cacheHeight = cache[cacheIndex][3] = size.height; + return size; + } + + void flushCache() { + cacheWidth = cacheHeight = -1; + cacheIndex = -1; + } + + String getName() { + String string = this.classinfo.name; + int index = string.lastIndexOf('.'); + if (index is -1) + return string; + return string.substring(index + 1, string.length); + } + + public String toString() { + + String hAlign = ""; //$NON-NLS-1$ + switch (horizontalAlignment) { + case DWT.FILL: + hAlign = "DWT.FILL"; //$NON-NLS-1$ + break; + case DWT.BEGINNING: + hAlign = "DWT.BEGINNING"; //$NON-NLS-1$ + break; + case DWT.LEFT: + hAlign = "DWT.LEFT"; //$NON-NLS-1$ + break; + case DWT.END: + hAlign = "DWT.END"; //$NON-NLS-1$ + break; + case END: + hAlign = "GridData.END"; //$NON-NLS-1$ + break; + case DWT.RIGHT: + hAlign = "DWT.RIGHT"; //$NON-NLS-1$ + break; + case DWT.CENTER: + hAlign = "DWT.CENTER"; //$NON-NLS-1$ + break; + case CENTER: + hAlign = "GridData.CENTER"; //$NON-NLS-1$ + break; + default: + hAlign = "Undefined " ~ Integer.toString(horizontalAlignment); //$NON-NLS-1$ + break; + } + String vAlign = ""; //$NON-NLS-1$ + switch (verticalAlignment) { + case DWT.FILL: + vAlign = "DWT.FILL"; //$NON-NLS-1$ + break; + case DWT.BEGINNING: + vAlign = "DWT.BEGINNING"; //$NON-NLS-1$ + break; + case DWT.TOP: + vAlign = "DWT.TOP"; //$NON-NLS-1$ + break; + case DWT.END: + vAlign = "DWT.END"; //$NON-NLS-1$ + break; + case END: + vAlign = "GridData.END"; //$NON-NLS-1$ + break; + case DWT.BOTTOM: + vAlign = "DWT.BOTTOM"; //$NON-NLS-1$ + break; + case DWT.CENTER: + vAlign = "DWT.CENTER"; //$NON-NLS-1$ + break; + case CENTER: + vAlign = "GridData.CENTER"; //$NON-NLS-1$ + break; + default: + vAlign = "Undefined " ~ Integer.toString(verticalAlignment); //$NON-NLS-1$ + break; + } + String string = getName() ~ " {"; //$NON-NLS-1$ + string ~= "horizontalAlignment=" ~ hAlign ~ " "; //$NON-NLS-1$ //$NON-NLS-2$ + if (horizontalIndent !is 0) + string ~= "horizontalIndent=" ~ Integer.toString(horizontalIndent) ~ " "; //$NON-NLS-1$ //$NON-NLS-2$ + if (horizontalSpan !is 1) + string ~= "horizontalSpan=" ~ Integer.toString(horizontalSpan) ~ " "; //$NON-NLS-1$//$NON-NLS-2$ + if (grabExcessHorizontalSpace) + string ~= "grabExcessHorizontalSpace=" ~ Integer.toString(grabExcessHorizontalSpace) //$NON-NLS-1$ + ~ " "; //$NON-NLS-1$ + if (widthHint !is DWT.DEFAULT) + string ~= "widthHint=" ~ Integer.toString(widthHint) ~ " "; //$NON-NLS-1$ //$NON-NLS-2$ + string ~= "verticalAlignment=" ~ vAlign ~ " "; //$NON-NLS-1$ //$NON-NLS-2$ + if (verticalSpan !is 1) + string ~= "verticalSpan=" ~ Integer.toString(verticalSpan) ~ " "; //$NON-NLS-1$ //$NON-NLS-2$ + if (grabExcessVerticalSpace) + string ~= "grabExcessVerticalSpace=" ~ Integer.toString(grabExcessVerticalSpace) //$NON-NLS-1$ + ~ " "; //$NON-NLS-1$ + if (heightHint !is DWT.DEFAULT) + string ~= "heightHint=" ~ Integer.toString(heightHint) ~ " "; //$NON-NLS-1$ //$NON-NLS-2$ + string = string.trim(); + string ~= "}"; //$NON-NLS-1$ + return string; + + } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/GridLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/GridLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,799 @@ +/******************************************************************************* + * 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 + * Asim Ullah - Ported for use in draw2d (c.f Bugzilla 71684). [Sep 10, 2004] + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.draw2d.GridLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwt.DWT; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.AbstractLayout; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.GridData; + +/** + * Lays out children into a Grid arrangement in which overall aligment and + * spacing can be configured, as well as specfic layout requirements for the + * each individual member of the GridLayout. This layout is a Draw2D port of the + * swt GridLayout. + * + * GridLayout has a number of configuration fields, and the + * Figures it lays out can have an associated layout data object, called + * GridData (similar to the swt GridData object). The power of + * GridLayout lies in the ability to configure + * GridData for each Figure in the layout. + *

+ * The following code creates a container Figure managed by a + * GridLayout with 2 columns, containing 3 RectangleFigure + * shapes, the last of which has been given further layout instructions. Note + * that it is the GridLayout method setConstraint + * that binds the child Figure to its layout + * GridData object. + * + *

+ * Figure container = new Figure();
+ * GridLayout gridLayout = new GridLayout();
+ * gridLayout.numColumns = 2;
+ * container.setLayout(gridLayout);
+ *
+ * Shape rect;
+ * rect = new RectangleFigure();
+ * container.add(rect);
+ *
+ * rect = new RectangleFigure();
+ * container.add(rect);
+ *
+ * rect = new RectangleFigure();
+ * GridData gridData = new GridData();
+ * gridData.widthHint = 150;
+ * layout.setConstraint(rect, gridData);
+ *
+ * container.add(rect);
+ * 
+ * + *

+ * The numColumns field is the most important field in a + * GridLayout. Widgets are laid out in columns from left to + * right, and a new row is created when numColumns+ 1 figures + * are added to the Figure parent container. + * + * @see GridData + * + */ +public class GridLayout : AbstractLayout { + + /** + * numColumns specifies the number of cell columns in the layout. + * + * The default value is 1. + */ + public int numColumns = 1; + + /** + * makeColumnsEqualWidth specifies whether all columns in the layout will be + * forced to have the same width. + * + * The default value is false. + */ + public bool makeColumnsEqualWidth = false; + + /** + * marginWidth specifies the number of pixels of horizontal margin that will + * be placed along the left and right edges of the layout. + * + * The default value is 5. + */ + public int marginWidth = 5; + + /** + * marginHeight specifies the number of pixels of vertical margin that will + * be placed along the top and bottom edges of the layout. + * + * The default value is 5. + */ + public int marginHeight = 5; + + /** + * horizontalSpacing specifies the number of pixels between the right edge + * of one cell and the left edge of its neighbouring cell to the right. + * + * The default value is 5. + */ + public int horizontalSpacing = 5; + + /** + * verticalSpacing specifies the number of pixels between the bottom edge of + * one cell and the top edge of its neighbouring cell underneath. + * + * The default value is 5. + */ + public int verticalSpacing = 5; + + /** The layout contraints */ + protected Map constraints; + + /** + * Default Constructor + */ + public this() { + //super(); + constraints = new HashMap(); + } + + /** + * Constructs a new instance of this class given the number of columns, and + * whether or not the columns should be forced to have the same width. + * + * @param numColumns + * the number of columns in the grid + * @param makeColumnsEqualWidth + * whether or not the columns will have equal width + * + */ + public this(int numColumns, bool makeColumnsEqualWidth) { + this.numColumns = numColumns; + this.makeColumnsEqualWidth = makeColumnsEqualWidth; + } + + /** + * @param child + * @param wHint + * @param hHint + * @return the child size. + */ + protected Dimension getChildSize(IFigure child, int wHint, int hHint) { + return child.getPreferredSize(wHint, hHint); + } + + GridData getData(IFigure[][] grid, int row, int column, int rowCount, + int columnCount, bool first) { + IFigure figure = grid[row][column]; + if (figure !is null) { + GridData data = cast(GridData) getConstraint(figure); + int hSpan = Math.max(1, Math.min(data.horizontalSpan, columnCount)); + int vSpan = Math.max(1, data.verticalSpan); + int i = first ? row + vSpan - 1 : row - vSpan + 1; + int j = first ? column + hSpan - 1 : column - hSpan + 1; + if (0 <= i && i < rowCount) { + if (0 <= j && j < columnCount) { + if (figure is grid[i][j]) + return data; + } + } + } + return null; + } + + void initChildren(IFigure container) { + List children = container.getChildren(); + for (int i = 0; i < children.size(); i++) { + IFigure child = cast(IFigure) children.get(i); + if (child.getLayoutManager() is null) + child.setLayoutManager(this); + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.draw2d.AbstractLayout#calculatePreferredSize(dwtx.draw2d.IFigure, + * int, int) + */ + protected Dimension calculatePreferredSize(IFigure container, int wHint, + int hHint) { + Dimension size = layout(container, false, 0, 0, wHint, hHint, /* flushCache */ + true); + if (wHint !is DWT.DEFAULT) + size.width = wHint; + if (hHint !is DWT.DEFAULT) + size.height = hHint; + + return size; + } + + /* + * (non-Javadoc) + * + * @see dwtx.draw2d.LayoutManager#layout(dwtx.draw2d.IFigure) + */ + public void layout(IFigure container) { + // initChildren( container); + Rectangle rect = container.getClientArea(); + layout(container, true, rect.x, rect.y, rect.width, rect.height, /* flushCache */ + true); + + } + + Dimension layout(IFigure container, bool move, int x, int y, int width, + int height, bool flushCache) { + if (numColumns < 1) + return new Dimension(marginWidth * 2, marginHeight * 2); + List children = container.getChildren(); + for (int i = 0; i < children.size(); i++) { + IFigure child = cast(IFigure) children.get(i); + + GridData data = cast(GridData) getConstraint(child); + if (data is null) + setConstraint(child, data = new GridData()); + if (flushCache) + data.flushCache(); + data.computeSize(child, flushCache); + } + + /* Build the grid */ + int row = 0, column = 0, rowCount = 0, columnCount = numColumns; + IFigure[][] grid = new IFigure[][](4,columnCount); + for (int i = 0; i < children.size(); i++) { + IFigure child = cast(IFigure) children.get(i); + GridData data = cast(GridData) getConstraint(child); + int hSpan = Math.max(1, Math.min(data.horizontalSpan, columnCount)); + int vSpan = Math.max(1, data.verticalSpan); + while (true) { + int lastRow = row + vSpan; + if (lastRow >= grid.length) { + IFigure[][] newGrid = new IFigure[][](lastRow + 4, columnCount ); + SimpleType!(IFigure[]).arraycopy(grid, 0, newGrid, 0, grid.length); + grid = newGrid; + } + if (grid[row] is null) { + grid[row] = new IFigure[columnCount]; + } + while (column < columnCount && grid[row][column] !is null) { + column++; + } + int endCount = column + hSpan; + if (endCount <= columnCount) { + int index = column; + while (index < endCount && grid[row][index] is null) { + index++; + } + if (index is endCount) + break; + column = index; + } + if (column + hSpan >= columnCount) { + column = 0; + row++; + } + } + for (int j = 0; j < vSpan; j++) { + if (grid[row + j] is null) { + grid[row + j] = new IFigure[columnCount]; + } + for (int k = 0; k < hSpan; k++) { + grid[row + j][column + k] = child; + } + } + rowCount = Math.max(rowCount, row + vSpan); + column += hSpan; + } + + /* Column widths */ + int availableWidth = width - horizontalSpacing * (columnCount - 1) + - marginWidth * 2; + int expandCount = 0; + int[] widths = new int[columnCount]; + int[] minWidths = new int[columnCount]; + bool[] expandColumn = new bool[columnCount]; + for (int j = 0; j < columnCount; j++) { + for (int i = 0; i < rowCount; i++) { + GridData data = getData(grid, i, j, rowCount, columnCount, true); + if (data !is null) { + int hSpan = Math.max(1, Math.min(data.horizontalSpan, + columnCount)); + if (hSpan is 1) { + int w = data.cacheWidth + data.horizontalIndent; + widths[j] = Math.max(widths[j], w); + if (data.grabExcessHorizontalSpace) { + if (!expandColumn[j]) + expandCount++; + expandColumn[j] = true; + } + if (data.widthHint !is DWT.DEFAULT + || !data.grabExcessHorizontalSpace) { + minWidths[j] = Math.max(minWidths[j], w); + } + } + } + } + for (int i = 0; i < rowCount; i++) { + GridData data = getData(grid, i, j, rowCount, columnCount, + false); + if (data !is null) { + int hSpan = Math.max(1, Math.min(data.horizontalSpan, + columnCount)); + if (hSpan > 1) { + int spanWidth = 0, spanMinWidth = 0, spanExpandCount = 0; + for (int k = 0; k < hSpan; k++) { + spanWidth += widths[j - k]; + spanMinWidth += minWidths[j - k]; + if (expandColumn[j - k]) + spanExpandCount++; + } + if (data.grabExcessHorizontalSpace + && spanExpandCount is 0) { + expandCount++; + expandColumn[j] = true; + } + int w = data.cacheWidth + data.horizontalIndent + - spanWidth - (hSpan - 1) * horizontalSpacing; + if (w > 0) { + if (spanExpandCount is 0) { + widths[j] += w; + } else { + int delta = w / spanExpandCount; + int remainder = w % spanExpandCount, last = -1; + for (int k = 0; k < hSpan; k++) { + if (expandColumn[j - k]) { + widths[last = j - k] += delta; + } + } + if (last > -1) + widths[last] += remainder; + } + } + if (data.widthHint !is DWT.DEFAULT + || !data.grabExcessHorizontalSpace) { + w = data.cacheWidth + data.horizontalIndent + - spanMinWidth - (hSpan - 1) + * horizontalSpacing; + if (w > 0) { + if (spanExpandCount is 0) { + minWidths[j] += w; + } else { + int delta = w / spanExpandCount; + int remainder = w % spanExpandCount, last = -1; + for (int k = 0; k < hSpan; k++) { + if (expandColumn[j - k]) { + minWidths[last = j - k] += delta; + } + } + if (last > -1) + minWidths[last] += remainder; + } + } + } + } + } + } + } + if (makeColumnsEqualWidth) { + int minColumnWidth = 0; + int columnWidth = 0; + for (int i = 0; i < columnCount; i++) { + minColumnWidth = Math.max(minColumnWidth, minWidths[i]); + columnWidth = Math.max(columnWidth, widths[i]); + } + columnWidth = width is DWT.DEFAULT || expandCount is 0 ? columnWidth + : Math.max(minColumnWidth, availableWidth / columnCount); + for (int i = 0; i < columnCount; i++) { + expandColumn[i] = expandCount > 0; + widths[i] = columnWidth; + } + } else { + if (width !is DWT.DEFAULT && expandCount > 0) { + int totalWidth = 0; + for (int i = 0; i < columnCount; i++) { + totalWidth += widths[i]; + } + int count = expandCount; + int delta = (availableWidth - totalWidth) / count; + int remainder = (availableWidth - totalWidth) % count; + int last = -1; + while (totalWidth !is availableWidth) { + for (int j = 0; j < columnCount; j++) { + if (expandColumn[j]) { + if (widths[j] + delta > minWidths[j]) { + widths[last = j] = widths[j] + delta; + } else { + widths[j] = minWidths[j]; + expandColumn[j] = false; + count--; + } + } + } + if (last > -1) + widths[last] += remainder; + + for (int j = 0; j < columnCount; j++) { + for (int i = 0; i < rowCount; i++) { + GridData data = getData(grid, i, j, rowCount, + columnCount, false); + if (data !is null) { + int hSpan = Math.max(1, Math.min( + data.horizontalSpan, columnCount)); + if (hSpan > 1) { + if (data.widthHint !is DWT.DEFAULT + || !data.grabExcessHorizontalSpace) { + int spanWidth = 0, spanExpandCount = 0; + for (int k = 0; k < hSpan; k++) { + spanWidth += widths[j - k]; + if (expandColumn[j - k]) + spanExpandCount++; + } + int w = data.cacheWidth + + data.horizontalIndent + - spanWidth - (hSpan - 1) + * horizontalSpacing; + if (w > 0) { + if (spanExpandCount is 0) { + widths[j] += w; + } else { + int delta2 = w + / spanExpandCount; + int remainder2 = w + % spanExpandCount, last2 = -1; + for (int k = 0; k < hSpan; k++) { + if (expandColumn[j - k]) { + widths[last2 = j - k] += delta2; + } + } + if (last2 > -1) + widths[last2] += remainder2; + } + } + } + } + } + } + } + if (count is 0) + break; + totalWidth = 0; + for (int i = 0; i < columnCount; i++) { + totalWidth += widths[i]; + } + delta = (availableWidth - totalWidth) / count; + remainder = (availableWidth - totalWidth) % count; + last = -1; + } + } + } + + /* Wrapping */ + GridData[] flush = null; + int flushLength = 0; + if (width !is DWT.DEFAULT) { + for (int j = 0; j < columnCount; j++) { + for (int i = 0; i < rowCount; i++) { + GridData data = getData(grid, i, j, rowCount, columnCount, + false); + if (data !is null) { + if (data.heightHint is DWT.DEFAULT) { + IFigure child = grid[i][j]; + // TEMPORARY CODE + int hSpan = Math.max(1, Math.min( + data.horizontalSpan, columnCount)); + int currentWidth = 0; + for (int k = 0; k < hSpan; k++) { + currentWidth += widths[j - k]; + } + currentWidth += (hSpan - 1) * horizontalSpacing + - data.horizontalIndent; + if ((currentWidth !is data.cacheWidth && data.horizontalAlignment is DWT.FILL) + || (data.cacheWidth > currentWidth)) { + int trim = 0; + /* + * // *Note*: Left this in place from DWT // + * GridLayout. Not sure if Draw2D Borders or // + * Scrollbars 'trim' will need to be takeninto + * account. + * + * if (child instanceof Group) { Group g + * =(Group)child; trim = g.getSize ().x - + * g.getClientArea ().width; } else if (child + * instanceof Scrollable) { Rectangle rect = + * ((Scrollable) child).computeTrim (0, 0, 0,0); + * trim = rect.width; } else { trim = + * child.getBorderWidth () * 2; } + */ + int oldWidthHint = data.widthHint; + data.widthHint = Math.max(0, currentWidth + - trim); + data.cacheWidth = data.cacheHeight = DWT.DEFAULT; + data.computeSize(child, false); + data.widthHint = oldWidthHint; + if (flush is null) + flush = new GridData[children.size()]; + flush[flushLength++] = data; + } + } + } + } + } + } + + /* Row heights */ + int availableHeight = height - verticalSpacing * (rowCount - 1) + - marginHeight * 2; + expandCount = 0; + int[] heights = new int[rowCount]; + int[] minHeights = new int[rowCount]; + bool[] expandRow = new bool[rowCount]; + for (int i = 0; i < rowCount; i++) { + for (int j = 0; j < columnCount; j++) { + GridData data = getData(grid, i, j, rowCount, columnCount, true); + if (data !is null) { + int vSpan = Math.max(1, Math.min(data.verticalSpan, + rowCount)); + if (vSpan is 1) { + int h = data.cacheHeight; // + data.verticalIndent; + heights[i] = Math.max(heights[i], h); + if (data.grabExcessVerticalSpace) { + if (!expandRow[i]) + expandCount++; + expandRow[i] = true; + } + if (data.heightHint !is DWT.DEFAULT + || !data.grabExcessVerticalSpace) { + minHeights[i] = Math.max(minHeights[i], h); + } + } + } + } + for (int j = 0; j < columnCount; j++) { + GridData data = getData(grid, i, j, rowCount, columnCount, + false); + if (data !is null) { + int vSpan = Math.max(1, Math.min(data.verticalSpan, + rowCount)); + if (vSpan > 1) { + int spanHeight = 0, spanMinHeight = 0, spanExpandCount = 0; + for (int k = 0; k < vSpan; k++) { + spanHeight += heights[i - k]; + spanMinHeight += minHeights[i - k]; + if (expandRow[i - k]) + spanExpandCount++; + } + if (data.grabExcessVerticalSpace + && spanExpandCount is 0) { + expandCount++; + expandRow[i] = true; + } + int h = data.cacheHeight - spanHeight - (vSpan - 1) + * verticalSpacing; // + data.verticalalIndent + if (h > 0) { + if (spanExpandCount is 0) { + heights[i] += h; + } else { + int delta = h / spanExpandCount; + int remainder = h % spanExpandCount, last = -1; + for (int k = 0; k < vSpan; k++) { + if (expandRow[i - k]) { + heights[last = i - k] += delta; + } + } + if (last > -1) + heights[last] += remainder; + } + } + if (data.heightHint !is DWT.DEFAULT + || !data.grabExcessVerticalSpace) { + h = data.cacheHeight - spanMinHeight - (vSpan - 1) + * verticalSpacing; // + data.verticalIndent + if (h > 0) { + if (spanExpandCount is 0) { + minHeights[i] += h; + } else { + int delta = h / spanExpandCount; + int remainder = h % spanExpandCount, last = -1; + for (int k = 0; k < vSpan; k++) { + if (expandRow[i - k]) { + minHeights[last = i - k] += delta; + } + } + if (last > -1) + minHeights[last] += remainder; + } + } + } + } + } + } + } + if (height !is DWT.DEFAULT && expandCount > 0) { + int totalHeight = 0; + for (int i = 0; i < rowCount; i++) { + totalHeight += heights[i]; + } + int count = expandCount; + int delta = (availableHeight - totalHeight) / count; + int remainder = (availableHeight - totalHeight) % count; + int last = -1; + while (totalHeight !is availableHeight) { + for (int i = 0; i < rowCount; i++) { + if (expandRow[i]) { + if (heights[i] + delta > minHeights[i]) { + heights[last = i] = heights[i] + delta; + } else { + heights[i] = minHeights[i]; + expandRow[i] = false; + count--; + } + } + } + if (last > -1) + heights[last] += remainder; + + for (int i = 0; i < rowCount; i++) { + for (int j = 0; j < columnCount; j++) { + GridData data = getData(grid, i, j, rowCount, + columnCount, false); + if (data !is null) { + int vSpan = Math.max(1, Math.min(data.verticalSpan, + rowCount)); + if (vSpan > 1) { + if (data.heightHint !is DWT.DEFAULT + || !data.grabExcessVerticalSpace) { + int spanHeight = 0, spanExpandCount = 0; + for (int k = 0; k < vSpan; k++) { + spanHeight += heights[i - k]; + if (expandRow[i - k]) + spanExpandCount++; + } + int h = data.cacheHeight - spanHeight + - (vSpan - 1) * verticalSpacing; // + + // data.verticalIndent + if (h > 0) { + if (spanExpandCount is 0) { + heights[i] += h; + } else { + int delta2 = h / spanExpandCount; + int remainder2 = h + % spanExpandCount, last2 = -1; + for (int k = 0; k < vSpan; k++) { + if (expandRow[i - k]) { + heights[last2 = i - k] += delta2; + } + } + if (last2 > -1) + heights[last2] += remainder2; + } + } + } + } + } + } + } + if (count is 0) + break; + totalHeight = 0; + for (int i = 0; i < rowCount; i++) { + totalHeight += heights[i]; + } + delta = (availableHeight - totalHeight) / count; + remainder = (availableHeight - totalHeight) % count; + last = -1; + } + } + + /* Position the IFigures */ + if (move) { + int gridY = y + marginHeight; + for (int i = 0; i < rowCount; i++) { + int gridX = x + marginWidth; + for (int j = 0; j < columnCount; j++) { + GridData data = getData(grid, i, j, rowCount, columnCount, + true); + if (data !is null) { + int hSpan = Math.max(1, Math.min(data.horizontalSpan, + columnCount)); + int vSpan = Math.max(1, data.verticalSpan); + int cellWidth = 0, cellHeight = 0; + for (int k = 0; k < hSpan; k++) { + cellWidth += widths[j + k]; + } + for (int k = 0; k < vSpan; k++) { + cellHeight += heights[i + k]; + } + cellWidth += horizontalSpacing * (hSpan - 1); + int childX = gridX + data.horizontalIndent; + int childWidth = Math.min(data.cacheWidth, cellWidth); + switch (data.horizontalAlignment) { + case DWT.CENTER: + case GridData.CENTER: + childX = gridX + + Math.max(0, (cellWidth - childWidth) / 2); + break; + case DWT.RIGHT: + case DWT.END: + case GridData.END: + childX = gridX + + Math.max(0, cellWidth - childWidth); + break; + case DWT.FILL: + childWidth = cellWidth - data.horizontalIndent; + break; + } + cellHeight += verticalSpacing * (vSpan - 1); + int childY = gridY; // + data.verticalIndent; + int childHeight = Math + .min(data.cacheHeight, cellHeight); + switch (data.verticalAlignment) { + case DWT.CENTER: + case GridData.CENTER: + childY = gridY + + Math.max(0, + (cellHeight - childHeight) / 2); + break; + case DWT.BOTTOM: + case DWT.END: + case GridData.END: + childY = gridY + + Math.max(0, cellHeight - childHeight); + break; + case DWT.FILL: + childHeight = cellHeight; // - + // data.verticalIndent; + break; + } + IFigure child = grid[i][j]; + if (child !is null) { + // following param could be replaced by + // Rectangle.SINGLETON + child.setBounds(new Rectangle(childX, childY, + childWidth, childHeight)); + } + } + gridX += widths[j] + horizontalSpacing; + } + gridY += heights[i] + verticalSpacing; + } + } + + // clean up cache + for (int i = 0; i < flushLength; i++) { + flush[i].cacheWidth = flush[i].cacheHeight = -1; + } + + int totalDefaultWidth = 0; + int totalDefaultHeight = 0; + for (int i = 0; i < columnCount; i++) { + totalDefaultWidth += widths[i]; + } + for (int i = 0; i < rowCount; i++) { + totalDefaultHeight += heights[i]; + } + totalDefaultWidth += horizontalSpacing * (columnCount - 1) + + marginWidth * 2; + totalDefaultHeight += verticalSpacing * (rowCount - 1) + marginHeight + * 2; + return new Dimension(totalDefaultWidth, totalDefaultHeight); + } + + /* + * (non-Javadoc) + * + * @see dwtx.draw2d.LayoutManager#getConstraint(dwtx.draw2d.IFigure) + */ + public Object getConstraint(IFigure child) { + return constraints.get(cast(Object)child); + } + + /** + * Sets the layout constraint of the given figure. The constraints can only + * be of type {@link GridData}. + * + * @see LayoutManager#setConstraint(IFigure, Object) + */ + public void setConstraint(IFigure figure, Object newConstraint) { + super.setConstraint(figure, newConstraint); + if (newConstraint !is null) { + constraints.put(cast(Object)figure, newConstraint); + + } + } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/GroupBoxBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/GroupBoxBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.GroupBoxBorder; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.AbstractLabeledBorder; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.FigureUtilities; + +/** + * A labeled border intended to house a Figure with a group of children. The label should + * serve as a description of the group. + */ +public class GroupBoxBorder + : AbstractLabeledBorder +{ + +/** + * Constructs a GroupBoxBorder with the name of this class as its label. + * + * @since 2.0 + */ +public this() { } + +/** + * Constructs a GroupBoxBorder with label s. + * + * @param s the label + * @since 2.0 + */ +public this(String s) { + super(s); +} + +/** + * Calculates and returns the Insets for this GroupBoxBorder. + * + * @param figure IFigure on which the calculations should be made. Generally this is + * the IFigure of which this GroupBoxBorder is surrounding. + * @return the Insets for this GroupBoxBorder. + * @since 2.0 + */ +protected Insets calculateInsets(IFigure figure) { + int height = getTextExtents(figure).height; + return new Insets(height); +} + +/** + * @see dwtx.draw2d.Border#getPreferredSize(IFigure) + */ +public Dimension getPreferredSize(IFigure fig) { + Dimension textSize = getTextExtents(fig); + return textSize.getCopy().expand(textSize.height * 2, 0); +} + +/** + * @see Border#paint(IFigure, Graphics, Insets) + */ +public void paint(IFigure figure, Graphics g, Insets insets) { + tempRect.setBounds(getPaintRectangle(figure, insets)); + Rectangle r = tempRect; + if (r.isEmpty()) + return; + + Rectangle textLoc = new Rectangle(r.getTopLeft(), getTextExtents(figure)); + r.crop(new Insets(getTextExtents(figure).height / 2)); + FigureUtilities.paintEtchedBorder(g, r); + + textLoc.x += getInsets(figure).left; + g.setFont(getFont(figure)); + g.setForegroundColor(getTextColor()); + g.clipRect(textLoc); + g.fillText(getLabel(), textLoc.getTopLeft()); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/IFigure.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/IFigure.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,911 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.IFigure; + +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.AncestorListener; +import dwtx.draw2d.CoordinateListener; +import dwtx.draw2d.FigureListener; +import dwtx.draw2d.FocusListener; +import dwtx.draw2d.KeyListener; +import dwtx.draw2d.KeyEvent; +import dwtx.draw2d.LayoutListener; +import dwtx.draw2d.MouseListener; +import dwtx.draw2d.MouseEvent; +import dwtx.draw2d.MouseMotionListener; +import dwtx.draw2d.TreeSearch; +import dwtx.draw2d.Border; +import dwtx.draw2d.LayoutManager; +import dwtx.draw2d.FocusEvent; +import dwtx.draw2d.UpdateManager; +import dwtx.draw2d.EventDispatcher; +import dwtx.draw2d.Graphics; + +/** + * Insets that are all 0. Always returns true for {@link #isEmpty()}. + */ +class NoInsets + : Insets +{ + this() { + super(0, 0, 0, 0); + } + public bool isEmpty() { + return true; + } +} + +static this(){ + IFigure_MAX_DIMENSION = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + IFigure_MIN_DIMENSION = new Dimension(5, 5); + IFigure_NO_INSETS = new NoInsets(); +} +/** + * The maximum allowable dimension. ({@link Integer#MAX_VALUE},{@link Integer#MAX_VALUE}) + */ +static const Dimension IFigure_MAX_DIMENSION; + +/** + * The minimum allowable dimension. (5,5) + */ +static const Dimension IFigure_MIN_DIMENSION; + +/** + * Empty Insets. + */ +static const Insets IFigure_NO_INSETS; + + +/** + * A lightweight graphical object. Figures are rendered to a {@link Graphics} object. + * Figures can be composed to create complex graphics. + * + *

+ * WARNING: This interface is not intended to be implemented by clients. + */ +public interface IFigure { + +/** + * Adds the given IFigure as a child of this IFigure. The same as calling {@link + * #add(IFigure, Object, int) add(figure, null, -1)}. + * @param figure The IFigure to add + */ +void add(IFigure figure); + +/** + * Adds the given IFigure as a child of this IFigure at the given index. The same as + * calling {@link #add(IFigure, Object, int) add(figure, null, index)}. + * @param figure The IFigure to add + * @param index The index where the IFigure should be added + */ +void add(IFigure figure, int index); + +/** + * Adds the given IFigure as a child of this IFigure with the given constraint. The same + * as calling {@link #add(IFigure, Object, int) add(figure, constraint, -1)}. + * @param figure The IFigure to add + * @param constraint The newly added IFigure's constraint + */ +void add(IFigure figure, Object constraint); + +/** + * Adds the child with the specified index and constraint. The child's parent is currently + * not null, it is removed from that parent. If this figure has a LayoutManager, then + * {@link LayoutManager#setConstraint(IFigure, Object)} shall be called on the layout. + * @param figure The IFigure to add + * @param constraint The newly added IFigure's constraint + * @param index The index where the IFigure should be added + * @throws IndexOutOfBoundsException if the index is out of range + * @throws IllegalArgumentException if adding the child creates a cycle + */ +void add(IFigure figure, Object constraint, int index); + +/** + * Registers the given listener as an AncestorListener of this figure. + * @param listener The listener to add + */ +void addAncestorListener(AncestorListener listener); + +/** + * Registers the given listener as a CoordinateListener of this figure. + * @param listener the listener to add + * @since 3.1 + */ +void addCoordinateListener(CoordinateListener listener); + +/** + * Registers the given listener as a FigureListener of this figure. + * @param listener The listener to add + */ +void addFigureListener(FigureListener listener); + +/** + * Registers the given listener as a FocusListener of this figure. + * @param listener The listener to add + */ +void addFocusListener(FocusListener listener); + +/** + * Registers the given listener as a KeyListener of this figure. + * @param listener The listener to add + */ +void addKeyListener(KeyListener listener); + +/** + * Registers the given listener on this figure. + * @param listener The listener to add + * @since 3.1 + */ +void addLayoutListener(LayoutListener listener); + +/** + * Registers the given listener as a MouseListener of this IFigure. + * @param listener The listener to add + */ +void addMouseListener(MouseListener listener); + +/** + * Registers the given listener as a MouseMotionListener of this IFigure. + * @param listener The listener to add + */ +void addMouseMotionListener(MouseMotionListener listener); + +/** + * Called after this IFigure is added to its parent. + */ +void addNotify(); + +/** + * Registers the given listener as a PropertyChangeListener of this IFigure. + * @param listener The listener to add + */ +void addPropertyChangeListener(PropertyChangeListener listener); + +/** + * Registers the given listener as a PropertyChangeListener of this IFigure, interested + * only in the given property. + * @param property The property the listener is interested in + * @param listener The listener to add + */ +void addPropertyChangeListener(String property, PropertyChangeListener listener); + +/** + * Returns true if the point (x, y) is contained within this + * IFigure's bounds. + * @param x The X coordinate + * @param y The Y coordinate + * @return true if the point (x,y) is contained in this IFigure's bounds + */ +bool containsPoint(int x, int y); + +/** + * Returns true if the Point p is contained within this IFigure's bounds. + * @param p The point + * @return true if the Point p is contained within this IFigure's bounds + */ +bool containsPoint(Point p); + +/** + * Erases this IFigure. + */ +void erase(); + +/** + * Returns the IFigure at the specified location. May return this or + * null. + * @param x The X coordinate + * @param y The Y coordinate + * @return The IFigure at the specified location + */ +IFigure findFigureAt(int x, int y); + +/** + * Returns the IFigure at the specified location based on the conditional TreeSearch. May + * return this or null + * @param x the X coordinate + * @param y the Y coordinate + * @param search the conditional TreeSearch + * @return the IFigure at the specified location + */ +IFigure findFigureAt(int x, int y, TreeSearch search); + +/** + * Returns the IFigure at the specified location. May return this or + * null. + * @param p The point + * @return The IFigure at the specified location + */ +IFigure findFigureAt(Point p); + +/** + * Returns the IFigure at the specified location, excluding any IFigures in + * collection. May return this or null. + * @param x The X coordinate + * @param y The Y coordinate + * @param collection A collection of IFigures to be excluded + * @return The IFigure at the specified location, excluding any IFigures in collection + */ +IFigure findFigureAtExcluding(int x, int y, Collection collection); + +/** + * Returns the IFigure located at the given location which will accept mouse events. + * @param x The X coordinate + * @param y The Y coordinate + * @return The IFigure located at the given location which will accept mouse events + */ +IFigure findMouseEventTargetAt(int x, int y); + +/** + * Returns the background color. Background color can be inherited from the parent. + * @return The background color + */ +Color getBackgroundColor(); + +/** + * Returns the current border by reference. + * @return The current border + */ +Border getBorder(); + +/** + * Returns the smallest rectangle completely enclosing the IFigure. Implementation may + * return the Rectangle by reference. For this reason, callers of this method must not + * modify the returned Rectangle. The Rectangle's values may change in the future. + * @return This IFigure's bounds + */ +Rectangle getBounds(); + +/** + * Returns an unmodifiable list of children by reference. + * @return An unmodifiable list of children by reference + */ +List getChildren(); + +/** + * Returns the rectangular area within this Figure's bounds in which children will be + * placed (via {@link LayoutManager LayoutManagers}) and the painting of children will be + * clipped. + * @return The client area + */ +Rectangle getClientArea(); + +/** + * Copies the client area into the specificied Recangle, and returns that rectangle for + * convenience. + * @param rect The destination rectangle for the client area + * @return The same instance that was passed in, modified to contain the client area + */ +Rectangle getClientArea(Rectangle rect); + +/** + * Returns the Cursor used when the mouse is over this IFigure. + * @return The Cursor used when the mouse is over this IFigure + */ +Cursor getCursor(); + +/** + * Returns the current Font by reference. + * @return The current Font + */ +Font getFont(); + +/** + * Returns the foreground color. + * @return The foreground color + */ +Color getForegroundColor(); + +/** + * Returns the current Insets. May be returned by reference. The returned value should + * not be modified. + * @return The current Insets. + */ +Insets getInsets(); + +/** + * Returns the current LayoutManager by reference. + * @return The current LayoutManager by reference + */ +LayoutManager getLayoutManager(); + +/** + * Returns the background Color of this Figure. Does not inherit this Color from the + * parent, may return null. + * @return The local background Color + */ +Color getLocalBackgroundColor(); + +/** + * Returns the local foreground Color of this Figure. Does not inherit this Color from the + * parent, may return null. + * @return The local foreground Color + */ +Color getLocalForegroundColor(); + +/** + * Returns a hint indicating the largest desireable size for the IFigure. Returned + * Dimension is by value. + * @return The maximum size + */ +Dimension getMaximumSize(); + +/** + * Returns a hint indicating the smallest desireable size for the IFigure. + * The returned dimension may be by reference, and it must not be modified by the + * caller. + * @return The minimum size + */ +Dimension getMinimumSize(); + +/** + * Returns a hint indicating the smallest desireable size for the IFigure. + * The returned dimension may be by reference, and it must not be modified by the + * caller. + * @param wHint the width hint + * @param hHint the height hint + * @return The minimum size + */ +Dimension getMinimumSize(int wHint, int hHint); + +/** + * Returns the IFigure that is the current parent of this IFigure or null if + * there is no parent. + * @return null or the parent figure + */ +IFigure getParent(); + +/** + * Returns the preferred size for this IFigure. The returned value must not be modified + * by the caller. If the figure has no preference, it returns its current size. The same + * as calling {@link #getPreferredSize(int, int) getPreferredSize(-1, -1)}. + * @return The preferred size + */ +Dimension getPreferredSize(); + +/** + * Returns the preferred size for this IFigure using the provided width and height hints. + * The returned dimension may be by reference, and it must not be modified by the + * caller. A value of -1 indicates that there is no constraint in that + * direction. + * + * @param wHint a width hint + * @param hHint a height hint + * @return The preferred size + */ +Dimension getPreferredSize(int wHint, int hHint); + +/** + * Returns the current size. Returned Dimension is by value. + * @return The current size + */ +Dimension getSize(); + +/** + * Returns a IFigure that is the tooltip for this IFigure. + * @return This IFigure's tooltip + */ +IFigure getToolTip(); + +/** + * Returns the UpdateManager for this IFigure by reference. + * @return The update manager + */ +UpdateManager getUpdateManager(); + +/** + * Called when this IFigure has gained focus. + *

NOTE: You should not override this method. If you are interested in receiving + * notification of this type of event, you should register a {@link FocusListener} with + * this IFigure. + * @param event The focus event + */ +void handleFocusGained(FocusEvent event); + +/** + * Called when this IFigure has lost focus. + *

NOTE: You should not override this method. If you are interested in receiving + * notification of this type of event, you should register a {@link FocusListener} with + * this IFigure. + * @param event The focus event + */ +void handleFocusLost(FocusEvent event); + +/** + * Called when a key is pressed while this IFigure has focus. + *

NOTE: You should not override this method. If you are interested in receiving + * notification of this type of event, you should register a {@link KeyListener} with + * this IFigure. + * @param event The key event + */ +void handleKeyPressed(KeyEvent event); + +/** + * Called when a key is released while this IFigure has focus. + *

NOTE: You should not override this method. If you are interested in receiving + * notification of this type of event, you should register a {@link KeyListener} with + * this IFigure. + * @param event The key event + */ +void handleKeyReleased(KeyEvent event); + +/** + * Called when a mouse button has been double-clicked while within this IFigure's bounds. + *

NOTE: You should not override this method. If you are interested in receiving + * notification of this type of event, you should register a {@link MouseListener} with + * this IFigure. + * @param event The mouse event + */ +void handleMouseDoubleClicked(MouseEvent event); + +/** + * Called when the mouse has been dragged within this IFigure's bounds. + *

NOTE: You should not override this method. If you are interested in receiving + * notification of this type of event, you should register a {@link MouseMotionListener} + * with this IFigure. + * @param event The mouse event + */ +void handleMouseDragged(MouseEvent event); + +/** + * Called when the mouse has entered this IFigure's bounds. + *

NOTE: You should not override this method. If you are interested in receiving + * notification of this type of event, you should register a {@link MouseMotionListener} + * with this IFigure. + * @param event The mouse event + */ +void handleMouseEntered(MouseEvent event); + +/** + * Called when the mouse has exited this IFigure's bounds. + *

NOTE: You should not override this method. If you are interested in receiving + * notification of this type of event, you should register a {@link MouseMotionListener} + * with this IFigure. + * @param event The mouse event + */ +void handleMouseExited(MouseEvent event); + +/** + * Called when the mouse has hovered over this IFigure. + *

NOTE: You should not override this method. If you are interested in receiving + * notification of this type of event, you should register a {@link MouseMotionListener} + * with this IFigure. + * @param event The mouse event + */ +void handleMouseHover(MouseEvent event); + +/** + * Called when the mouse has moved within this IFigure's bounds. + *

NOTE: You should not override this method. If you are interested in receiving + * notification of this type of event, you should register a {@link MouseMotionListener} + * with this IFigure. + * @param event The mouse event + */ +void handleMouseMoved(MouseEvent event); + +/** + * Called when a mouse button has been pressed while within this IFigure's bounds. + *

NOTE: You should not override this method. If you are interested in receiving + * notification of this type of event, you should register a {@link MouseListener} with + * this IFigure. + * @param event The mouse event + */ +void handleMousePressed(MouseEvent event); + +/** + * Called when a mouse button has been released while within this IFigure's bounds. + *

NOTE: You should not override this method. If you are interested in receiving + * notification of this type of event, you should register a {@link MouseListener} with + * this IFigure. + * @param event The mouse event + */ +void handleMouseReleased(MouseEvent event); + +/** + * Returns true if this IFigure has focus. + * @return true if this IFigure has focus + */ +bool hasFocus(); + +/** + * This method is for internal purposes only and should not be called. + * @return The event dispatcher + */ +EventDispatcher internalGetEventDispatcher(); + +/** + * Returns true if this IFigure's bounds intersect with the given Rectangle. + * Figure is asked so that non-rectangular IFigures can reduce the frequency of paints. + * @param rect The rectangle to check for intersection + * @return true if this IFigure's bounds intersect with the given Rectangle + */ +bool intersects(Rectangle rect); + +/** + * Invalidates this IFigure. If this figure has a LayoutManager, then + * {@link LayoutManager#invalidate()} should be called on that layout. + */ +void invalidate(); + +/** + * Invalidates this figure as well as all contained within. + */ +void invalidateTree(); + +/** + * Returns true if this figure is capable of applying a local coordinate + * system which affects its children. + * @since 3.1 + * @return true if this figure provides local coordinates to children + */ +bool isCoordinateSystem(); + +/** + * Returns true if this IFigure is enabled. + * @return true if this IFigure is enabled + */ +bool isEnabled(); + +/** + * Returns true if this IFigure can gain focus on a + * {@link dwt.events.TraverseEvent}. + * @return true if this IFigure can gain focus on a TraverseEvent + */ +bool isFocusTraversable(); + +/** + * @return true if this figure is hosted in a Control that is mirrored + * @since 3.1 + */ +bool isMirrored(); + +/** + * Returns true if this IFigure is opaque. + * @return true if this IFigure is opaque + */ +bool isOpaque(); + +/** + * Returns true if this IFigure can receive focus on a call to + * {@link #requestFocus()}. + * @return true if this IFigure can receive focus on a call to requestFocus() + */ +bool isRequestFocusEnabled(); + +/** + * Returns true if this IFigure is showing. This figure is only showing if + * it is visible and its parent is showing, or it has no parent. + * @return true if this IFigure is showing + */ +bool isShowing(); + +/** + * returns true if this figure's visibility flag is set to true. Does not + * walk up the parent chain. + * @return true if the figure's visibility flag is set + */ +bool isVisible(); + +/** + * Paints this IFigure and its children. + * @param graphics The Graphics object used for painting + */ +void paint(Graphics graphics); + +/** + * Removes the given child from this figure's children. If this figure has a + * LayoutManager, then {@link LayoutManager#remove(IFigure)} shall be called on that + * layout with the child. + * @param figure The IFigure to remove + */ +void remove(IFigure figure); + +/** + * Unregisters the given listener, so that it will no longer receive notification of + * ancestor events. + * @param listener The listener to remove + */ +void removeAncestorListener(AncestorListener listener); + +/** + * Unregisters the given listener, so that it will no longer receive notification of + * coordinate changes. + * @param listener the listener to remove + * @since 3.1 + */ +void removeCoordinateListener(CoordinateListener listener); + +/** + * Unregisters the given listener, so that it will no longer receive notification of + * IFigure events. + * @param listener The listener to remove + */ +void removeFigureListener(FigureListener listener); + +/** + * Unregisters the given listener, so that it will no longer receive notification of focus + * events. + * @param listener The listener to remove + */ +void removeFocusListener(FocusListener listener); + +/** + * Removes the first occurence of the given listener. + * @param listener The listener to remove + */ +void removeKeyListener(KeyListener listener); + +/** + * Removes the most recent occurence of the given listener. + * @since 3.1 + * @param listener the listener to remove + */ +void removeLayoutListener(LayoutListener listener); + +/** + * Unregisters the given listener, so that it will no longer receive notification of mouse + * events. + * @param listener The listener to remove + */ +void removeMouseListener(MouseListener listener); + +/** + * Unregisters the given listener, so that it will no longer receive notification of mouse + * motion events. + * @param listener The listener to remove + */ +void removeMouseMotionListener(MouseMotionListener listener); + +/** + * Called before this IFigure is removed from its parent. + */ +void removeNotify(); + +/** + * Unregisters the given listener, so that it will no longer receive notification of any + * property changes. + * @param listener The listener to remove + */ +void removePropertyChangeListener(PropertyChangeListener listener); + +/** + * Unregisters the given listener, so that it will no longer receive notification of + * changes in the given property. This will only unregister the listener for the given + * property. If the listener is registered to listen to other properties, this will not + * affect the notification of the listener regarding those properties. + * @param property The property that the listener is no longer interested in + * @param listener The listener no longer interested in the property + */ +void removePropertyChangeListener(String property, PropertyChangeListener listener); + +/** + * Repaints this IFigure. + */ +void repaint(); + +/** + * Repaints the rectangular area within this IFigure whose upper-left corner is located at + * the point (x,y) and whose width and height are w and + * h, respectively. + * @param x The X coordinate of the area to repaint + * @param y The Y coordinate of the area to repaint + * @param w The width of the area to repaint + * @param h The height of the area to repaint + */ +void repaint(int x, int y, int w, int h); + +/** + * Repaints the rectangular area within this IFigure represented by rect. + * @param rect The rectangular area to be repainted + */ +void repaint(Rectangle rect); + +/** + * Requests focus from the {@link EventDispatcher}. + */ +void requestFocus(); + +/** + * Invalidates this figure and revalidates() its parent. If a figure does not have a + * parent, it will request a validation from it UpdateManager. Calling this method does + * not guarantee that a repaint will occur. + */ +void revalidate(); + +/** + * Sets the background color. + * @param c The new background color + */ +void setBackgroundColor(Color c); + +/** + * Sets the border. + * @param b The new border + */ +void setBorder(Border b); + +/** + * Sets the bounds to the bounds of the specified Rectangle. + * @param rect The new bounds + */ +void setBounds(Rectangle rect); + +/** + * Convenience method to set the constraint of the specified child in the current + * LayoutManager. + * @param child The figure whose constraint is being set + * @param constraint the constraint + * @throws IllegalArgumentException if the child is not contained by this Figure + */ +void setConstraint(IFigure child, Object constraint); + +/** + * Sets the cursor. + * @param cursor The new cursor + */ +void setCursor(Cursor cursor); + +/** + * Sets this IFigure to be enabled. + * @param value true if this IFigure should be enabled + */ +void setEnabled(bool value); + +/** + * Sets the ability for this IFigure to gain focus on a + * {@link dwt.events.TraverseEvent}. + * @param value true if this IFigure should gain focus on a TraverseEvent + */ +void setFocusTraversable(bool value); + +/** + * Sets the font. + * @param f The new font + */ +void setFont(Font f); + +/** + * Sets the foreground color. + * @param c The new foreground color + */ +void setForegroundColor(Color c); + +/** + * Sets the LayoutManager. + * @param lm The new layout manager + */ +void setLayoutManager(LayoutManager lm); + +/** + * Sets the location of this IFigure. + * @param p The new location + */ +void setLocation(Point p); + +/** + * Sets the maximum size this IFigure can be. + * @param size The new maximum size + */ +void setMaximumSize(Dimension size); + +/** + * Sets the minimum size this IFigure can be. + * @param size The new minimum size + */ +void setMinimumSize(Dimension size); + +/** + * Sets this IFigure to be opaque if isOpaque is true and transparent + * if isOpaque is false. + * @param isOpaque true is this IFigure should be opaque + */ +void setOpaque(bool isOpaque); + +/** + * Sets this IFigure's parent. + * @param parent The new parent IFigure + */ +void setParent(IFigure parent); + +/** + * Sets this IFigure's preferred size. + * @param size The new preferred size + */ +void setPreferredSize(Dimension size); + +/** + * Sets the ability for this Figure to gain focus on a call to {@link #requestFocus()}. + * @param requestFocusEnabled true if this IFigure should gain focus on a call + * to requestFocus() + */ +void setRequestFocusEnabled(bool requestFocusEnabled); + +/** + * Sets this IFigure's size. + * @param d The new size + */ +void setSize(Dimension d); + +/** + * Sets this IFigure's size. + * @param w The new width + * @param h The new height + */ +void setSize(int w, int h); + +/** + * Sets a tooltip that is displayed when the mouse hovers over this IFigure. + * @param figure The tooltip IFigure + */ +void setToolTip(IFigure figure); + +/** + * Sets this IFigure's visibility. + * @param visible true if this IFigure should be visible + */ +void setVisible(bool visible); + +/** + * Moves this IFigure x pixels horizontally and y pixels + * vertically. + * @param x The amount to move this IFigure horizontally + * @param y The amount to move this IFigure vertically + */ +void translate(int x, int y); + +/** + * Translates a Translatable from this IFigure's parent's coordinates to this IFigure's + * local coordinates. + * @param t The object to translate + */ +void translateFromParent(Translatable t); + +/** + * Translates a Translatable that is relative to this figure's bounds to absolute. + * @param t The object to translate + */ +void translateToAbsolute(Translatable t); + +/** + * Translates a Translatable from this IFigure's coordinates to its parent's coordinates. + * @param t The object to translate + */ +void translateToParent(Translatable t); + +/** + * Translates a Translatable in absolute coordinates to be relative to this figure's + * bounds. + * @param t The object to translate + */ +void translateToRelative(Translatable t); + +/** + * Indicates that this figure should make itself valid. Validation includes invoking + * layout on a LayoutManager if present, and then validating all children figures. + * Default validation uses pre-order, depth-first ordering. + */ +void validate(); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ImageFigure.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ImageFigure.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ImageFigure; + +import dwt.dwthelper.utils; + + + +import dwt.graphics.Image; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Figure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.PositionConstants; + +/** + * A Figure that simply contains an Image. Use this Figure, instead of a Label, when + * displaying Images without any accompanying text. This figure is not intended to have a + * layout mananger or children. + *

+ * Note that it is the client's responsibility to dispose the given image. There is no + * "free" resource management in draw2d. + * + * @author Pratik Shah + */ +public class ImageFigure + : Figure +{ + +private Image img; +private Dimension size; +private int alignment; + +/** + * Constructor
+ * The default alignment is PositionConstants.CENTER. + */ +public this() { + this(null, PositionConstants.CENTER); +} + +/** + * Constructor
+ * The default alignment is PositionConstants.CENTER. + * + * @param image The Image to be displayed + */ +public this(Image image) { + this(image, PositionConstants.CENTER); +} + +/** + * Constructor + * + * @param image The Image to be displayed + * @param alignment A PositionConstant indicating the alignment + * + * @see ImageFigure#setImage(Image) + * @see ImageFigure#setAlignment(int) + */ +public this(Image image, int alignment) { + size = new Dimension(); + setImage(image); + setAlignment(alignment); +} + +/** + * @return The Image that this Figure displays + */ +public Image getImage() { + return img; +} + +/** + * Calculates the necessary size to display the Image within the figure's client area. + * + * @see dwtx.draw2d.Figure#getPreferredSize(int, int) + */ +public Dimension getPreferredSize(int wHint, int hHint) { + if (getInsets() is IFigure_NO_INSETS) + return size; + Insets i = getInsets(); + return size.getExpanded(i.getWidth(), i.getHeight()); +} + +/** + * @see dwtx.draw2d.Figure#paintFigure(Graphics) + */ +protected void paintFigure(Graphics graphics) { + super.paintFigure(graphics); + + if (getImage() is null) + return; + + int x, y; + Rectangle area = getClientArea(); + switch (alignment & PositionConstants.NORTH_SOUTH) { + case PositionConstants.NORTH: + y = area.y; + break; + case PositionConstants.SOUTH: + y = area.y + area.height - size.height; + break; + default: + y = (area.height - size.height) / 2 + area.y; + break; + } + switch (alignment & PositionConstants.EAST_WEST) { + case PositionConstants.EAST: + x = area.x + area.width - size.width; + break; + case PositionConstants.WEST: + x = area.x; + break; + default: + x = (area.width - size.width) / 2 + area.x; + break; + } + graphics.drawImage(getImage(), x, y); +} + +/** + * Sets the alignment of the Image within this Figure. The alignment comes into play + * when the ImageFigure is larger than the Image. The alignment could be any valid + * combination of the following: + * + *

    + *
  • PositionConstants.NORTH
  • + *
  • PositionConstants.SOUTH
  • + *
  • PositionConstants.EAST
  • + *
  • PositionConstants.WEST
  • + *
  • PositionConstants.CENTER or PositionConstants.NONE
  • + *
+ * + * @param flag A constant indicating the alignment + */ +public void setAlignment(int flag) { + alignment = flag; +} + +/** + * Sets the Image that this ImageFigure displays. + *

+ * IMPORTANT: Note that it is the client's responsibility to dispose the given image. + * + * @param image The Image to be displayed. It can be null. + */ +public void setImage(Image image) { + if (img is image) + return; + img = image; + if (img !is null) + size = (new Rectangle(image.getBounds())).getSize(); + else + size = new Dimension(); + revalidate(); + repaint(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ImageUtilities.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ImageUtilities.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ImageUtilities; + +import dwt.dwthelper.utils; + + +static import dwt.graphics.Rectangle; +import dwt.DWT; +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.FontMetrics; +import dwt.graphics.GC; +import dwt.graphics.Image; +import dwt.graphics.ImageData; +import dwt.graphics.PaletteData; +import dwt.graphics.RGB; +import dwt.widgets.Display; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.FigureUtilities; + +/** + * @author Pratik Shah + * @since 3.0 + */ +public class ImageUtilities { + +/** + * Returns a new Image with the given String rotated left (by 90 degrees). The String + * will be rendered using the provided colors and fonts. The client is responsible for + * disposing the returned Image. Strings cannot contain newline or tab characters. This + * method MUST be invoked from the user-interface (Display) thread. + * + * @param string the String to be rendered + * @param font the font + * @param foreground the text's color + * @param background the background color + * @return an Image which must be disposed + */ +public static Image createRotatedImageOfString(String string, Font font, + Color foreground, Color background) { + Display display = Display.getCurrent(); + if (display is null) + DWT.error(DWT.ERROR_THREAD_INVALID_ACCESS); + + FontMetrics metrics = FigureUtilities.getFontMetrics(font); + Dimension strSize = FigureUtilities.getStringExtents(string, font); + Image srcImage = new Image(display, strSize.width, metrics.getAscent()); + GC gc = new GC(srcImage); + gc.setFont(font); + gc.setForeground(foreground); + gc.setBackground(background); + gc.fillRectangle(srcImage.getBounds()); + gc.drawString(string, 0, 0 - metrics.getLeading()); + Image result = createRotatedImage(srcImage); + gc.dispose(); + srcImage.dispose(); + return result; +} + +/** + * Returns a new Image that is the given Image rotated left by 90 degrees. The client is + * responsible for disposing the returned Image. This method MUST be invoked from the + * user-interface (Display) thread. + * + * @param srcImage the Image that is to be rotated left + * @return the rotated Image (the client is responsible for disposing it) + */ +public static Image createRotatedImage(Image srcImage) { + Display display = Display.getCurrent(); + if (display is null) + DWT.error(DWT.ERROR_THREAD_INVALID_ACCESS); + + ImageData srcData = srcImage.getImageData(); + ImageData destData; + if (srcData.depth < 8) + destData = rotatePixelByPixel(srcData); + else + destData = rotateOptimized(srcData); + + return new Image(display, destData); +} + +/** + * Creates an ImageData representing the given Image shaded with the given + * Color. + * + * @param fromImage Image that has to be shaded + * @param shade The Color to be used for shading + * @return A new ImageData that can be used to create an Image. + */ +public static ImageData createShadedImage(Image fromImage, Color shade) { + dwt.graphics.Rectangle.Rectangle r = fromImage.getBounds(); + ImageData data = fromImage.getImageData(); + PaletteData palette = data.palette; + if (!palette.isDirect) { + /* Convert the palette entries */ + RGB [] rgbs = palette.getRGBs(); + for (int i = 0; i < rgbs.length; i++) { + if (data.transparentPixel !is i) { + RGB color = rgbs [i]; + color.red = determineShading(color.red, shade.getRed()); + color.blue = determineShading(color.blue, shade.getBlue()); + color.green = determineShading(color.green, shade.getGreen()); + } + } + data.palette = new PaletteData(rgbs); + } else { + /* Convert the pixels. */ + int[] scanline = new int[r.width]; + int redMask = palette.redMask; + int greenMask = palette.greenMask; + int blueMask = palette.blueMask; + int redShift = palette.redShift; + int greenShift = palette.greenShift; + int blueShift = palette.blueShift; + for (int y = 0; y < r.height; y++) { + data.getPixels(0, y, r.width, scanline, 0); + for (int x = 0; x < r.width; x++) { + int pixel = scanline[x]; + int red = pixel & redMask; + red = (redShift < 0) ? red >>> -redShift : red << redShift; + int green = pixel & greenMask; + green = (greenShift < 0) ? green >>> -greenShift : green << greenShift; + int blue = pixel & blueMask; + blue = (blueShift < 0) ? blue >>> -blueShift : blue << blueShift; + red = determineShading(red, shade.getRed()); + blue = determineShading(blue, shade.getBlue()); + green = determineShading(green, shade.getGreen()); + red = (redShift < 0) ? red << -redShift : red >> redShift; + red &= redMask; + green = (greenShift < 0) ? green << -greenShift : green >> greenShift; + green &= greenMask; + blue = (blueShift < 0) ? blue << -blueShift : blue >> blueShift; + blue &= blueMask; + scanline[x] = red | blue | green; + } + data.setPixels(0, y, r.width, scanline, 0); + } + } + return data; +} + +private static int determineShading(int origColor, int shadeColor) { + return (origColor + shadeColor) / 2; +} + +private static ImageData rotateOptimized(ImageData srcData) { + int bytesPerPixel = Math.max(1, srcData.depth / 8); + int destBytesPerLine = ((srcData.height * bytesPerPixel - 1) / srcData.scanlinePad + 1) + * srcData.scanlinePad; + byte[] newData = new byte[destBytesPerLine * srcData.width]; + for (int srcY = 0; srcY < srcData.height; srcY++) { + for (int srcX = 0; srcX < srcData.width; srcX++) { + int destX = srcY; + int destY = srcData.width - srcX - 1; + int destIndex = (destY * destBytesPerLine) + (destX * bytesPerPixel); + int srcIndex = (srcY * srcData.bytesPerLine) + (srcX * bytesPerPixel); + System.arraycopy(srcData.data, srcIndex, newData, destIndex, bytesPerPixel); + } + } + return new ImageData(srcData.height, srcData.width, srcData.depth, srcData.palette, + srcData.scanlinePad, newData); +} + +private static ImageData rotatePixelByPixel(ImageData srcData) { + ImageData destData = new ImageData(srcData.height, srcData.width, srcData.depth, + srcData.palette); + for (int y = 0; y < srcData.height; y++) { + for (int x = 0; x < srcData.width; x++) { + destData.setPixel(y, srcData.width - x - 1, srcData.getPixel(x, y)); + } + } + return destData; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/InputEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/InputEvent.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.InputEvent; + +import dwt.dwthelper.utils; + +import dwt.DWT; +import dwtx.draw2d.EventDispatcher; +import dwtx.draw2d.IFigure; + +/** + * The base class for Draw2d events. + */ +public abstract class InputEvent + : /+java.util.+/EventObject +{ + +private int state; + +private bool consumed = false; + +/** @see DWT#ALT */ +public static const int ALT = DWT.ALT; +/** @see DWT#CONTROL */ +public static const int CONTROL = DWT.CONTROL; +/** @see DWT#SHIFT */ +public static const int SHIFT = DWT.SHIFT; +/** @see DWT#BUTTON1 */ +public static const int BUTTON1 = DWT.BUTTON1; +/** @see DWT#BUTTON2 */ +public static const int BUTTON2 = DWT.BUTTON2; +/** @see DWT#BUTTON3 */ +public static const int BUTTON3 = DWT.BUTTON3; +/** @see DWT#BUTTON4 */ +public static const int BUTTON4 = DWT.BUTTON4; +/** @see DWT#BUTTON5 */ +public static const int BUTTON5 = DWT.BUTTON5; +/** A bitwise OR'ing of {@link #BUTTON1}, {@link #BUTTON2}, {@link #BUTTON3}, + * {@link #BUTTON4} and {@link #BUTTON5} */ +public static const int ANY_BUTTON = DWT.BUTTON_MASK; + +/** + * Constructs a new InputEvent. + * @param dispatcher the event dispatcher + * @param source the source of the event + * @param state the state + */ +public this(EventDispatcher dispatcher, IFigure source, int state) { + super(cast(Object)source); + this.state = state; +} + +/** + * Marks this event as consumed so that it doesn't get passed on to other listeners. + */ +public void consume() { + consumed = true; +} + +/** + * Returns the event statemask, which is a bitwise OR'ing of any of the following: + * {@link #ALT}, {@link #CONTROL}, {@link #SHIFT}, {@link #BUTTON1}, {@link #BUTTON2}, + * {@link #BUTTON3}, {@link #BUTTON4} and {@link #BUTTON5}. + * @return the state + */ +public int getState() { + return state; +} + +/** + * @return whether this event has been consumed. + */ +public bool isConsumed() { + return consumed; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/KeyEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/KeyEvent.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.KeyEvent; + +import dwt.dwthelper.utils; +import dwtx.draw2d.InputEvent; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.EventDispatcher; +static import dwt.events.KeyEvent; + +/** + * An event caused by the user interacting with the keyboard. + */ +public class KeyEvent + : InputEvent +{ + +/** + * The character that was pressed. + * @see dwt.events.KeyEvent#character + */ +public char character; + +/** + * The keycode. + * @see dwt.events.KeyEvent#keyCode + */ +public int keycode; + +/** + * Constructs a new KeyEvent. + * @param dispatcher the event dispatcher + * @param source the source of the event + * @param ke an DWT key event used to supply the statemask, keycode and character + */ +public this(EventDispatcher dispatcher, IFigure source, + dwt.events.KeyEvent.KeyEvent ke) { + super(dispatcher, source, ke.stateMask); + character = ke.character; + keycode = ke.keyCode; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/KeyListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/KeyListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.KeyListener; + +import dwt.dwthelper.utils; +import dwtx.draw2d.KeyEvent; + +/** + * A listener interface for receiving {@link KeyEvent KeyEvents} from the keyboard. + */ +public interface KeyListener { + +/** + * Called when a key is pressed. + * @param ke The KeyEvent object + */ +void keyPressed(KeyEvent ke); + +/** + * Called when a key is released. + * @param ke The KeyEvent object + */ +void keyReleased(KeyEvent ke); + +} + +/** + * An empty implementation of KeyListener for convenience. + */ +class KeyListenerStub + : KeyListener +{ + /** + * @see dwtx.draw2d.KeyListener#keyPressed(KeyEvent) + */ + public void keyPressed(KeyEvent ke) { } + /** + * @see dwtx.draw2d.KeyListener#keyReleased(KeyEvent) + */ + public void keyReleased(KeyEvent ke) { } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Label.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Label.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,706 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +module dwtx.draw2d.Label; + +import dwt.dwthelper.utils; + + + +import dwt.graphics.Font; +import dwt.graphics.Image; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Figure; +import dwtx.draw2d.PositionConstants; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.TextUtilities; +import dwtx.draw2d.ColorConstants; + +/** + * A figure that can display text and/or an image. + */ +public class Label + : Figure + , PositionConstants +{ +alias Figure.getPreferredSize getPreferredSize; + +private static String ELLIPSIS = "..."; //$NON-NLS-1$ + + +private Image icon; +private String text = "";//$NON-NLS-1$ +private String subStringText; +private Dimension textSize; +private Dimension subStringTextSize; +private Dimension iconSize; +private Point iconLocation; +private Point textLocation; +private int textAlignment = CENTER; +private int iconAlignment = CENTER; +private int labelAlignment = CENTER; +private int textPlacement = EAST; +private int iconTextGap = 3; + +/** + * Construct an empty Label. + * + * @since 2.0 + */ +public this() { + iconSize = new Dimension(0, 0); +} + +/** + * Construct a Label with passed String as its text. + * + * @param s the label text + * @since 2.0 + */ +public this(String s) { + this(); + setText(s); +} + +/** + * Construct a Label with passed Image as its icon. + * + * @param i the label image + * @since 2.0 + */ +public this(Image i) { + this(); + setIcon(i); +} + +/** + * Construct a Label with passed String as text and passed Image as its icon. + * + * @param s the label text + * @param i the label image + * @since 2.0 + */ +public this(String s, Image i) { + this(); + setText(s); + setIcon(i); +} + +private void alignOnHeight(Point loc, Dimension size, int alignment) { + Insets insets = getInsets(); + switch(alignment) { + case TOP: + loc.y = insets.top; + break; + case BOTTOM: + loc.y = bounds.height - size.height - insets.bottom; + break; + default: + loc.y = (bounds.height - size.height) / 2; + } +} + +private void alignOnWidth(Point loc, Dimension size, int alignment) { + Insets insets = getInsets(); + switch(alignment) { + case LEFT: + loc.x = insets.left; + break; + case RIGHT: + loc.x = bounds.width - size.width - insets.right; + break; + default: + loc.x = (bounds.width - size.width) / 2; + } +} + +private void calculateAlignment() { + switch(textPlacement) { + case EAST: + case WEST: + alignOnHeight(textLocation, getTextSize(), textAlignment); + alignOnHeight(iconLocation, getIconSize(), iconAlignment); + break; + case NORTH: + case SOUTH: + alignOnWidth(textLocation, getSubStringTextSize(), textAlignment); + alignOnWidth(iconLocation, getIconSize(), iconAlignment); + break; + } +} + +/** + * Calculates the size of the Label using the passed Dimension as the size of the Label's + * text. + * + * @param txtSize the precalculated size of the label's text + * @return the label's size + * @since 2.0 + */ +protected Dimension calculateLabelSize(Dimension txtSize) { + int gap = getIconTextGap(); + if (getIcon() is null || getText().equals("")) //$NON-NLS-1$ + gap = 0; + Dimension d = new Dimension(0, 0); + if (textPlacement is WEST || textPlacement is EAST) { + d.width = getIconSize().width + gap + txtSize.width; + d.height = Math.max(getIconSize().height, txtSize.height); + } else { + d.width = Math.max(getIconSize().width, txtSize.width); + d.height = getIconSize().height + gap + txtSize.height; + } + return d; +} + +private void calculateLocations() { + textLocation = new Point(); + iconLocation = new Point(); + + calculatePlacement(); + calculateAlignment(); + Dimension offset = getSize().getDifference(getPreferredSize()); + offset.width += getTextSize().width - getSubStringTextSize().width; + switch (labelAlignment) { + case CENTER: + offset.scale(0.5f); + break; + case LEFT: + offset.scale(0.0f); + break; + case RIGHT: + offset.scale(1.0f); + break; + case TOP: + offset.height = 0; + offset.scale(0.5f); + break; + case BOTTOM: + offset.height = offset.height * 2; + offset.scale(0.5f); + break; + default: + offset.scale(0.5f); + break; + } + + switch (textPlacement) { + case EAST: + case WEST: + offset.height = 0; + break; + case NORTH: + case SOUTH: + offset.width = 0; + break; + } + + textLocation.translate(offset); + iconLocation.translate(offset); +} + +private void calculatePlacement() { + int gap = getIconTextGap(); + if (icon is null || text.equals("")) //$NON-NLS-1$ + gap = 0; + Insets insets = getInsets(); + + switch(textPlacement) { + case EAST: + iconLocation.x = insets.left; + textLocation.x = getIconSize().width + gap + insets.left; + break; + case WEST: + textLocation.x = insets.left; + iconLocation.x = getSubStringTextSize().width + gap + insets.left; + break; + case NORTH: + textLocation.y = insets.top; + iconLocation.y = getTextSize().height + gap + insets.top; + break; + case SOUTH: + textLocation.y = getIconSize().height + gap + insets.top; + iconLocation.y = insets.top; + } +} + +/** + * Calculates the size of the Label's text size. The text size calculated takes into + * consideration if the Label's text is currently truncated. If text size without + * considering current truncation is desired, use {@link #calculateTextSize()}. + * + * @return the size of the label's text, taking into account truncation + * @since 2.0 + */ +protected Dimension calculateSubStringTextSize() { + return getTextUtilities().getTextExtents(getSubStringText(), getFont()); +} + +/** + * Calculates and returns the size of the Label's text. Note that this Dimension is + * calculated using the Label's full text, regardless of whether or not its text is + * currently truncated. If text size considering current truncation is desired, use + * {@link #calculateSubStringTextSize()}. + * + * @return the size of the label's text, ignoring truncation + * @since 2.0 + */ +protected Dimension calculateTextSize() { + return getTextUtilities().getTextExtents(getText(), getFont()); +} + +private void clearLocations() { + iconLocation = textLocation = null; +} + +/** + * Returns the Label's icon. + * + * @return the label icon + * @since 2.0 + */ +public Image getIcon() { + return icon; +} + +/** + * Returns the current alignment of the Label's icon. The default is + * {@link PositionConstants#CENTER}. + * + * @return the icon alignment + * @since 2.0 + */ +public int getIconAlignment() { + return iconAlignment; +} + +/** + * Returns the bounds of the Label's icon. + * + * @return the icon's bounds + * @since 2.0 + */ +public Rectangle getIconBounds() { + Rectangle bounds = getBounds(); + return new Rectangle(bounds.getLocation().translate(getIconLocation()), getIconSize()); +} + +/** + * Returns the location of the Label's icon relative to the Label. + * + * @return the icon's location + * @since 2.0 + */ +protected Point getIconLocation() { + if (iconLocation is null) + calculateLocations(); + return iconLocation; +} + +/** + * Returns the gap in pixels between the Label's icon and its text. + * + * @return the gap + * @since 2.0 + */ +public int getIconTextGap() { + return iconTextGap; +} + +/** + * @see IFigure#getMinimumSize(int, int) + */ +public Dimension getMinimumSize(int w, int h) { + if (minSize !is null) + return minSize; + minSize = new Dimension(); + if (getLayoutManager() !is null) + minSize.setSize(getLayoutManager().getMinimumSize(this, w, h)); + + Dimension labelSize = + calculateLabelSize(getTextUtilities().getTextExtents(getTruncationString(), getFont()) + .intersect(getTextUtilities().getTextExtents(getText(), getFont()))); + Insets insets = getInsets(); + labelSize.expand(insets.getWidth(), insets.getHeight()); + minSize.union_(labelSize); + return minSize; +} + +/** + * @see IFigure#getPreferredSize(int, int) + */ +public Dimension getPreferredSize(int wHint, int hHint) { + if (prefSize is null) { + prefSize = calculateLabelSize(getTextSize()); + Insets insets = getInsets(); + prefSize.expand(insets.getWidth(), insets.getHeight()); + if (getLayoutManager() !is null) + prefSize.union_(getLayoutManager().getPreferredSize(this, wHint, hHint)); + } + if (wHint >= 0 && wHint < prefSize.width) { + Dimension minSize = getMinimumSize(wHint, hHint); + Dimension result = prefSize.getCopy(); + result.width = Math.min(result.width, wHint); + result.width = Math.max(minSize.width, result.width); + return result; + } + return prefSize; +} + +/** + * Calculates the amount of the Label's current text will fit in the Label, including an + * elipsis "..." if truncation is required. + * + * @return the substring + * @since 2.0 + */ +public String getSubStringText() { + if (subStringText !is null) + return subStringText; + + subStringText = text; + int widthShrink = getPreferredSize().width - getSize().width; + if (widthShrink <= 0) + return subStringText; + + Dimension effectiveSize = getTextSize().getExpanded(-widthShrink, 0); + Font currentFont = getFont(); + int dotsWidth = getTextUtilities().getTextExtents(getTruncationString(), currentFont).width; + + if (effectiveSize.width < dotsWidth) + effectiveSize.width = dotsWidth; + + int subStringLength = getTextUtilities().getLargestSubstringConfinedTo(text, + currentFont, + effectiveSize.width - dotsWidth); + subStringText = (text.substring(0, subStringLength) ~ getTruncationString()).dup; + return subStringText; +} + +/** + * Returns the size of the Label's current text. If the text is currently truncated, the + * truncated text with its ellipsis is used to calculate the size. + * + * @return the size of this label's text, taking into account truncation + * @since 2.0 + */ +protected Dimension getSubStringTextSize() { + if (subStringTextSize is null) + subStringTextSize = calculateSubStringTextSize(); + return subStringTextSize; +} + +/** + * Returns the text of the label. Note that this is the complete text of the label, + * regardless of whether it is currently being truncated. Call {@link #getSubStringText()} + * to return the label's current text contents with truncation considered. + * + * @return the complete text of this label + * @since 2.0 + */ +public String getText() { + return text; +} + +/** + * Returns the current alignment of the Label's text. The default text alignment is + * {@link PositionConstants#CENTER}. + * + * @return the text alignment + */ +public int getTextAlignment() { + return textAlignment; +} + +/** + * Returns the bounds of the label's text. Note that the bounds are calculated using the + * label's complete text regardless of whether the label's text is currently truncated. + * + * @return the bounds of this label's complete text + * @since 2.0 + */ +public Rectangle getTextBounds() { + Rectangle bounds = getBounds(); + return new Rectangle(bounds.getLocation().translate(getTextLocation()), textSize); +} + +/** + * Returns the location of the label's text relative to the label. + * + * @return the text location + * @since 2.0 + */ +protected Point getTextLocation() { + if (textLocation !is null) + return textLocation; + calculateLocations(); + return textLocation; +} + +/** + * Returns the current placement of the label's text relative to its icon. The default + * text placement is {@link PositionConstants#EAST}. + * + * @return the text placement + * @since 2.0 + */ +public int getTextPlacement() { + return textPlacement; +} + +/** + * Returns the size of the label's complete text. Note that the text used to make this + * calculation is the label's full text, regardless of whether the label's text is + * currently being truncated and is displaying an ellipsis. If the size considering + * current truncation is desired, call {@link #getSubStringTextSize()}. + * + * @return the size of this label's complete text + * @since 2.0 + */ +protected Dimension getTextSize() { + if (textSize is null) + textSize = calculateTextSize(); + return textSize; +} + +/** + * @see IFigure#invalidate() + */ +public void invalidate() { + prefSize = null; + minSize = null; + clearLocations(); + textSize = null; + subStringTextSize = null; + subStringText = null; + super.invalidate(); +} + +/** + * Returns true if the label's text is currently truncated and is displaying + * an ellipsis, false otherwise. + * + * @return true if the label's text is truncated + * @since 2.0 + */ +public bool isTextTruncated() { + return !getSubStringText().equals(getText()); +} + +/** + * @see Figure#paintFigure(Graphics) + */ +protected void paintFigure(Graphics graphics) { + if (isOpaque()) + super.paintFigure(graphics); + Rectangle bounds = getBounds(); + graphics.translate(bounds.x, bounds.y); + if (icon !is null) + graphics.drawImage(icon, getIconLocation()); + if (!isEnabled()) { + graphics.translate(1, 1); + graphics.setForegroundColor(ColorConstants.buttonLightest); + graphics.drawText(getSubStringText(), getTextLocation()); + graphics.translate(-1, -1); + graphics.setForegroundColor(ColorConstants.buttonDarker); + } + graphics.drawText(getSubStringText(), getTextLocation()); + graphics.translate(-bounds.x, -bounds.y); +} + +/** + * Sets the label's icon to the passed image. + * + * @param image the new label image + * @since 2.0 + */ +public void setIcon(Image image) { + if (icon is image) + return; + icon = image; + //Call repaint, in case the image dimensions are the same. + repaint(); + if (icon is null) + setIconDimension(new Dimension()); + else + setIconDimension(new Dimension(image)); +} + +/** + * This method sets the alignment of the icon within the bounds of the label. If the label + * is larger than the icon, then the icon will be aligned according to this alignment. + * Valid values are: + *

    + *
  • {@link PositionConstants#CENTER} + *
  • {@link PositionConstants#TOP} + *
  • {@link PositionConstants#BOTTOM} + *
  • {@link PositionConstants#LEFT} + *
  • {@link PositionConstants#RIGHT} + *
+ * @param align the icon alignment + * @since 2.0 + */ +public void setIconAlignment (int align_) { + if (iconAlignment is align_) + return; + iconAlignment = align_; + clearLocations(); + repaint(); +} + +/** + * Sets the label's icon size to the passed Dimension. + * + * @param d the new icon size + * @deprecated the icon is automatically displayed at 1:1 + * @since 2.0 + */ +public void setIconDimension(Dimension d) { + if (d.opEquals(getIconSize())) + return; + iconSize = d; + revalidate(); +} + +/** + * Sets the gap in pixels between the label's icon and text to the passed value. The + * default is 4. + * + * @param gap the gap + * @since 2.0 + */ +public void setIconTextGap(int gap) { + if (iconTextGap is gap) + return; + iconTextGap = gap; + repaint(); + revalidate(); +} + +/** + * Sets the alignment of the label (icon and text) within the figure. If this + * figure's bounds are larger than the size needed to display the label, the + * label will be aligned accordingly. Valid values are: + *
    + *
  • {@link PositionConstants#CENTER} + *
  • {@link PositionConstants#TOP} + *
  • {@link PositionConstants#BOTTOM} + *
  • {@link PositionConstants#LEFT} + *
  • {@link PositionConstants#RIGHT} + *
+ * + * @param align label alignment + */ +public void setLabelAlignment(int align_) { + if (labelAlignment is align_) + return; + labelAlignment = align_; + clearLocations(); + repaint(); +} + +/** + * Sets the label's text. + * @param s the new label text + * @since 2.0 + */ +public void setText(String s) { + //"text" will never be null. + if (s is null) + s = "";//$NON-NLS-1$ + if (text.equals(s)) + return; + text = s; + revalidate(); + repaint(); +} + +/** + * Sets the alignment of the text relative to the icon within the label. The text + * alignment must be orthogonal to the text placement. For example, if the placement + * is EAST, then the text can be aligned using TOP, CENTER, or BOTTOM. Valid values are: + *
    + *
  • {@link PositionConstants#CENTER} + *
  • {@link PositionConstants#TOP} + *
  • {@link PositionConstants#BOTTOM} + *
  • {@link PositionConstants#LEFT} + *
  • {@link PositionConstants#RIGHT} + *
+ * @see #setLabelAlignment(int) + * @param align the text alignment + * @since 2.0 + */ +public void setTextAlignment(int align_) { + if (textAlignment is align_) + return; + textAlignment = align_; + clearLocations(); + repaint(); +} + +/** + * Sets the placement of the text relative to the icon within the label. + * Valid values are: + *
    + *
  • {@link PositionConstants#EAST} + *
  • {@link PositionConstants#NORTH} + *
  • {@link PositionConstants#SOUTH} + *
  • {@link PositionConstants#WEST} + *
+ * + * @param where the text placement + * @since 2.0 + */ +public void setTextPlacement (int where) { + if (textPlacement is where) + return; + textPlacement = where; + revalidate(); + repaint(); +} + +/** + * Gets the TextUtilities instance to be used in measurement + * calculations. + * + * @return a TextUtilities instance + * @since 3.4 + */ +public TextUtilities getTextUtilities() { + return TextUtilities.INSTANCE; +} + +/** + * Gets the string that will be appended to the text when the label is + * truncated. By default, this returns an ellipsis. + * + * @return the string to append to the text when truncated + * @since 3.4 + */ +protected String getTruncationString() { + return ELLIPSIS; +} + +/** + * Gets the icon size + * + * @return the icon size + * @since 3.4 + */ +protected Dimension getIconSize() { + return iconSize; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/LabelAnchor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/LabelAnchor.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.LabelAnchor; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.ChopboxAnchor; +import dwtx.draw2d.Label; + +/** + * LabelAnchors must have an owner of type {@link Label}. The LabelAnchor behaves like + * {@link ChopboxAnchor} but {@link Connection Connections} will point to the center of + * its owner's icon as opposed to the center of the entire owning Label. + */ +public class LabelAnchor + : ChopboxAnchor +{ + +/** + * Constructs a LabelAnchor with no owner. + * + * @since 2.0 + */ +protected this() { } + +/** + * Constructs a LabelAnchor with owner label. + * @param label This LabelAnchor's owner + * @since 2.0 + */ +public this(Label label) { + super(label); +} + +/** + * Returns the bounds of this LabelAnchor's owning Label icon. + * @return The bounds of this LabelAnchor's owning Label icon + * @since 2.0 + */ +protected Rectangle getBox() { + Label label = cast(Label)getOwner(); + return label.getIconBounds(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/LabeledBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/LabeledBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.LabeledBorder; + +import dwt.dwthelper.utils; + +import dwt.graphics.Font; +import dwtx.draw2d.Border; + +/** + * LabeledBorders have a text message somewhere on them. The Font for the text can be set. + * LabeledBorders should not change their Insets when the label changes, therefore, + * Figures using this Border should repaint() when updating the label, and revalidate() + * when changing the Font. + */ +public interface LabeledBorder + : Border +{ + +/** + * Returns the label for this Border. + * @return The label for this Border + */ +String getLabel(); + +/** + * Sets the Font for the label. + * @param f The Font to be set + */ +void setFont(Font f); + +/** + * Sets the text to be displayed as the label for this Border. + * @param l The text + */ +void setLabel(String l); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/LabeledContainer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/LabeledContainer.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.LabeledContainer; + +import dwt.dwthelper.utils; + +import dwt.graphics.Font; +import dwtx.draw2d.Figure; +import dwtx.draw2d.Border; +import dwtx.draw2d.LabeledBorder; +import dwtx.draw2d.GroupBoxBorder; +import dwtx.draw2d.CompoundBorder; + +/** + * A Container with a title bar describing the contents of the container. The frame is + * generated by a {@link LabeledBorder}. + */ +public class LabeledContainer + : Figure +{ + +/** + * Constructs a default container with a {@link GroupBoxBorder}. + * + * @since 2.0 + */ +public this() { + this(new GroupBoxBorder()); +} + +/** + * Constructs a labeled container with the border given as input. + * + * @param border the border + * @since 2.0 + */ +public this(Border border) { + setBorder(border); + setOpaque(true); +} + +private static LabeledBorder findLabeledBorder(Border border) { + if ( auto b = cast(LabeledBorder)border ) + return b; + if ( auto cb = cast(CompoundBorder)border ) { + LabeledBorder labeled = findLabeledBorder(cb.getInnerBorder()); + if (labeled is null) + labeled = findLabeledBorder(cb.getOuterBorder()); + return labeled; + } + return null; +} + +/** + * Returns the text of the LabeledContainer's label. + * + * @return the label text + * @since 2.0 + */ +public String getLabel() { + return getLabeledBorder().getLabel(); +} + +/** + * Returns the LabeledBorder of this container. + * + * @return the border + * @since 2.0 + */ +protected LabeledBorder getLabeledBorder() { + return findLabeledBorder(getBorder()); +} + +/** + * Sets the title of the container. + * + * @param s the new title text + * @since 2.0 + */ +public void setLabel(String s) { + getLabeledBorder().setLabel(s); + revalidate(); + repaint(); +} + +/** + * Sets the font to be used for the container title. + * + * @param f the new font + * @since 2.0 + */ +public void setLabelFont(Font f) { + getLabeledBorder().setFont(f); + revalidate(); + repaint(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Layer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Layer.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Layer; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.Figure; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.TreeSearch; + + +/** + * A transparent figure intended to be added exclusively to a {@link LayeredPane}, who has + * the responsibilty of managing its layers. + */ +public class Layer + : Figure +{ + +/** + * Overridden to implement transparent behavior. + * @see IFigure#containsPoint(int, int) + * + */ +public bool containsPoint(int x, int y) { + if (isOpaque()) + return super.containsPoint(x, y); + Point pt = new Point(x, y); + translateFromParent(pt); + for (int i = 0; i < getChildren().size(); i++) { + IFigure child = cast(IFigure)getChildren().get(i); + if (child.containsPoint(pt.x, pt.y)) + return true; + } + return false; +} + +/** + * Overridden to implement transparency. + * @see IFigure#findFigureAt(int, int, TreeSearch) + */ +public IFigure findFigureAt(int x, int y, TreeSearch search) { + if (!isEnabled()) + return null; + if (isOpaque()) + return super.findFigureAt(x, y, search); + + IFigure f = super.findFigureAt(x, y, search); + if (f is this) + return null; + return f; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/LayeredPane.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/LayeredPane.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.LayeredPane; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.Layer; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.StackLayout; + +/** + * A figure capable of holding any number of layers. Only layers can be added to this + * figure. Layers are added to this figure with thier respective keys, which are used to + * identify them. + */ +public class LayeredPane + : Layer +{ + +private List layerKeys; + +/** + * Constructs a new layered pane with no layers in it. + */ +public this() { + layerKeys = new ArrayList(); + setLayoutManager(new StackLayout()); +} + +/** + * Adds the given layer figure, identifiable with the given key, at the specified index. + * While adding the layer, it informs the surrounding layers of the addition. + * + * @param figure the layer + * @param layerKey the layer's key + * @param index the index where the layer should be added + * @since 2.0 + */ +public void add(IFigure figure, Object layerKey, int index) { + if (index is -1) + index = layerKeys.size(); + super.add(figure, null, index); + layerKeys.add(index, layerKey); +} + +/** + * Adds the given layer, identifiable with the given key, under the after layer + * provided in the input. + * + * @param layer the layer + * @param key the layer's key + * @param after the layer under which the input layer should be added + * @since 2.0 + */ +public void addLayerAfter(Layer layer, Object key, Object after) { + int index = layerKeys.indexOf(after); + add(layer, key, ++index); +} + +/** + * Adds the given layer, identifiable with the given key, above the before layer + * provided in the input. + * + * @param layer the layer + * @param key the layer's key + * @param before the layer above which the input layer should be added + * @since 2.0 + */ +public void addLayerBefore(Layer layer, Object key, Object before) { + int index = layerKeys.indexOf(before); + add(layer, key, index); +} + +/** + * Returns the layer identified by the key given in the input. + * + * @param key the key to identify the desired layer + * @return the desired layer + * @since 2.0 + */ +public Layer getLayer(Object key) { + int index = layerKeys.indexOf(key); + if (index is -1) + return null; + return cast(Layer)getChildren().get(index); +} + +/** + * Returns the layer at the specified index in this pane. + * + * @param index the index of the desired layer + * @return the desired layer + * @since 2.0 + */ +protected Layer getLayer(int index) { + return cast(Layer)getChildren().get(index); +} + +/** + * @see dwtx.draw2d.IFigure#remove(dwtx.draw2d.IFigure) + */ +public void remove(IFigure figure) { + int index = getChildren().indexOf(cast(Object)figure); + if (index !is -1) + layerKeys.remove(index); + super.remove(figure); +} + +/** + * Removes the layer identified by the given key from this layered pane. + * + * @param key the key of the layer to be removed + * @since 2.0 + */ +public void removeLayer(Object key) { + removeLayer(layerKeys.indexOf(key)); +} + +/** + * Removes the given layer from this layered pane. + * + * @deprecated call {@link IFigure#remove(IFigure)} instead + * @param layer the layer to be removed + * @since 2.0 + */ +public void removeLayer(IFigure layer) { + remove(layer); +} + +/** + * Removes the layer at the specified index from the list of layers in this layered pane. + * It collapses the layers, occupying the space vacated by the removed layer. + * + * @param index the index of the layer to be removed + * @since 2.0 + */ +protected void removeLayer(int index) { + Layer removeLayer = getLayer(index); + remove(removeLayer); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/LayoutAnimator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/LayoutAnimator.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.LayoutAnimator; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Animator; +import dwtx.draw2d.LayoutListener; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Animation; + +/** + * Animates the layout of a figure's children. The animator will capture the effects of a + * layout manager, and then play back the placement of children using linear interpolation + * for each child's start and end locations. + *

+ * To use an animator, hook it as a layout listener for the figure whose layout is to + * be animated, by calling {@link IFigure#addLayoutListener(LayoutListener)}. It is not + * necessary to have an animator for every figure in a composition that is undergoing + * animation. For example, if a figure without an animator moves during the animation, it + * will continue to move and layout its children normally during each step of the + * animation. + *

+ * Animator must be used in conjunction with layouts. If figures are placed manually using + * setBounds(), the animator may not be able to track and playback the + * changes that occur. + * + * @since 3.2 + */ +public class LayoutAnimator : Animator , LayoutListener { + +static const LayoutAnimator INSTANCE; + +static this(){ + INSTANCE = new LayoutAnimator(); +} +/** + * Constructs a new Animator. The default instance ({@link #getDefault()}) can be used on + * all figures being animated. + * + * @since 3.2 + */ +protected this() { } + +/** + * Returns an object encapsulating the placement of children in a container. This method + * is called to capture both the initial and final states. + * @param container the container figure + * @return the current state + * @since 3.2 + */ +protected Object getCurrentState(IFigure container) { + Map locations = new HashMap(); + List children = container.getChildren(); + IFigure child; + for (int i = 0; i < children.size(); i++) { + child = cast(IFigure)children.get(i); + locations.put(cast(Object)child, child.getBounds().getCopy()); + } + return cast(Object)locations; +} + +/** + * Returns the default instance. + * @return the default instance + * @since 3.2 + */ +public static LayoutAnimator getDefault() { + return INSTANCE; +} + +/** + * Hooks invalidation in case animation is in progress. + * @see LayoutListener#invalidate(IFigure) + */ +public final void invalidate(IFigure container) { + if (Animation.isInitialRecording()) + Animation.hookAnimator(container, this); +} + +/** + * Hooks layout in case animation is in progress. + * @see dwtx.draw2d.LayoutListener#layout(dwtx.draw2d.IFigure) + */ +public final bool layout(IFigure container) { + if (Animation.isAnimating()) + return Animation.hookPlayback(container, this); + return false; +} + +/** + * Plays back the animated layout. + * @see Animator#playback(IFigure) + */ +protected bool playback(IFigure container) { + Map initial = cast(Map) Animation.getInitialState(this, container); + Map ending = cast(Map) Animation.getFinalState(this, container); + if (initial is null) + return false; + List children = container.getChildren(); + + float progress = Animation.getProgress(); + float ssergorp = 1 - progress; + + Rectangle rect1, rect2; + + for (int i = 0; i < children.size(); i++) { + IFigure child = cast(IFigure) children.get(i); + rect1 = cast(Rectangle)initial.get(cast(Object)child); + rect2 = cast(Rectangle)ending.get(cast(Object)child); + + //TODO need to change this to hide the figure until the end. + if (rect1 is null) + continue; + child.setBounds(new Rectangle( + cast(int)Math.round(progress * rect2.x + ssergorp * rect1.x), + cast(int)Math.round(progress * rect2.y + ssergorp * rect1.y), + cast(int)Math.round(progress * rect2.width + ssergorp * rect1.width), + cast(int)Math.round(progress * rect2.height + ssergorp * rect1.height) + )); + } + return true; +} + +/** + * Hooks post layout in case animation is in progress. + * @see LayoutListener#postLayout(IFigure) + */ +public final void postLayout(IFigure container) { + if (Animation.isFinalRecording()) + Animation.hookNeedsCapture(container, this); +} + +/** + * This callback is unused. Reserved for possible future use. + * @see LayoutListener#remove(IFigure) + */ +public final void remove(IFigure child) { } + +/** + * This callback is unused. Reserved for possible future use. + * @see LayoutListener#setConstraint(IFigure, Object) + */ +public final void setConstraint(IFigure child, Object constraint) { } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/LayoutListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/LayoutListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.LayoutListener; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.IFigure; + + +/** + * Classes which implement this interface provide callback hooks for various layout + * related events. + *

+ * Instances can be hooked to figures by calling {@link + * IFigure#addLayoutListener(LayoutListener)}. Listeners will be made aware of various + * steps of the layout mechanism, and even have the opportunity to prevent normal layout + * from occurring. + * @since 3.1 + */ +public interface LayoutListener { + +/** + * Called when a container has been invalidated. + * @param container the invalidated Figure + * @since 3.1 + */ +void invalidate(IFigure container); + +/** + * Called prior to layout occurring. A listener may intercept a layout by + * returning true. If the layout is intercepted, the container's + * LayoutManager will not receive a layout call. + * @param container the figure incurring a layout + * @return true if the layout has been intercepted by the listener + * @since 3.1 + */ +bool layout(IFigure container); + +/** + * Called after layout has occurred. + * @since 3.1 + * @param container the figure incurring a layout + */ +void postLayout(IFigure container); + +/** + * Called when a child is about to be removed from its parent. + * @since 3.1 + * @param child the child being removed + */ +void remove(IFigure child); + +/** + * Called when a child's constraint is initialized or updated. + * @param child the child being updated + * @param constraint the child's new constraint + * @since 3.1 + */ +void setConstraint(IFigure child, Object constraint); + +} + +/** + * A stub implementation which implements all of the declared methods. + * @since 3.1 + */ +class LayoutListenerStub : LayoutListener { + + /** + * Stub which does nothing. + * @see LayoutListener#invalidate(IFigure) + */ + public void invalidate(IFigure container) { } + + /** + * Stub which does nothing. + * @see LayoutListener#layout(IFigure) + */ + public bool layout(IFigure container) { + return false; + } + + /** + * Stub which does nothing. + * @see LayoutListener#postLayout(IFigure) + */ + public void postLayout(IFigure container) { } + + /** + * Stub which does nothing. + * @see LayoutListener#remove(IFigure) + */ + public void remove(IFigure child) { } + + /** + * Stub which does nothing. + * @see LayoutListener#setConstraint(IFigure, java.lang.Object) + */ + public void setConstraint(IFigure child, Object constraint) { } + +} \ No newline at end of file diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/LayoutManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/LayoutManager.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.LayoutManager; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.IFigure; + +/** + * A helper for positioning child figures and determining the ideal size for a figure + * with children. + */ +public interface LayoutManager { + +/** + * Returns the constraint for the given figure. + * @param child The figure + * @return The constraint + */ +Object getConstraint(IFigure child); + +/** + * Returns the minimum size of the given figure. + * @param container The Figure + * @param wHint the width hint + * @param hHint the height hint + * @return The minimum size + */ +Dimension getMinimumSize(IFigure container, int wHint, int hHint); + +/** + * Returns the preferred size of the given figure, using width and height hints. + * @param container The figure + * @param wHint The width hint + * @param hHint The height hint + * @return The preferred size + */ +Dimension getPreferredSize(IFigure container, int wHint, int hHint); + +/** + * Tells the LayoutManager to throw away all cached information about the figures it is + * responsible for. This method is called whenever the owning figure is invalidated. + */ +void invalidate(); + +/** + * Lays out the given figure. + * @param container The figure + */ +void layout(IFigure container); + +/** + * Removes the given child from this layout. + * @param child the child being remoced + */ +void remove(IFigure child); + +/** + * Sets the constraint for the given child. + * @param child The figure + * @param constraint The constraint + */ +void setConstraint(IFigure child, Object constraint); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/LightweightSystem.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/LightweightSystem.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,560 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.LightweightSystem; + +import dwt.dwthelper.utils; + +import dwt.DWT; +import dwt.accessibility.AccessibleControlEvent; +import dwt.accessibility.AccessibleControlListener; +import dwt.accessibility.AccessibleEvent; +import dwt.accessibility.AccessibleListener; +import dwt.events.ControlAdapter; +import dwt.events.ControlEvent; +import dwt.events.DisposeEvent; +import dwt.events.DisposeListener; +import dwt.events.FocusEvent; +import dwt.events.FocusListener; +import dwt.events.KeyEvent; +import dwt.events.KeyListener; +import dwt.events.MouseEvent; +import dwt.events.MouseListener; +import dwt.events.MouseMoveListener; +import dwt.events.MouseTrackListener; +import dwt.events.TraverseEvent; +import dwt.events.TraverseListener; +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.GC; +import dwt.widgets.Canvas; +import dwt.widgets.Event; +import dwt.widgets.Listener; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.EventDispatcher; +import dwtx.draw2d.UpdateManager; +import dwtx.draw2d.Figure; +import dwtx.draw2d.DeferredUpdateManager; +import dwtx.draw2d.SWTEventDispatcher; +import dwtx.draw2d.StackLayout; +import dwtx.draw2d.BufferedGraphicsSource; +import dwtx.draw2d.NativeGraphicsSource; + +/** + * The LightweightSystem is the link between DWT and Draw2d. It is the component that + * provides the ability for {@link Figure Figures} to be hosted on an DWT Canvas. + *

+ * Normal procedure for using a LightweightSystem: + *

    + *
  1. Create an DWT Canvas. + *
  2. Create a LightweightSystem passing it that Canvas. + *
  3. Create a Draw2d Figure and call setContents(IFigure). This Figure will be the + * top-level Figure of the Draw2d application. + *
+ */ +public class LightweightSystem { + +private Canvas canvas; +IFigure contents; +private IFigure root; +private EventDispatcher dispatcher; +private UpdateManager manager; +private int ignoreResize; + +/** + * Constructs a LightweightSystem on Canvas c. + * + * @param c the canvas + * @since 2.0 + */ +public this(Canvas c) { + this(); + setControl(c); +} + +/** + * Constructs a LightweightSystem without a Canvas. + */ +public this() { + manager = new DeferredUpdateManager(); + init(); +} + +/** + * Adds DWT listeners to the LightWeightSystem's Canvas. This allows for DWT events to be + * dispatched and handled by its {@link EventDispatcher}. + *

+ * WARNING: This method should not be overridden. + * @since 2.0 + */ +protected void addListeners() { + EventHandler handler = createEventHandler(); + canvas.getAccessible().addAccessibleListener(handler); + canvas.getAccessible().addAccessibleControlListener(handler); + canvas.addMouseListener(handler); + canvas.addMouseMoveListener(handler); + canvas.addMouseTrackListener(handler); + canvas.addKeyListener(handler); + canvas.addTraverseListener(handler); + canvas.addFocusListener(handler); + canvas.addDisposeListener(handler); + canvas.addListener(DWT.MouseWheel, handler); + + canvas.addControlListener(new class() ControlAdapter { + public void controlResized(ControlEvent e) { + this.outer.controlResized(); + } + }); + canvas.addListener(DWT.Paint, new class() Listener { + public void handleEvent(Event e) { + this.outer.paint(e.gc); + } + }); +} + +/** + * Resizes and revalidates the root figure when the control is resized. + */ +protected void controlResized() { + if (ignoreResize > 0) + return; + Rectangle r = new Rectangle(canvas.getClientArea()); + r.setLocation(0, 0); + root.setBounds(r); + root.revalidate(); + getUpdateManager().performUpdate(); +} + +/** + * Returns this LightwightSystem's EventDispatcher. + * + * @return the event dispatcher + * @since 2.0 + */ +protected EventDispatcher getEventDispatcher() { + if (dispatcher is null) + setEventDispatcher(new SWTEventDispatcher()); + return dispatcher; +} + +/** + * Returns this LightweightSystem's root figure. + * + * @return the root figure + * @since 2.0 + */ +public IFigure getRootFigure() { + return root; +} + +/** + * Returns a new instance of this LightweightSystem's EventHandler. + * + * @return the newly created event handler + * @since 2.0 + */ +protected final EventHandler createEventHandler() { + return internalCreateEventHandler(); +} + +/** + * Creates and returns the root figure. + * + * @return the newly created root figure + */ +protected RootFigure createRootFigure() { + RootFigure f = new RootFigure(); + f.addNotify(); + f.setOpaque(true); + f.setLayoutManager(new StackLayout()); + return f; +} + +/** + * Returns this LightweightSystem's UpdateManager. + * + * @return the update manager + * @since 2.0 + */ +public UpdateManager getUpdateManager() { + return manager; +} + +/** + * Initializes this LightweightSystem by setting the root figure. + */ +protected void init() { + setRootPaneFigure(createRootFigure()); +} + +EventHandler internalCreateEventHandler() { + return new EventHandler(); +} + +/** + * Invokes this LightweightSystem's {@link UpdateManager} to paint this + * LightweightSystem's Canvas and contents. + * + * @param gc the GC used for painting + * @since 2.0 + */ +public void paint(GC gc) { + getUpdateManager().paint(gc); +} + +/** + * Sets the contents of the LightweightSystem to the passed figure. This figure should be + * the top-level Figure in a Draw2d application. + * + * @param figure the new root figure + * @since 2.0 + */ +public void setContents(IFigure figure) { + if (contents !is null) + root.remove(contents); + contents = figure; + root.add(contents); +} + +/** + * Sets the LightweightSystem's control to the passed Canvas. + * + * @param c the canvas + * @since 2.0 + */ +public void setControl(Canvas c) { + if (canvas is c) + return; + canvas = c; + if ((c.getStyle() & DWT.DOUBLE_BUFFERED) !is 0) + getUpdateManager().setGraphicsSource(new NativeGraphicsSource(canvas)); + else + getUpdateManager().setGraphicsSource(new BufferedGraphicsSource(canvas)); + getEventDispatcher().setControl(c); + addListeners(); + + //Size the root figure and contents to the current control's size + Rectangle r = new Rectangle(canvas.getClientArea()); + r.setLocation(0, 0); + root.setBounds(r); + root.revalidate(); +} + +/** + * Sets this LightweightSystem's EventDispatcher. + * + * @param dispatcher the new event dispatcher + * @since 2.0 + */ +public void setEventDispatcher(EventDispatcher dispatcher) { + this.dispatcher = dispatcher; + dispatcher.setRoot(root); + dispatcher.setControl(canvas); +} + +void setIgnoreResize(bool value) { + if (value) + ignoreResize++; + else + ignoreResize--; +} + +/** + * Sets this LightweightSystem's root figure. + * @param root the new root figure + */ +protected void setRootPaneFigure(RootFigure root) { + getUpdateManager().setRoot(root); + this.root = root; +} + +/** + * Sets this LightweightSystem's UpdateManager. + * + * @param um the new update manager + * @since 2.0 + */ +public void setUpdateManager(UpdateManager um) { + manager = um; + manager.setRoot(root); +} + +/** + * The figure at the root of the LightweightSystem. If certain properties (i.e. font, + * background/foreground color) are not set, the RootFigure will obtain these properties + * from LightweightSystem's Canvas. + */ +protected class RootFigure + : Figure +{ + /** @see IFigure#getBackgroundColor() */ + public Color getBackgroundColor() { + if (bgColor !is null) + return bgColor; + if (canvas !is null) + return canvas.getBackground(); + return null; + } + + /** @see IFigure#getFont() */ + public Font getFont() { + if (font !is null) + return font; + if (canvas !is null) + return canvas.getFont(); + return null; + } + + /** @see IFigure#getForegroundColor() */ + public Color getForegroundColor() { + if (fgColor !is null) + return fgColor; + if (canvas !is null) + return canvas.getForeground(); + return null; + } + + /** @see IFigure#getUpdateManager() */ + public UpdateManager getUpdateManager() { + return this.outer.getUpdateManager(); + } + + /** @see IFigure#internalGetEventDispatcher() */ + public EventDispatcher internalGetEventDispatcher() { + return getEventDispatcher(); + } + + /** + * @see IFigure#isMirrored() + */ + public bool isMirrored() { + return (this.outer.canvas.getStyle() & DWT.MIRRORED) !is 0; + } + + /** @see Figure#isShowing() */ + public bool isShowing() { + return true; + } +} + +/** + * Listener used to get all necessary events from the Canvas and pass them on to the + * {@link EventDispatcher}. + */ +protected class EventHandler + : MouseMoveListener, MouseListener, AccessibleControlListener, KeyListener, + TraverseListener, FocusListener, AccessibleListener, MouseTrackListener, + Listener, DisposeListener +{ + /** @see FocusListener#focusGained(FocusEvent) */ + public void focusGained(FocusEvent e) { + getEventDispatcher().dispatchFocusGained(e); + } + + /** @see FocusListener#focusLost(FocusEvent) */ + public void focusLost(FocusEvent e) { + getEventDispatcher().dispatchFocusLost(e); + } + + /** @see AccessibleControlListener#getChild(AccessibleControlEvent) */ + public void getChild(AccessibleControlEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getChild(e); + } + + /** @see AccessibleControlListener#getChildAtPoint(AccessibleControlEvent) */ + public void getChildAtPoint(AccessibleControlEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getChildAtPoint(e); + } + + /** @see AccessibleControlListener#getChildCount(AccessibleControlEvent) */ + public void getChildCount(AccessibleControlEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getChildCount(e); + } + + /** @see AccessibleControlListener#getChildren(AccessibleControlEvent) */ + public void getChildren(AccessibleControlEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getChildren(e); + } + + /** @see AccessibleControlListener#getDefaultAction(AccessibleControlEvent) */ + public void getDefaultAction(AccessibleControlEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getDefaultAction(e); + } + + /** @see AccessibleListener#getDescription(AccessibleEvent) */ + public void getDescription(AccessibleEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getDescription(e); + } + + /** @see AccessibleControlListener#getFocus(AccessibleControlEvent) */ + public void getFocus(AccessibleControlEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getFocus(e); + } + + /** @see AccessibleListener#getHelp(AccessibleEvent) */ + public void getHelp(AccessibleEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getHelp(e); + } + + /** @see AccessibleListener#getKeyboardShortcut(AccessibleEvent) */ + public void getKeyboardShortcut(AccessibleEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getKeyboardShortcut(e); + } + + /** @see AccessibleControlListener#getLocation(AccessibleControlEvent) */ + public void getLocation(AccessibleControlEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getLocation(e); + } + + /** @see AccessibleListener#getName(AccessibleEvent) */ + public void getName(AccessibleEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getName(e); + } + + /** @see AccessibleControlListener#getRole(AccessibleControlEvent) */ + public void getRole(AccessibleControlEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getRole(e); + } + + /** @see AccessibleControlListener#getSelection(AccessibleControlEvent) */ + public void getSelection(AccessibleControlEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getSelection(e); + } + + /** @see AccessibleControlListener#getState(AccessibleControlEvent) */ + public void getState(AccessibleControlEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getState(e); + } + + /** @see AccessibleControlListener#getValue(AccessibleControlEvent) */ + public void getValue(AccessibleControlEvent e) { + EventDispatcher.AccessibilityDispatcher ad; + ad = getEventDispatcher().getAccessibilityDispatcher_package(); + if (ad !is null) + ad.getValue(e); + } + + /** + * @see Listener#handleEvent(dwt.widgets.Event) + * @since 3.1 + */ + public void handleEvent(Event event) { + // Mouse wheel events + if (event.type is DWT.MouseWheel) + getEventDispatcher().dispatchMouseWheelScrolled(event); + } + + /** @see KeyListener#keyPressed(KeyEvent) */ + public void keyPressed(KeyEvent e) { + getEventDispatcher().dispatchKeyPressed(e); + } + + /** @see KeyListener#keyReleased(KeyEvent) */ + public void keyReleased(KeyEvent e) { + getEventDispatcher().dispatchKeyReleased(e); + } + + /** @see TraverseListener#keyTraversed(TraverseEvent) */ + public void keyTraversed(TraverseEvent e) { + /* + * Doit is almost always false by default for Canvases with KeyListeners. Set to + * true to allow normal behavior. For example, in Dialogs ESC should close. + */ + e.doit = true; + getEventDispatcher().dispatchKeyTraversed(e); + } + + /** @see MouseListener#mouseDoubleClick(MouseEvent) */ + public void mouseDoubleClick(MouseEvent e) { + getEventDispatcher().dispatchMouseDoubleClicked(e); + } + + /**@see MouseListener#mouseDown(MouseEvent)*/ + public void mouseDown(MouseEvent e) { + getEventDispatcher().dispatchMousePressed(e); + } + + /**@see MouseTrackListener#mouseEnter(MouseEvent)*/ + public void mouseEnter(MouseEvent e) { + getEventDispatcher().dispatchMouseEntered(e); + } + + /**@see MouseTrackListener#mouseExit(MouseEvent)*/ + public void mouseExit(MouseEvent e) { + getEventDispatcher().dispatchMouseExited(e); + } + + /**@see MouseTrackListener#mouseHover(MouseEvent)*/ + public void mouseHover(MouseEvent e) { + getEventDispatcher().dispatchMouseHover(e); + } + + /**@see MouseMoveListener#mouseMove(MouseEvent)*/ + public void mouseMove(MouseEvent e) { + getEventDispatcher().dispatchMouseMoved(e); + } + + /**@see MouseListener#mouseUp(MouseEvent)*/ + public void mouseUp(MouseEvent e) { + getEventDispatcher().dispatchMouseReleased(e); + } + + /**@see DisposeListener#widgetDisposed(DisposeEvent)*/ + public void widgetDisposed(DisposeEvent e) { + getUpdateManager().dispose(); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/LineBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/LineBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.LineBorder; + +import dwt.dwthelper.utils; + + +import dwtx.draw2d.AbstractBorder; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; + +import dwt.graphics.Color; +import dwtx.draw2d.geometry.Insets; + +/** + * Provides for a line border with sides of equal widths. + */ +public class LineBorder + : AbstractBorder +{ + +private int width = 1; +private Color color; + +/** + * Constructs a LineBorder with the specified color and of the specified width. + * + * @param color The color of the border. + * @param width The width of the border in pixels. + * @since 2.0 + */ +public this(Color color, int width) { + setColor(color); + setWidth(width); +} + +/** + * Constructs a LineBorder with the specified color and a width of 1 pixel. + * + * @param color The color of the border. + * @since 2.0 + */ +public this(Color color) { + this(color, 1); +} + +/** + * Constructs a black LineBorder with the specified width. + * + * @param width The width of the border in pixels. + * @since 2.0 + */ +public this(int width) { + this(null, width); +} + +/** + * Constructs a default black LineBorder with a width of one pixel. + * + * @since 2.0 + */ +public this() { } + +/** + * Returns the line color of this border. + * @return The line color of this border + */ +public Color getColor() { + return color; +} + +/** + * Returns the space used by the border for the figure provided as input. In this border + * all sides always have equal width. + * @param figure The figure this border belongs to + * @return This border's insets + */ +public Insets getInsets(IFigure figure) { + return new Insets(getWidth()); +} + +/** + * Returns the line width of this border. + * @return The line width of this border + */ +public int getWidth() { + return width; +} + +/** + * Returns true since this border is opaque. Being opaque it is responsible + * to fill in the area within its boundaries. + * @return true since this border is opaque + */ +public bool isOpaque() { + return true; +} + +/** + * @see dwtx.draw2d.Border#paint(IFigure, Graphics, Insets) + */ +public void paint(IFigure figure, Graphics graphics, Insets insets) { + tempRect.setBounds(getPaintRectangle(figure, insets)); + if (getWidth() % 2 is 1) { + tempRect.width--; + tempRect.height--; + } + tempRect.shrink(getWidth() / 2, getWidth() / 2); + graphics.setLineWidth(getWidth()); + if (getColor() !is null) + graphics.setForegroundColor(getColor()); + graphics.drawRectangle(tempRect); +} + +/** + * Sets the line color for this border. + * @param color The line color + */ +public void setColor(Color color) { + this.color = color; +} + +/** + * Sets the line width for this border. + * @param width The line width + */ +public void setWidth(int width) { + this.width = width; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Locator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Locator.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Locator; + +import dwt.dwthelper.utils; +import dwtx.draw2d.IFigure; + +/** + * Controls the location of an IFigure. + */ +public interface Locator { + +/** + * Relocates the given IFigure. + * @param target The figure to relocate + */ +void relocate(IFigure target); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ManhattanConnectionRouter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ManhattanConnectionRouter.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,394 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ManhattanConnectionRouter; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.Ray; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.AbstractRouter; +import dwtx.draw2d.Connection; +import dwtx.draw2d.ConnectionAnchor; + +/** + * Provides a {@link Connection} with an orthogonal route between the Connection's source + * and target anchors. + */ +public final class ManhattanConnectionRouter + : AbstractRouter +{ + +private Map rowsUsed; +private Map colsUsed; +//private Hashtable offsets = new Hashtable(7); + +private Map reservedInfo; + +private class ReservedInfo { + public List reservedRows; + public List reservedCols; + this(){ + reservedRows = new ArrayList(2); + reservedCols = new ArrayList(2); + } +} + +private static Ray UP, DOWN, LEFT, RIGHT; + + +static this(){ + UP = new Ray(0, -1); + DOWN = new Ray(0, 1); + LEFT = new Ray(-1, 0); + RIGHT = new Ray(1, 0); +} + +public this(){ + rowsUsed = new HashMap(); + colsUsed = new HashMap(); + reservedInfo = new HashMap(); +} + +/** + * @see ConnectionRouter#invalidate(Connection) + */ +public void invalidate(Connection connection) { + removeReservedLines(connection); +} + +private int getColumnNear(Connection connection, int r, int n, int x) { + int min = Math.min(n, x), + max = Math.max(n, x); + if (min > r) { + max = min; + min = r - (min - r); + } + if (max < r) { + min = max; + max = r + (r - max); + } + int proximity = 0; + int direction = -1; + if (r % 2 is 1) + r--; + Integer i; + while (proximity < r) { + i = new Integer(r + proximity * direction); + if (!colsUsed.containsKey(i)) { + colsUsed.put(i, i); + reserveColumn(connection, i); + return i.intValue(); + } + int j = i.intValue(); + if (j <= min) + return j + 2; + if (j >= max) + return j - 2; + if (direction is 1) + direction = -1; + else { + direction = 1; + proximity += 2; + } + } + return r; +} + +/** + * Returns the direction the point p is in relation to the given rectangle. + * Possible values are LEFT (-1,0), RIGHT (1,0), UP (0,-1) and DOWN (0,1). + * + * @param r the rectangle + * @param p the point + * @return the direction from r to p + */ +protected Ray getDirection(Rectangle r, Point p) { + int i, distance = Math.abs(r.x - p.x); + Ray direction; + + direction = LEFT; + + i = Math.abs(r.y - p.y); + if (i <= distance) { + distance = i; + direction = UP; + } + + i = Math.abs(r.bottom() - p.y); + if (i <= distance) { + distance = i; + direction = DOWN; + } + + i = Math.abs(r.right() - p.x); + if (i < distance) { + distance = i; + direction = RIGHT; + } + + return direction; +} + +protected Ray getEndDirection(Connection conn) { + ConnectionAnchor anchor = conn.getTargetAnchor(); + Point p = getEndPoint(conn); + Rectangle rect; + if (anchor.getOwner() is null) + rect = new Rectangle(p.x - 1, p.y - 1, 2, 2); + else { + rect = conn.getTargetAnchor().getOwner().getBounds().getCopy(); + conn.getTargetAnchor().getOwner().translateToAbsolute(rect); + } + return getDirection(rect, p); +} + +protected int getRowNear(Connection connection, int r, int n, int x) { + int min = Math.min(n, x), + max = Math.max(n, x); + if (min > r) { + max = min; + min = r - (min - r); + } + if (max < r) { + min = max; + max = r + (r - max); + } + + int proximity = 0; + int direction = -1; + if (r % 2 is 1) + r--; + Integer i; + while (proximity < r) { + i = new Integer(r + proximity * direction); + if (!rowsUsed.containsKey(i)) { + rowsUsed.put(i, i); + reserveRow(connection, i); + return i.intValue(); + } + int j = i.intValue(); + if (j <= min) + return j + 2; + if (j >= max) + return j - 2; + if (direction is 1) + direction = -1; + else { + direction = 1; + proximity += 2; + } + } + return r; +} + +protected Ray getStartDirection(Connection conn) { + ConnectionAnchor anchor = conn.getSourceAnchor(); + Point p = getStartPoint(conn); + Rectangle rect; + if (anchor.getOwner() is null) + rect = new Rectangle(p.x - 1, p.y - 1, 2, 2); + else { + rect = conn.getSourceAnchor().getOwner().getBounds().getCopy(); + conn.getSourceAnchor().getOwner().translateToAbsolute(rect); + } + return getDirection(rect, p); +} + +protected void processPositions(Ray start, Ray end, List positions, + bool horizontal, Connection conn) { + removeReservedLines(conn); + + int pos[] = new int[positions.size() + 2]; + if (horizontal) + pos[0] = start.x; + else + pos[0] = start.y; + int i; + for (i = 0; i < positions.size(); i++) { + pos[i + 1] = (cast(Integer)positions.get(i)).intValue(); + } + if (horizontal is (positions.size() % 2 is 1)) + pos[++i] = end.x; + else + pos[++i] = end.y; + + PointList points = new PointList(); + points.addPoint(new Point(start.x, start.y)); + Point p; + int current, prev, min, max; + bool adjust; + for (i = 2; i < pos.length - 1; i++) { + horizontal = !horizontal; + prev = pos[i - 1]; + current = pos[i]; + + adjust = (i !is pos.length - 2); + if (horizontal) { + if (adjust) { + min = pos[i - 2]; + max = pos[i + 2]; + pos[i] = current = getRowNear(conn, current, min, max); + } + p = new Point(prev, current); + } else { + if (adjust) { + min = pos[i - 2]; + max = pos[i + 2]; + pos[i] = current = getColumnNear(conn, current, min, max); + } + p = new Point(current, prev); + } + points.addPoint(p); + } + points.addPoint(new Point(end.x, end.y)); + conn.setPoints(points); +} + +/** + * @see ConnectionRouter#remove(Connection) + */ +public void remove(Connection connection) { + removeReservedLines(connection); +} + +protected void removeReservedLines(Connection connection) { + ReservedInfo rInfo = cast(ReservedInfo) reservedInfo.get(cast(Object)connection); + if (rInfo is null) + return; + + for (int i = 0; i < rInfo.reservedRows.size(); i++) { + rowsUsed.remove(rInfo.reservedRows.get(i)); + } + for (int i = 0; i < rInfo.reservedCols.size(); i++) { + colsUsed.remove(rInfo.reservedCols.get(i)); + } + reservedInfo.remove(cast(Object)connection); +} + +protected void reserveColumn(Connection connection, Integer column) { + ReservedInfo info = cast(ReservedInfo) reservedInfo.get(cast(Object)connection); + if (info is null) { + info = new ReservedInfo(); + reservedInfo.put(cast(Object)connection, info); + } + info.reservedCols.add(column); +} + +protected void reserveRow(Connection connection, Integer row) { + ReservedInfo info = cast(ReservedInfo) reservedInfo.get(cast(Object)connection); + if (info is null) { + info = new ReservedInfo(); + reservedInfo.put(cast(Object)connection, info); + } + info.reservedRows.add(row); +} + +/** + * @see ConnectionRouter#route(Connection) + */ +public void route(Connection conn) { + if ((conn.getSourceAnchor() is null) || (conn.getTargetAnchor() is null)) + return; + int i; + Point startPoint = getStartPoint(conn); + conn.translateToRelative(startPoint); + Point endPoint = getEndPoint(conn); + conn.translateToRelative(endPoint); + + Ray start = new Ray(startPoint); + Ray end = new Ray(endPoint); + Ray average = start.getAveraged(end); + + Ray direction = new Ray(start, end); + Ray startNormal = getStartDirection(conn); + Ray endNormal = getEndDirection(conn); + + List positions = new ArrayList(5); + bool horizontal = startNormal.isHorizontal(); + if (horizontal) + positions.add(new Integer(start.y)); + else + positions.add(new Integer(start.x)); + horizontal = !horizontal; + + if (startNormal.dotProduct(endNormal) is 0) { + if ((startNormal.dotProduct(direction) >= 0) + && (endNormal.dotProduct(direction) <= 0)) { + // 0 + } else { + // 2 + if (startNormal.dotProduct(direction) < 0) + i = startNormal.similarity(start.getAdded(startNormal.getScaled(10))); + else { + if (horizontal) + i = average.y; + else + i = average.x; + } + positions.add(new Integer(i)); + horizontal = !horizontal; + + if (endNormal.dotProduct(direction) > 0) + i = endNormal.similarity(end.getAdded(endNormal.getScaled(10))); + else { + if (horizontal) + i = average.y; + else + i = average.x; + } + positions.add(new Integer(i)); + horizontal = !horizontal; + } + } else { + if (startNormal.dotProduct(endNormal) > 0) { + //1 + if (startNormal.dotProduct(direction) >= 0) + i = startNormal.similarity(start.getAdded(startNormal.getScaled(10))); + else + i = endNormal.similarity(end.getAdded(endNormal.getScaled(10))); + positions.add(new Integer(i)); + horizontal = !horizontal; + } else { + //3 or 1 + if (startNormal.dotProduct(direction) < 0) { + i = startNormal.similarity(start.getAdded(startNormal.getScaled(10))); + positions.add(new Integer(i)); + horizontal = !horizontal; + } + + if (horizontal) + i = average.y; + else + i = average.x; + positions.add(new Integer(i)); + horizontal = !horizontal; + + if (startNormal.dotProduct(direction) < 0) { + i = endNormal.similarity(end.getAdded(endNormal.getScaled(10))); + positions.add(new Integer(i)); + horizontal = !horizontal; + } + } + } + if (horizontal) + positions.add(new Integer(end.y)); + else + positions.add(new Integer(end.x)); + + processPositions(start, end, positions, startNormal.isHorizontal(), conn); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/MarginBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/MarginBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.MarginBorder; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.AbstractBorder; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; + +/** + * A border that provides blank padding. + */ +public class MarginBorder + : AbstractBorder +{ + +/** + * This border's insets. + */ +protected Insets insets; + +/** + * Constructs a MarginBorder with dimensions specified by insets. + * + * @param insets The Insets for the border + * @since 2.0 + */ +public this(Insets insets) { + this.insets = insets; +} + +/** + * Constructs a MarginBorder with padding specified by the passed values. + * + * @param t Top padding + * @param l Left padding + * @param b Bottom padding + * @param r Right padding + * @since 2.0 + */ +public this(int t, int l, int b, int r) { + this(new Insets(t, l, b, r)); +} + +/** + * Constructs a MarginBorder with equal padding on all sides. + * + * @param allsides Padding size for all sides of the border. + * @since 2.0 + */ +public this(int allsides) { + this(new Insets(allsides)); +} +/** + * @see dwtx.draw2d.Border#getInsets(IFigure) + */ +public Insets getInsets(IFigure figure) { + return insets; +} + +/** + * This method does nothing, since this border is just for spacing. + * @see dwtx.draw2d.Border#paint(IFigure, Graphics, Insets) + */ +public void paint(IFigure figure, Graphics graphics, Insets insets) { } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/MidpointLocator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/MidpointLocator.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.MidpointLocator; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.ConnectionLocator; +import dwtx.draw2d.Connection; + +/** + * A ConnectionLocator that is used to place figures at the midpoint between two points on + * a {@link dwtx.draw2d.Connection}. + */ +public class MidpointLocator + : ConnectionLocator +{ + +private int index; + +/** + * Constructs a MidpointLocator with associated Connection c and index i. + * The points at index i and i+1 on the connection are used to calculate the midpoint of + * the line segment. + * + * @param c the connection associated with the locator + * @param i the point from where the connection's midpoint will be calculated. + * @since 2.0 + */ +public this(Connection c, int i) { + super(c); + index = i; +} + +/** + * Returns this MidpointLocator's index. This integer represents the position of the start + * point in this MidpointLocator's associated {@link Connection} from where midpoint + * calculation will be made. + * + * @return the locator's index + * @since 2.0 + */ + +protected int getIndex() { + return index; +} + +/** + * Returns the point of reference associated with this locator. This point will be midway + * between points at 'index' and 'index' + 1. + * + * @return the reference point + * @since 2.0 + */ +protected Point getReferencePoint() { + Connection conn = getConnection(); + Point p = Point.SINGLETON; + Point p1 = conn.getPoints().getPoint(getIndex()); + Point p2 = conn.getPoints().getPoint(getIndex() + 1); + conn.translateToAbsolute(p1); + conn.translateToAbsolute(p2); + p.x = (p2.x - p1.x) / 2 + p1.x; + p.y = (p2.y - p1.y) / 2 + p1.y; + return p; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/MouseEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/MouseEvent.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.MouseEvent; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import tango.text.convert.Format; +import dwtx.draw2d.InputEvent; +import dwtx.draw2d.EventDispatcher; +import dwtx.draw2d.IFigure; + +/** + * An event caused by the user interacting with the mouse. + */ +public class MouseEvent + : InputEvent +{ + +/** The X coordinate of the mouse event. */ +public int x; +/** The Y coordinate of the mouse event. */ +public int y; + +/** The button that was pressed or released: {1, 2, 3}. */ +public int button; + +this(int x, int y, EventDispatcher dispatcher, + IFigure f, int button, int stateMask) { + super(dispatcher, f, stateMask); + Point pt = Point.SINGLETON; + pt.setLocation(x, y); + f.translateToRelative(pt); + this.button = button; + this.x = pt.x; + this.y = pt.y; +} + +/** + * @return the location of this mouse event + */ +public Point getLocation() { + return new Point(x, y); +} + +/** + * @see Object#toString() + */ +public override String toString() { + return Format( "MouseEvent({},{}) to Figure: {}", x, y, source);//$NON-NLS-2$//$NON-NLS-1$ +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/MouseListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/MouseListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.MouseListener; + +import dwt.dwthelper.utils; +import dwtx.draw2d.MouseEvent; + +/** + * A listener interface for receiving mouse button events. + */ +public interface MouseListener { + +/** + * Called when a mouse button has been pressed while over the listened to object. + * @param me The MouseEvent object + */ +void mousePressed(MouseEvent me); + +/** + * Called when a pressed mouse button has been released. + * @param me The MouseEvent object + */ +void mouseReleased(MouseEvent me); + +/** + * Called when a mouse button has been double clicked over the listened to object. + * @param me The MouseEvent object + */ +void mouseDoubleClicked(MouseEvent me); + +/** + * An empty implementation of MouseListener for convenience. + */ +public class Stub + : MouseListener +{ + /** + * @see dwtx.draw2d.MouseListener#mousePressed(MouseEvent) + */ + public void mousePressed(MouseEvent me) { } + /** + * @see dwtx.draw2d.MouseListener#mouseReleased(MouseEvent) + */ + public void mouseReleased(MouseEvent me) { } + /** + * @see dwtx.draw2d.MouseListener#mouseDoubleClicked(MouseEvent) + */ + public void mouseDoubleClicked(MouseEvent me) { } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/MouseMotionListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/MouseMotionListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.MouseMotionListener; + +import dwt.dwthelper.utils; +import dwtx.draw2d.MouseEvent; +/** + * A listener interface for receiving mouse motion events. + */ +public interface MouseMotionListener { + +/** + * Called when the mouse has moved over the listened to object while a button was pressed. + * @param me The MouseEvent object + */ +void mouseDragged(MouseEvent me); + +/** + * Called when the mouse has entered the listened to object. + * @param me The MouseEvent object + */ +void mouseEntered(MouseEvent me); + +/** + * Called when the mouse has exited the listened to object. + * @param me The MouseEvent object + */ +void mouseExited(MouseEvent me); + +/** + * Called when the mouse hovers over the listened to object. + * @param me The MouseEvent object + */ +void mouseHover(MouseEvent me); + +/** + * Called when the mouse has moved over the listened to object. + * @param me The MouseEvent object + */ +void mouseMoved(MouseEvent me); + +/** + * An empty implementation of MouseMotionListener for convenience. + */ +static public class Stub + : MouseMotionListener +{ + /** + * @see dwtx.draw2d.MouseMotionListener#mouseDragged(MouseEvent) + */ + public void mouseDragged(MouseEvent me) { } + /** + * @see dwtx.draw2d.MouseMotionListener#mouseEntered(MouseEvent) + */ + public void mouseEntered(MouseEvent me) { } + /** + * @see dwtx.draw2d.MouseMotionListener#mouseExited(MouseEvent) + */ + public void mouseExited(MouseEvent me) { } + /** + * @see dwtx.draw2d.MouseMotionListener#mouseMoved(MouseEvent) + */ + public void mouseMoved(MouseEvent me) { } + /** + * @see dwtx.draw2d.MouseMotionListener#mouseHover(MouseEvent) + */ + public void mouseHover(MouseEvent me) { } +} + +} + + diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/NativeGraphicsSource.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/NativeGraphicsSource.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.NativeGraphicsSource; + +import dwt.dwthelper.utils; + + + +import dwt.widgets.Control; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.GraphicsSource; +import dwtx.draw2d.Graphics; + +/** + * A graphics source that posts a paint request to the control rather than constructing GC + * on it directly. This allows the OS's native painting mechanism to be used directly, + * including any double-buffering that the OS may provide for free. + * @since 3.2 + */ +public final class NativeGraphicsSource : GraphicsSource { + +private final Control canvas; + +/** + * Constructs a new graphics source on the given control. + * @param canvas the control + * @since 3.2 + */ +public this(Control canvas) { + this.canvas = canvas; +} + +/** + * Always returns null, because + * @see GraphicsSource#getGraphics(Rectangle) + */ +public Graphics getGraphics(Rectangle r) { + canvas.redraw(r.x, r.y, r.width, r.height, false); + canvas.update(); + return null; +} + +/** + * Does nothing. + * @see GraphicsSource#flushGraphics(Rectangle) + */ +public void flushGraphics(Rectangle region) { } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Orientable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Orientable.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Orientable; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.IFigure; +import dwtx.draw2d.PositionConstants; + +/** + * An interface for objects that can be either horizontally or vertically oriented. + */ +public interface Orientable + : IFigure, PositionConstants +{ + +/** + * A constant representing a horizontal orientation. + */ +static const int HORIZONTAL = 0; +/** + * A constant representing a vertical orientation. + */ +static const int VERTICAL = 1; + +/** + * Sets the orientation. Can be either {@link #HORIZONTAL} or {@link #VERTICAL}. + * @param orientation The orientation + */ +void setOrientation(int orientation); + +/** + * Sets the direction the orientable figure will face. Can be one of many directional + * constants defined in {@link PositionConstants}. + * @param direction The direction + */ +void setDirection(int direction); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Panel.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Panel.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Panel; + +import dwt.dwthelper.utils; +import dwtx.draw2d.Figure; + +/** + * A General purpose Container. This figure is opaque by default, and will fill its entire + * bounds with either the background color that is set on the figure, or the IGraphics + * current background color if none has been set. Opaque figures help to optimize + * painting. + *

+ * Note that the paintFigure() method in the superclass Figure actually fills the bounds + * of this figure. + */ +public class Panel + : Figure +{ + +/** + * Returns true as this is an opaque figure. + * + * @return the opaque state of this figure + * @since 2.0 + */ +public bool isOpaque() { + return true; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Polygon.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Polygon.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + * Alex Selkov - Fix for Bug# 22701 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.draw2d.Polygon; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.Polyline; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.IFigure; + +/** + * Renders a {@link dwtx.draw2d.geometry.PointList} as a polygonal shape. + * This class is similar to Polyline, except the PointList is closed and can be filled in + * as a solid shape. + * @see Polyline + */ +public class Polygon + : Polyline +{ + +/** + * Returns whether the point (x,y) is contained inside this polygon. + * @param x the X coordinate + * @param y the Y coordinate + * @return whether the point (x,y) is contained in this polygon + */ +public bool containsPoint(int x, int y) { + if (!getBounds().contains(x, y)) + return false; + + bool isOdd = false; + int[] pointsxy = getPoints().toIntArray(); + int n = pointsxy.length; + if (n > 3) { //If there are at least 2 Points (4 ints) + int x1, y1; + int x0 = pointsxy[n - 2]; + int y0 = pointsxy[n - 1]; + + for (int i = 0; i < n; x0 = x1, y0 = y1) { + x1 = pointsxy[i++]; + y1 = pointsxy[i++]; + + if (y0 <= y && y < y1 + && crossProduct(x1, y1, x0, y0, x, y) > 0) + isOdd = !isOdd; + if (y1 <= y && y < y0 + && crossProduct(x0, y0, x1, y1, x, y) > 0) + isOdd = !isOdd; + } + if (isOdd) + return true; + } + + List children = getChildren(); + for (int i = 0; i < children.size(); i++) + if ((cast(IFigure) children.get(i)).containsPoint(x, y)) + return true; + + return false; +} + +private int crossProduct(int ax, int ay, int bx, int by, int cx, int cy) { + return (ax - cx) * (by - cy) - (ay - cy) * (bx - cx); +} + +/** + * Fill the Polygon with the background color set by g. + * + * @param g the Graphics object + * @since 2.0 + */ +protected void fillShape(Graphics g) { + g.fillPolygon(getPoints()); +} + +/** + * Draw the outline of the Polygon. + * + * @param g the Graphics object + * @since 2.0 + */ +protected void outlineShape(Graphics g) { + g.drawPolygon(getPoints()); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/PolygonDecoration.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/PolygonDecoration.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.PolygonDecoration; + +import dwt.dwthelper.utils; + + + +import dwt.graphics.Color; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.Transform; +import dwtx.draw2d.RotatableDecoration; +import dwtx.draw2d.Polygon; + +/** + * A rotatable, polygon shaped decoration most commonly used for decorating the ends of + * {@link dwtx.draw2d.Polyline polylines}. + */ +public class PolygonDecoration + : Polygon + , RotatableDecoration +{ + +/** Template for a triangle that points to the right when the rotation angle is 0 */ +public static const PointList TRIANGLE_TIP; +/** Template for a triangle that points to the left when the rotation angle is 0 */ +public static const PointList INVERTED_TRIANGLE_TIP; + +static this() { + TRIANGLE_TIP = new PointList(); + INVERTED_TRIANGLE_TIP = new PointList(); + TRIANGLE_TIP.addPoint(0, 0); + TRIANGLE_TIP.addPoint(-1, 1); + TRIANGLE_TIP.addPoint(-1, -1); + + INVERTED_TRIANGLE_TIP.addPoint(0, 1); + INVERTED_TRIANGLE_TIP.addPoint(0, -1); + INVERTED_TRIANGLE_TIP.addPoint(-1, 0); +} + +private Point location; +private PointList template_; +private Transform transform; + +/** + * Constructs a PolygonDecoration. Defaults the PolygonDecoration to fill its region + * with black. + * + * @since 2.0 + */ +public this() { + location = new Point(); + template_ = TRIANGLE_TIP; + transform = new Transform(); + setFill(true); + setScale(7, 3); +} + +/** + * @see dwtx.draw2d.IFigure#getBackgroundColor() + */ +public Color getLocalBackgroundColor() { + if (super.getLocalBackgroundColor() is null) + return getForegroundColor(); + return super.getLocalBackgroundColor(); +} + +/** + * Returns the points in the PolygonDecoration as a PointList. + * + * @return the points in this PolygonDecoration + * @since 2.0 + */ +public PointList getPoints() { + if (points is null) { + points = new PointList(); + for (int i = 0; i < template_.size(); i++) + points.addPoint(transform.getTransformed(template_.getPoint(i))); + } + return points; +} + +/** + * Sets the location of this PolygonDecoration. + * @param p the new location + */ +public void setLocation(Point p) { + points = null; + bounds = null; + location.setLocation(p); + transform.setTranslation(p.x, p.y); +} + +/** + * Sets the PolygonDecorations point template_ to the passed PointList. This template_ is an + * outline of the PolygonDecoration's region. (The default value is TRIANGLE_TIP which is + * a triangle whose tip is pointing to the right). + * + * @param pl the PointList outline to use as the PolygonDecoration's region + * @since 2.0 + */ +public void setTemplate(PointList pl) { + erase(); + template_ = pl; + points = null; + bounds = null; + repaint(); +} + +/** + * Sets the amount of scaling to be done along X and Y axes on the PolygonDecoration's + * template_. + * + * @param x X scaling + * @param y Y scaling + * @since 2.0 + */ +public void setScale(double x, double y) { + points = null; + bounds = null; + transform.setScale(x, y); +} + +/** + * Sets the rotation of this decoration so that the decoration points toward the + * given reference point. + * @param ref the reference point + */ +public void setReferencePoint(Point ref_) { + Point pt = Point.SINGLETON; + pt.setLocation(ref_); + pt.negate().translate(location); + setRotation(Math.atan2(pt.y, pt.x)); +} + +/** + * Sets the angle by which rotation is to be done on the PolygonDecoration. + * + * @param angle Angle of rotation + * @since 2.0 + */ +public void setRotation(double angle) { + points = null; + bounds = null; + transform.setRotation(angle); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Polyline.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Polyline.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,317 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +module dwtx.draw2d.Polyline; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Shape; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Connection; + +/** + * Renders a {@link PointList} as a series of line segments. A Polyline figure should be + * positioned by manipulating its points, NOT by calling + * {@link Figure#setBounds(Rectangle)}. + *

+ * A polyline's bounds will be calculated automatically based on its PointList. The + * bounds will be the smallest Rectangle large enough to render the line properly. + * Children should not be added to a Polyline and will not affect the bounds calculation. + */ +public class Polyline + : Shape +{ + +PointList points; +private int tolerance = 2; +private static const Rectangle LINEBOUNDS; + +static this(){ + LINEBOUNDS = Rectangle.SINGLETON; + assert( LINEBOUNDS !is null ); +} + +this(){ + points = new PointList(); + setFill(false); + bounds = null; +} + +/** + * Adds the passed point to the Polyline. + * + * @param pt the Point to be added to the Polyline + * @since 2.0 + */ +public void addPoint(Point pt) { + points.addPoint(pt); + bounds = null; + repaint(); +} + +/** + * @see dwtx.draw2d.IFigure#containsPoint(int, int) + */ +public bool containsPoint(int x, int y) { + int tolerance = Math.max(lineWidth / 2, this.tolerance); + LINEBOUNDS.setBounds(getBounds()); + LINEBOUNDS.expand(tolerance, tolerance); + if (!LINEBOUNDS.contains(x, y)) + return false; + int ints[] = points.toIntArray(); + for (int index = 0; index < ints.length - 3; index += 2) { + if (lineContainsPoint(ints[index], ints[index + 1], + ints[index + 2], ints[index + 3], x, y, tolerance)) + return true; + } + List children = getChildren(); + for (int i = 0; i < children.size(); i++) { + if ((cast(IFigure)children.get(i)).containsPoint(x, y)) + return true; + } + return false; +} + +private bool lineContainsPoint( + int x1, int y1, + int x2, int y2, + int px, int py, + int tolerance) { + LINEBOUNDS.setSize(0, 0); + LINEBOUNDS.setLocation(x1, y1); + LINEBOUNDS.union_(x2, y2); + LINEBOUNDS.expand(tolerance, tolerance); + if (!LINEBOUNDS.contains(px, py)) + return false; + + int v1x, v1y, v2x, v2y; + int numerator, denominator; + int result = 0; + + /** + * calculates the length squared of the cross product of two vectors, v1 & v2. + */ + if (x1 !is x2 && y1 !is y2) { + v1x = x2 - x1; + v1y = y2 - y1; + v2x = px - x1; + v2y = py - y1; + + numerator = v2x * v1y - v1x * v2y; + + denominator = v1x * v1x + v1y * v1y; + + result = cast(int)(cast(long)numerator * numerator / denominator); + } + + // if it is the same point, and it passes the bounding box test, + // the result is always true. + return result <= tolerance * tolerance; + +} + +/** + * Null implementation for a line. + * @see dwtx.draw2d.Shape#fillShape(Graphics) + */ +protected void fillShape(Graphics g) { } + +/** + * @see dwtx.draw2d.IFigure#getBounds() + */ +public Rectangle getBounds() { + if (bounds is null) { + bounds = getPoints() + .getBounds() + .getExpanded(lineWidth / 2, lineWidth / 2); + } + return bounds; +} + +/** + * Returns the last point in the Polyline. + * @since 2.0 + * @return the last point + */ +public Point getEnd() { + return points.getLastPoint(); +} + +/** + * Returns the points in this Polyline by reference. If the returned list is + * modified, this Polyline must be informed by calling {@link #setPoints(PointList)}. + * Failure to do so will result in layout and paint problems. + * + * @return this Polyline's points + * @since 2.0 + */ +public PointList getPoints() { + return points; +} + +/** + * @return the first point in the Polyline + * @since 2.0 + */ +public Point getStart() { + return points.getFirstPoint(); +} + +/** + * Inserts a given point at a specified index in the Polyline. + * + * @param pt the point to be added + * @param index the position in the Polyline where the point is to be added + * + * @since 2.0 + */ +public void insertPoint(Point pt, int index) { + bounds = null; + points.insertPoint(pt, index); + repaint(); +} + +/** + * @return false because Polyline's aren't filled + */ +public bool isOpaque() { + return false; +} + +/** + * @see Shape#outlineShape(Graphics) + */ +protected void outlineShape(Graphics g) { + g.drawPolyline(points); +} + +/** + * @see Figure#primTranslate(int, int) + */ +public void primTranslate(int x, int y) { } + +/** + * Erases the Polyline and removes all of its {@link Point Points}. + * + * @since 2.0 + */ +public void removeAllPoints() { + erase(); + bounds = null; + points.removeAllPoints(); +} + +/** + * Removes a point from the Polyline. + * + * @param index the position of the point to be removed + * @since 2.0 + */ +public void removePoint(int index) { + erase(); + bounds = null; + points.removePoint(index); +} + +/** + * Sets the end point of the Polyline + * + * @param end the point that will become the last point in the Polyline + * @since 2.0 + */ +public void setEnd(Point end) { + if (points.size() < 2) + addPoint(end); + else + setPoint(end, points.size() - 1); +} + +/** + * Sets the points at both extremes of the Polyline + * + * @param start the point to become the first point in the Polyline + * @param end the point to become the last point in the Polyline + * @since 2.0 + */ +public void setEndpoints(Point start, Point end) { + setStart(start); + setEnd(end); +} + +/** + * @see dwtx.draw2d.Shape#setLineWidth(int) + */ +public void setLineWidth(int w) { + if (lineWidth is w) + return; + if (w < lineWidth) //The bounds will become smaller, so erase must occur first. + erase(); + bounds = null; + super.setLineWidth(w); +} + +/** + * Sets the point at index to the Point pt. Calling this method + * results in a recalculation of the polyline's bounding box. If you're going to set + * multiple Points, use {@link #setPoints(PointList)}. + * @param pt the point + * @param index the index + */ +public void setPoint(Point pt, int index) { + erase(); + points.setPoint(pt, index); + bounds = null; + repaint(); +} + +/** + * Sets the list of points to be used by this polyline connection. Removes any previously + * existing points. The polyline will hold onto the given list by reference. + * + * @param points new set of points + * @since 2.0 + */ +public void setPoints(PointList points) { + erase(); + this.points = points; + bounds = null; + firePropertyChange(Connection.PROPERTY_POINTS, null, points); + repaint(); +} + +/** + * Sets the start point of the Polyline + * + * @param start the point that will become the first point in the Polyline + * @since 2.0 + */ +public void setStart(Point start) { + if (points.size() is 0) + addPoint(start); + else + setPoint(start, 0); +} + +/** + * Sets the tolerance + * + * @param tolerance the new tolerance value of the Polyline + */ +public void setTolerance(int tolerance) { + this.tolerance = tolerance; +} +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/PolylineConnection.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/PolylineConnection.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,395 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.PolylineConnection; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.Polyline; +import dwtx.draw2d.Connection; +import dwtx.draw2d.ConnectionAnchor; +import dwtx.draw2d.AnchorListener; +import dwtx.draw2d.ConnectionRouter; +import dwtx.draw2d.ConnectionLocator; +import dwtx.draw2d.RotatableDecoration; +import dwtx.draw2d.RoutingListener; +import dwtx.draw2d.DelegatingLayout; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.ArrowLocator; + +/** + * An implementation of {@link Connection} based on Polyline. PolylineConnection adds + * the following additional features: + *

    + *
  • + * A {@link ConnectionRouter} may be provided which will be used to determine the + * connections points. + *
  • + * Children may be added. The bounds calculation is extended such that the bounds is + * the smallest Rectangle which is large enough to display the Polyline and all of its + * children figures. + *
  • + * A {@link DelegatingLayout} is set as the default layout. A delegating layout allows + * children to position themselves via {@link Locator Locators}. + *
+ *

+ */ +public class PolylineConnection + : Polyline + , Connection, AnchorListener +{ + +// reimplement for Connection +PointList getPoints(){ + return super.getPoints(); +} +// reimplement for Connection +void setPoints(PointList list){ + super.setPoints(list); +} + + +private ConnectionAnchor startAnchor, endAnchor; +private ConnectionRouter connectionRouter; +private RotatableDecoration startArrow, endArrow; + +private void instanceInit(){ + connectionRouter = ConnectionRouter_NULL; + setLayoutManager(new DelegatingLayout()); + addPoint(new Point(0, 0)); + addPoint(new Point(100, 100)); +} + +this(){ + instanceInit(); +} +/** + * Hooks the source and target anchors. + * @see Figure#addNotify() + */ +public void addNotify() { + super.addNotify(); + hookSourceAnchor(); + hookTargetAnchor(); +} + +/** + * Appends the given routing listener to the list of listeners. + * @param listener the routing listener + * @since 3.2 + */ +public void addRoutingListener(RoutingListener listener) { + if (auto notifier = cast(RoutingNotifier)connectionRouter ) { + notifier.listeners.add(cast(Object)listener); + } else + connectionRouter = new RoutingNotifier(connectionRouter, listener); +} + +/** + * Called by the anchors of this connection when they have moved, revalidating this + * polyline connection. + * @param anchor the anchor that moved + */ +public void anchorMoved(ConnectionAnchor anchor) { + revalidate(); +} + +/** + * Returns the bounds which holds all the points in this polyline connection. Returns any + * previously existing bounds, else calculates by unioning all the children's + * dimensions. + * @return the bounds + */ +public Rectangle getBounds() { + if (bounds is null) { + super.getBounds(); + for (int i = 0; i < getChildren().size(); i++) { + IFigure child = cast(IFigure)getChildren().get(i); + bounds.union_(child.getBounds()); + } + } + return bounds; +} + +/** + * Returns the ConnectionRouter used to layout this connection. Will not + * return null. + * @return this connection's router + */ +public ConnectionRouter getConnectionRouter() { + if (auto n = cast(RoutingNotifier)connectionRouter ) + return n.realRouter; + return connectionRouter; +} + +/** + * Returns this connection's routing constraint from its connection router. May return + * null. + * @return the connection's routing constraint + */ +public Object getRoutingConstraint() { + if (getConnectionRouter() !is null) + return getConnectionRouter().getConstraint(this); + else + return null; +} + +/** + * @return the anchor at the start of this polyline connection (may be null) + */ +public ConnectionAnchor getSourceAnchor() { + return startAnchor; +} + +/** + * @return the source decoration (may be null) + */ +protected RotatableDecoration getSourceDecoration() { + return startArrow; +} + +/** + * @return the anchor at the end of this polyline connection (may be null) + */ +public ConnectionAnchor getTargetAnchor() { + return endAnchor; +} + +/** + * @return the target decoration (may be null) + * + * @since 2.0 + */ +protected RotatableDecoration getTargetDecoration() { + return endArrow; +} + +private void hookSourceAnchor() { + if (getSourceAnchor() !is null) + getSourceAnchor().addAnchorListener(this); +} + +private void hookTargetAnchor() { + if (getTargetAnchor() !is null) + getTargetAnchor().addAnchorListener(this); +} + +/** + * Layouts this polyline. If the start and end anchors are present, the connection router + * is used to route this, after which it is laid out. It also fires a moved method. + */ +public void layout() { + if (getSourceAnchor() !is null && getTargetAnchor() !is null) + connectionRouter.route(this); + + Rectangle oldBounds = bounds; + super.layout(); + bounds = null; + + if (!getBounds().contains(oldBounds)) { + getParent().translateToParent(oldBounds); + getUpdateManager().addDirtyRegion(getParent(), oldBounds); + } + + repaint(); + fireFigureMoved(); +} + +/** + * Called just before the receiver is being removed from its parent. Results in removing + * itself from the connection router. + * + * @since 2.0 + */ +public void removeNotify() { + unhookSourceAnchor(); + unhookTargetAnchor(); + connectionRouter.remove(this); + super.removeNotify(); +} + +/** + * Removes the first occurence of the given listener. + * @param listener the listener being removed + * @since 3.2 + */ +public void removeRoutingListener(RoutingListener listener) { + if ( auto notifier = cast(RoutingNotifier)connectionRouter ) { + notifier.listeners.remove(cast(Object)listener); + if (notifier.listeners.isEmpty()) + connectionRouter = notifier.realRouter; + } +} + +/** + * @see IFigure#revalidate() + */ +public void revalidate() { + super.revalidate(); + connectionRouter.invalidate(this); +} + +/** + * Sets the connection router which handles the layout of this polyline. Generally set by + * the parent handling the polyline connection. + * @param cr the connection router + */ +public void setConnectionRouter(ConnectionRouter cr) { + if (cr is null) + cr = ConnectionRouter_NULL; + ConnectionRouter oldRouter = getConnectionRouter(); + if (oldRouter !is cr) { + connectionRouter.remove(this); + if (auto n = cast(RoutingNotifier)connectionRouter ) + n.realRouter = cr; + else + connectionRouter = cr; + firePropertyChange(Connection.PROPERTY_CONNECTION_ROUTER, cast(Object)oldRouter, cast(Object)cr); + revalidate(); + } +} + +/** + * Sets the routing constraint for this connection. + * @param cons the constraint + */ +public void setRoutingConstraint(Object cons) { + if (connectionRouter !is null) + connectionRouter.setConstraint(this, cons); + revalidate(); +} + +/** + * Sets the anchor to be used at the start of this polyline connection. + * @param anchor the new source anchor + */ +public void setSourceAnchor(ConnectionAnchor anchor) { + if (anchor is startAnchor) + return; + unhookSourceAnchor(); + //No longer needed, revalidate does this. + //getConnectionRouter().invalidate(this); + startAnchor = anchor; + if (getParent() !is null) + hookSourceAnchor(); + revalidate(); +} + +/** + * Sets the decoration to be used at the start of the {@link Connection}. + * @param dec the new source decoration + * @since 2.0 + */ +public void setSourceDecoration(RotatableDecoration dec) { + if (startArrow is dec) + return; + if (startArrow !is null) + remove(startArrow); + startArrow = dec; + if (startArrow !is null) + add(startArrow, new ArrowLocator(this, ConnectionLocator.SOURCE)); +} + +/** + * Sets the anchor to be used at the end of the polyline connection. Removes this listener + * from the old anchor and adds it to the new anchor. + * @param anchor the new target anchor + */ +public void setTargetAnchor(ConnectionAnchor anchor) { + if (anchor is endAnchor) + return; + unhookTargetAnchor(); + //No longer needed, revalidate does this. + //getConnectionRouter().invalidate(this); + endAnchor = anchor; + if (getParent() !is null) + hookTargetAnchor(); + revalidate(); +} + +/** + * Sets the decoration to be used at the end of the {@link Connection}. + * @param dec the new target decoration + */ +public void setTargetDecoration(RotatableDecoration dec) { + if (endArrow is dec) + return; + if (endArrow !is null) + remove(endArrow); + endArrow = dec; + if (endArrow !is null) + add(endArrow, new ArrowLocator(this, ConnectionLocator.TARGET)); +} + +private void unhookSourceAnchor() { + if (getSourceAnchor() !is null) + getSourceAnchor().removeAnchorListener(this); +} + +private void unhookTargetAnchor() { + if (getTargetAnchor() !is null) + getTargetAnchor().removeAnchorListener(this); +} + +final class RoutingNotifier : ConnectionRouter { + + ConnectionRouter realRouter; + List listeners; + + this(ConnectionRouter router, RoutingListener listener) { + listeners = new ArrayList(1); + realRouter = router; + listeners.add(cast(Object)listener); + } + + public Object getConstraint(Connection connection) { + return realRouter.getConstraint(connection); + } + + public void invalidate(Connection connection) { + for (int i = 0; i < listeners.size(); i++) + (cast(RoutingListener)listeners.get(i)).invalidate(connection); + + realRouter.invalidate(connection); + } + + public void route(Connection connection) { + bool consumed = false; + for (int i = 0; i < listeners.size(); i++) + consumed |= (cast(RoutingListener)listeners.get(i)).route(connection); + + if (!consumed) + realRouter.route(connection); + + for (int i = 0; i < listeners.size(); i++) + (cast(RoutingListener)listeners.get(i)).postRoute(connection); + } + + public void remove(Connection connection) { + for (int i = 0; i < listeners.size(); i++) + (cast(RoutingListener)listeners.get(i)).remove(connection); + realRouter.remove(connection); + } + + public void setConstraint(Connection connection, Object constraint) { + for (int i = 0; i < listeners.size(); i++) + (cast(RoutingListener)listeners.get(i)).setConstraint(connection, constraint); + realRouter.setConstraint(connection, constraint); + } + +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/PolylineDecoration.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/PolylineDecoration.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.PolylineDecoration; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.Transform; +import dwtx.draw2d.Polyline; +import dwtx.draw2d.RotatableDecoration; +import dwtx.draw2d.ColorConstants; + +/** + * A decorative Figure intended to be placed on a {@link Polyline}. It has the default + * shape of right-pointing triangle. + */ +public class PolylineDecoration + : Polyline + , RotatableDecoration +{ + +/** A triangle template_ */ +public static const PointList TRIANGLE_TIP; + +static this() { + TRIANGLE_TIP = new PointList(); + TRIANGLE_TIP.addPoint(-1, 1); + TRIANGLE_TIP.addPoint(0, 0); + TRIANGLE_TIP.addPoint(-1, -1); +} + +private Point location; +private PointList template_; +private Transform transform; + +/** + * Constructs a PolylineDecoration. Defaults the PolylineDecoration to fill its region + * with black. + * + * @since 2.0 + */ +public this() { + location = new Point(); + template_ = TRIANGLE_TIP; + transform = new Transform(); + setBackgroundColor(ColorConstants.black); + setScale(7, 3); +} + +/** + * @see Polyline#getPoints() + */ +public PointList getPoints() { + if (points is null) { + points = new PointList(); + for (int i = 0; i < template_.size(); i++) + points.addPoint(transform.getTransformed(template_.getPoint(i))); + } + return points; +} + +/** + * @see IFigure#setLocation(Point) + */ +public void setLocation(Point p) { + points = null; + bounds = null; + location.setLocation(p); + transform.setTranslation(p.x, p.y); +} + +/** + * Sets the PolylineDecoration's point template_. This template_ is an outline of the + * PolylineDecoration's region. (The default value is TRIANGLE_TIP which is a triangle + * whose tip is pointing to the right). + * + * @param pl the template_ + * @since 2.0 + */ +public void setTemplate(PointList pl) { + erase(); + template_ = pl; + points = null; + bounds = null; + repaint(); +} + +/** + * Sets the amount of scaling to be done along X and Y axes on the PolylineDecoration's + * template_. + * + * @param x the x scale + * @param y the y scale + * @since 2.0 + */ +public void setScale(double x, double y) { + points = null; + bounds = null; + transform.setScale(x, y); +} + +/** + * @see RotatableDecoration#setReferencePoint(Point) + */ +public void setReferencePoint(Point ref_) { + Point pt = Point.SINGLETON; + pt.setLocation(ref_); + pt.negate().translate(location); + setRotation(Math.atan2(pt.y, pt.x)); +} + +/** + * Sets the angle by which rotation is to be done on the PolylineDecoration. + * + * @param angle the angle of rotation + * @since 2.0 + */ +public void setRotation(double angle) { + points = null; + bounds = null; + transform.setRotation(angle); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/PopUpHelper.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/PopUpHelper.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,216 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.PopUpHelper; + +import dwt.dwthelper.utils; + + + +import dwt.DWT; +import dwt.graphics.Color; +import dwt.graphics.Rectangle; +import dwt.widgets.Control; +import dwt.widgets.Shell; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.LightweightSystem; + +/** + * Provides abstract support for classes that manage popups. Popups in Draw2d consist of a + * LightweightSystem object with an DWT shell as its Control. Desired popup behavior is + * attained by adding appropriate listeners to this shell. + */ +public abstract class PopUpHelper { + +private Shell shell; +private LightweightSystem lws; +private bool tipShowing; +/** + * The Control this PopUpHelper's tooltip will belong to. + */ +protected Control control; + +/** + * These style bits should be used when creating the Shell. + * @see #createShell() + */ +protected const int shellStyle; + +/** + * Constructs a PopUpHelper to assist with popups on Control c. + * + * @param c the Control + * @since 2.0 + */ +protected this(Control c) { + this (c, DWT.ON_TOP | DWT.NO_TRIM); +} + +/** + * Constructs a PopUpHelper to display the given shell style popup. + * @param c the control on which the popup is active. + * @param shellStyle the DWT style bits for the shell + * @since 3.1 + */ +protected this(Control c, int shellStyle) { + control = c; + this.shellStyle = shellStyle | DWT.NO_BACKGROUND | DWT.NO_REDRAW_RESIZE; +} + +/** + * Creates and returns the LightweightSystem object used by PopUpHelper to draw upon. + * + * @return the newly created LightweightSystem + * @since 2.0 + */ +protected LightweightSystem createLightweightSystem() { + return new LightweightSystem(); +} + +/** + * Creates a new Shell object with the style specified for this helper. + * + * @return the newly created Shell + * @since 2.0 + */ +protected Shell createShell() { + return new Shell(control.getShell(), shellStyle); +} + +/** + * Dispose of this PopUpHelper object. + * + * @since 2.0 + */ +public void dispose() { + if (isShowing()) + hide(); + if (shell !is null && !shell.isDisposed()) + shell.dispose(); +} + +/** + * Returns this PopUpHelper's shell. If no shell exists for this PopUpHelper, a new shell + * is created and hookShellListeners() is called. + * + * @return the Shell + * @since 2.0 + */ +protected Shell getShell() { + if (shell is null) { + shell = createShell(); + hookShellListeners(); + } + return shell; +} + +/** + * Returns the size needed to display the shell's trim. This method should not be called + * until the shell has been created. + * @return the size of the shells trim. + * @since 3.1 + */ +protected Dimension getShellTrimSize() { + Rectangle trim = shell.computeTrim(0, 0, 0, 0); + return new Dimension(trim.width, trim.height); +} + +/** + * Returns this PopUpHelper's LightweightSystem. If no LightweightSystem exists for this + * PopUpHelper, a new LightweightSystem is created with this PopUpHelper's Shell as its + * Control. + * + * @return the LightweightSystem + * @since 2.0 + */ +protected LightweightSystem getLightweightSystem() { + if (lws is null) { + lws = createLightweightSystem(); + lws.setControl(getShell()); + } + return lws; +} + +/** + * Hides this PopUpHelper's Shell. + * + * @since 2.0 + */ +protected void hide() { + if (shell !is null && !shell.isDisposed()) + shell.setVisible(false); + tipShowing = false; +} + +/** + * Desired popup helper behavior is achieved by writing listeners that manipulate the + * behavior of the PopUpHelper's Shell. Override this method and add these listeners here. + * + * @since 2.0 + */ +protected abstract void hookShellListeners(); + +/** + * Returns true if this PopUpHelper's Shell is visible, false + * otherwise. + * + * @return true if this PopUpHelper's Shell is visible + * @since 2.0 + */ +public bool isShowing() { + return tipShowing; +} + +/** + * Sets the background color of this PopUpHelper's Shell. + * + * @param c the new background color + * @since 2.0 + */ +public void setBackgroundColor(Color c) { + getShell().setBackground(c); +} + +/** + * Sets the foreground color of this PopUpHelper's Shell. + * + * @param c the new foreground color + * @since 2.0 + */ +public void setForegroundColor(Color c) { + getShell().setForeground(c); +} + +/** + * Sets the bounds on this PopUpHelper's Shell. + * + * @param x the x coordinate + * @param y the y coordinate + * @param width the width + * @param height the height + * @since 2.0 + */ +protected void setShellBounds(int x, int y, int width, int height) { + getShell().setBounds(x, y, width, height); +} + +/** + * Displays this PopUpHelper's Shell. + * + * @since 2.0 + */ +protected void show() { + getShell().setVisible(true); + tipShowing = true; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/PositionConstants.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/PositionConstants.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.PositionConstants; + +import dwt.dwthelper.utils; + +/** + * Constants representing cardinal directions and relative positions. Some of these + * constants can be grouped as follows: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
LEFT, CENTER, RIGHTUsed to describe horizontal position.
TOP, MIDDLE, BOTTOMUsed to describe vertical position.
NORTH, SOUTH, EAST, WESTUsed to describe the four positions relative to an object's center point. + * May also be used when describing which direction an object is facing.
+ * NOTE: If you have a use for all four of these possibilities, do not use TOP, + * BOTTOM, RIGHT, LEFT in place of NORTH, SOUTH, EAST, WEST.
+ */ +public interface PositionConstants { + +/** None */ +static const int NONE = 0; + +/** Left */ +static const int LEFT = 1; +/** Center (Horizontal) */ +static const int CENTER = 2; +/** Right */ +static const int RIGHT = 4; +/** Bit-wise OR of LEFT, CENTER, and RIGHT */ +static const int LEFT_CENTER_RIGHT = LEFT | CENTER | RIGHT; +/** Used to signify left alignment regardless of orientation (i.e., LTR or RTL) */ +static const int ALWAYS_LEFT = 64; +/** Used to signify right alignment regardless of orientation (i.e., LTR or RTL) */ +static const int ALWAYS_RIGHT = 128; + +/** Top */ +static const int TOP = 8; +/** Middle (Vertical) */ +static const int MIDDLE = 16; +/** Bottom */ +static const int BOTTOM = 32; +/** Bit-wise OR of TOP, MIDDLE, and BOTTOM */ +static const int TOP_MIDDLE_BOTTOM = TOP | MIDDLE | BOTTOM; + +/** North */ +static const int NORTH = 1; +/** South */ +static const int SOUTH = 4; +/** West */ +static const int WEST = 8; +/** East */ +static const int EAST = 16; + +/** A constant indicating horizontal direction */ +static const int HORIZONTAL = 64; +/** A constant indicating vertical direction */ +static const int VERTICAL = 128; + +/** North-East: a bit-wise OR of {@link #NORTH} and {@link #EAST} */ +static const int NORTH_EAST = NORTH | EAST; +/** North-West: a bit-wise OR of {@link #NORTH} and {@link #WEST} */ +static const int NORTH_WEST = NORTH | WEST; +/** South-East: a bit-wise OR of {@link #SOUTH} and {@link #EAST} */ +static const int SOUTH_EAST = SOUTH | EAST; +/** South-West: a bit-wise OR of {@link #SOUTH} and {@link #WEST} */ +static const int SOUTH_WEST = SOUTH | WEST; +/** North-South: a bit-wise OR of {@link #NORTH} and {@link #SOUTH} */ +static const int NORTH_SOUTH = NORTH | SOUTH; +/** East-West: a bit-wise OR of {@link #EAST} and {@link #WEST} */ +static const int EAST_WEST = EAST | WEST; + +/** North-South-East-West: a bit-wise OR of all 4 directions. */ +static const int NSEW = NORTH_SOUTH | EAST_WEST; + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/PrintFigureOperation.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/PrintFigureOperation.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + * Sven M�ller - Added tiling support + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.draw2d.PrintFigureOperation; + +import dwt.dwthelper.utils; + + + +import dwt.DWT; +import dwt.graphics.Color; +import dwt.printing.Printer; +import dwt.widgets.Display; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.PrintOperation; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.ColorConstants; + +/** + * Class responsible for printing Figures. + * + * @author Dan Lee + * @author Eric Bordeau + * @author Sven M�ller + */ +public class PrintFigureOperation : PrintOperation { + +/** + * The default print mode. Prints at 100% scale and tiles horizontally and/or vertically, + * if necessary. + */ +public static const int TILE = 1; +/** + * A print mode that scales the printer graphics so that the entire printed image fits on + * one page. + */ +public static const int FIT_PAGE = 2; +/** + * A print mode that scales the printer graphics so that the width of the printed image + * fits on one page and tiles vertically, if necessary. + */ +public static const int FIT_WIDTH = 3; +/** + * A print mode that scales the printer graphics so that the height of the printed image + * fits on one page and tiles horizontally, if necessary. + */ +public static const int FIT_HEIGHT = 4; + +private IFigure printSource; +private Color oldBGColor; +private int printMode = TILE; + +/** + * Constructor for PrintFigureOperation. + *

+ * Note: Descendants must call setPrintSource(IFigure) to set the IFigure that is to be + * printed. + * @see dwtx.draw2d.PrintOperation#PrintOperation(Printer) + */ +protected this(Printer p) { + super(p); +} + +/** + * Constructor for PrintFigureOperation. + * + * @param p Printer to print on + * @param srcFigure Figure to print + */ +public this(Printer p, IFigure srcFigure) { + super(p); + setPrintSource(srcFigure); +} + +/** + * @return DWT.RIGHT_TO_LEFT if the print source is mirrored; DWT.LEFT_TO_RIGHT otherwise + * @see dwtx.draw2d.PrintOperation#getGraphicsOrientation() + */ +int getGraphicsOrientation() { + return getPrintSource().isMirrored() ? DWT.RIGHT_TO_LEFT : DWT.LEFT_TO_RIGHT; +} + +/** + * Returns the current print mode. The print mode is one of: {@link #FIT_HEIGHT}, + * {@link #FIT_PAGE}, or {@link #FIT_WIDTH}. + * @return the print mode + */ +protected int getPrintMode() { + return printMode; +} + +/** + * Returns the printSource. + * + * @return IFigure The source IFigure + */ +protected IFigure getPrintSource() { + return printSource; +} + +/** + * @see dwtx.draw2d.PrintOperation#preparePrintSource() + */ +protected void preparePrintSource() { + oldBGColor = getPrintSource().getLocalBackgroundColor(); + getPrintSource().setBackgroundColor(ColorConstants.white); +} + +/** + * Prints the pages based on the current print mode. + * @see dwtx.draw2d.PrintOperation#printPages() + */ +protected void printPages() { + Graphics graphics = getFreshPrinterGraphics(); + IFigure figure = getPrintSource(); + setupPrinterGraphicsFor(graphics, figure); + Rectangle bounds = figure.getBounds(); + int x = bounds.x, y = bounds.y; + Rectangle clipRect = new Rectangle(); + while (y < bounds.y + bounds.height) { + while (x < bounds.x + bounds.width) { + graphics.pushState(); + getPrinter().startPage(); + graphics.translate(-x, -y); + graphics.getClip(clipRect); + clipRect.setLocation(x, y); + graphics.clipRect(clipRect); + figure.paint(graphics); + getPrinter().endPage(); + graphics.popState(); + x += clipRect.width; + } + x = bounds.x; + y += clipRect.height; + } +} + +/** + * @see dwtx.draw2d.PrintOperation#restorePrintSource() + */ +protected void restorePrintSource() { + getPrintSource().setBackgroundColor(oldBGColor); + oldBGColor = null; +} + +/** + * Sets the print mode. Possible values are {@link #TILE}, {@link #FIT_HEIGHT}, + * {@link #FIT_WIDTH} and {@link #FIT_PAGE}. + * @param mode the print mode + */ +public void setPrintMode(int mode) { + printMode = mode; +} + +/** + * Sets the printSource. + * @param printSource The printSource to set + */ +protected void setPrintSource(IFigure printSource) { + this.printSource = printSource; +} + +/** + * Sets up Graphics object for the given IFigure. + * @param graphics The Graphics to setup + * @param figure The IFigure used to setup graphics + */ +protected void setupPrinterGraphicsFor(Graphics graphics, IFigure figure) { + double dpiScale = cast(double)getPrinter().getDPI().x / Display.getCurrent().getDPI().x; + + Rectangle printRegion = getPrintRegion(); + // put the print region in display coordinates + printRegion.width /= dpiScale; + printRegion.height /= dpiScale; + + Rectangle bounds = figure.getBounds(); + double xScale = cast(double)printRegion.width / bounds.width; + double yScale = cast(double)printRegion.height / bounds.height; + switch (getPrintMode()) { + case FIT_PAGE: + graphics.scale(Math.min(xScale, yScale) * dpiScale); + break; + case FIT_WIDTH: + graphics.scale(xScale * dpiScale); + break; + case FIT_HEIGHT: + graphics.scale(yScale * dpiScale); + break; + default: + graphics.scale(dpiScale); + } + graphics.setForegroundColor(figure.getForegroundColor()); + graphics.setBackgroundColor(figure.getBackgroundColor()); + graphics.setFont(figure.getFont()); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/PrintOperation.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/PrintOperation.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,195 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.PrintOperation; + +import dwt.dwthelper.utils; + +static import dwt.graphics.Rectangle; +static import dwt.graphics.Point; + +import dwt.DWT; +import dwt.graphics.GC; +import dwt.printing.Printer; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.PrinterGraphics; +import dwtx.draw2d.SWTGraphics; + + +/** + * Implementation of draw2d's printing capabilities. + * + * @author Dan Lee + * @author Eric Bordeau + */ +public abstract class PrintOperation { + +private GC printerGC; // Note: Only one GC instance should be created per print job +private Insets printMargin; +private Printer printer; +private PrinterGraphics printerGraphics; +private SWTGraphics g; + +/** + * Creates a new PrintOperation + */ +public this() { + printMargin = new Insets(0, 0, 0, 0); +} + +/** + * Creates a new PrintOperation on Printer p + * @param p The printer to print on + */ +public this(Printer p) { + this(); + setPrinter(p); +} + +/** + * Disposes the PrinterGraphics and GC objects associated with this PrintOperation. + */ +protected void cleanup() { + if (g !is null) { + printerGraphics.dispose(); + g.dispose(); + } + if (printerGC !is null) + printerGC.dispose(); +} + +/** + * Returns a new PrinterGraphics setup for the Printer associated with this + * PrintOperation. + * + * @return PrinterGraphics The new PrinterGraphics + */ +protected PrinterGraphics getFreshPrinterGraphics() { + if (printerGraphics !is null) { + printerGraphics.dispose(); + g.dispose(); + printerGraphics = null; + g = null; + } + g = new SWTGraphics(printerGC); + printerGraphics = new PrinterGraphics(g, printer); + setupGraphicsForPage(printerGraphics); + return printerGraphics; +} + +/** + * This method is invoked by the {@link #run(String)} method to determine the orientation + * of the GC to be used for printing. This default implementation always returns + * DWT.LEFT_TO_RIGHT. + * @return DWT.LEFT_TO_RIGHT or DWT.RIGHT_TO_LEFT + * @since 3.1 + * @TODO Make protected post-3.1 + */ +int getGraphicsOrientation() { + return DWT.LEFT_TO_RIGHT; +} + +/** + * Returns the printer. + * @return Printer + */ +public Printer getPrinter() { + return printer; +} + +/** + * Returns a Rectangle that represents the region that can be printed to. The x, y, + * height, and width values are using the printers coordinates. + * @return the print region + */ +public Rectangle getPrintRegion() { + dwt.graphics.Rectangle.Rectangle trim = printer.computeTrim(0, 0, 0, 0); + dwt.graphics.Point.Point printerDPI = printer.getDPI(); + Insets notAvailable = new Insets(-trim.y, -trim.x, + trim.height + trim.y, trim.width + trim.x); + Insets userPreferred = new Insets( + (printMargin.top * printerDPI.x) / 72, + (printMargin.left * printerDPI.x) / 72, + (printMargin.bottom * printerDPI.x) / 72, + (printMargin.right * printerDPI.x) / 72); + Rectangle paperBounds = new Rectangle(printer.getBounds()); + Rectangle printRegion = paperBounds.getCropped(notAvailable); + printRegion.intersect(paperBounds.getCropped(userPreferred)); + printRegion.translate(trim.x, trim.y); + return printRegion; +} + +/** + * This method contains all operations performed to sourceFigure prior to being printed. + */ +protected void preparePrintSource() { } + +/** + * This method is responsible for printing pages. (A page is printed by calling + * Printer.startPage(), followed by painting to the PrinterGraphics object, and then + * calling Printer.endPage()). + */ +protected abstract void printPages(); + +/** + * This method contains all operations performed to + * sourceFigure after being printed. + */ +protected void restorePrintSource() { } + +/** + * Sets the print job into motion. + * + * @param jobName A String representing the name of the print job + */ +public void run(String jobName) { + preparePrintSource(); + if (printer.startJob(jobName)) { + printerGC = new GC(getPrinter(), getGraphicsOrientation()); + printPages(); + printer.endJob(); + } + restorePrintSource(); + cleanup(); +} + +/** + * Sets the printer. + * @param printer The printer to set + */ +public void setPrinter(Printer printer) { + this.printer = printer; +} + +/** + * Sets the page margin in pels (logical pixels) to the passed Insets.(72 pels is 1 inch) + * + * @param margin The margin to set on the page + */ +public void setPrintMargin(Insets margin) { + printMargin = margin; +} + +/** + * Manipulates the PrinterGraphics to position it to paint in the desired region of the + * page. (Default is the top left corner of the page). + * + * @param pg The PrinterGraphics to setup + */ +protected void setupGraphicsForPage(PrinterGraphics pg) { + Rectangle printRegion = getPrintRegion(); + pg.clipRect(printRegion); + pg.translate(printRegion.getTopLeft()); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/PrinterGraphics.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/PrinterGraphics.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.PrinterGraphics; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwt.graphics.Font; +import dwt.graphics.FontData; +import dwt.graphics.Image; +import dwt.printing.Printer; +import dwt.widgets.Display; +import dwtx.draw2d.ScaledGraphics; +import dwtx.draw2d.Graphics; + +/** + * A scalable graphics object used to print to a printer. + * @author danlee + */ +public class PrinterGraphics : ScaledGraphics { + +Map imageCache; + +Printer printer; + +/** + * Creates a new PrinterGraphics with Graphics g, using Printer p + * @param g Graphics object to draw with + * @param p Printer to print to + */ +public this(Graphics g, Printer p) { + imageCache = new HashMap(); + super(g); + printer = p; +} + +Font createFont(FontData data) { + return new Font(printer, data); +} + +private Image printerImage(Image image) { + Image result = cast(Image)imageCache.get(image); + if (result !is null) + return result; + + result = new Image(printer, image.getImageData()); + imageCache.put(image, result); + return result; +} + +/** + * @see dwtx.draw2d.ScaledGraphics#dispose() + */ +public void dispose() { + super.dispose(); + + //Dispose printer images + Iterator iter = imageCache.values().iterator(); + while (iter.hasNext()) { + Image printerImage = (cast(Image)iter.next()); + printerImage.dispose(); + } + + imageCache.clear(); +} + +/** + * @see dwtx.draw2d.Graphics#drawImage(Image, int, int) + */ +public void drawImage(Image srcImage, int x, int y) { + super.drawImage(printerImage(srcImage), x, y); +} + +/** + * @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int) + */ +public void drawImage(Image srcImage, int sx, int sy, int sw, int sh, + int tx, int ty, int tw, int th) { + super.drawImage(printerImage(srcImage), sx, sy, sw, sh, tx, ty, tw, th); +} + +int zoomFontHeight(int height) { + return cast(int) + (height * zoom * Display.getCurrent().getDPI().y / printer.getDPI().y + + 0.0000001); +} + +int zoomLineWidth(int w) { + return cast(int)(w * zoom); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/RangeModel.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/RangeModel.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.RangeModel; + +import dwt.dwthelper.utils; + +import dwtx.dwtxhelper.Bean; + +/** + * This interface represents a range of possible values as well as the current values. + * There is a minumum and maximum value, a current value, and the extent. One use for a + * RangeModel is a scrollbar. There is a minimum value (the top of the scrollbar), a + * maximum value (the bottom of the scrollbar), a current value (the top of the thumb), + * and an extent (the length of the thumb). + */ +public interface RangeModel { + +/** Value property name */ +static const String PROPERTY_VALUE = "value"; //$NON-NLS-1$ +/** Extent property name */ +static const String PROPERTY_EXTENT = "extent"; //$NON-NLS-1$ +/** Minimum property name */ +static const String PROPERTY_MINIMUM = "minimum"; //$NON-NLS-1$ +/** Maximum property name */ +static const String PROPERTY_MAXIMUM = "maximum"; //$NON-NLS-1$ + +/** + * Registers listener as a PropertyChangeListener of this RangeModel. Listeners will be + * notified of changes to {@link #PROPERTY_VALUE value}, {@link #PROPERTY_EXTENT extent}, + * {@link #PROPERTY_MINIMUM minimum} and {@link #PROPERTY_MAXIMUM maximum} properties. + * + * @param listener The listener to add + */ +void addPropertyChangeListener(PropertyChangeListener listener); + +/** + * Returns the extent. + * @return The extent + */ +int getExtent(); + +/** + * Returns the maximum value in the range. + * @return The maximum value + */ +int getMaximum(); + +/** + * Returns the minimum value in the range. + * @return The minimum value + */ +int getMinimum(); + +/** + * Returns the current value. + * @return The current value + */ +int getValue(); + +/** + * Returns true if this RangeModel is enabled. + * @return true if this Rangel Model is enabled + */ +bool isEnabled(); + +/** + * Removes the given listener from this RangeModel's list of PropertyChangeListeners. + * @param listener The listener to remove + */ +void removePropertyChangeListener(PropertyChangeListener listener); + +/** + * Sets min, extent, and max all at once. + * @param min the new mininum + * @param extent the new extent + * @param max the new maximum + */ +void setAll(int min, int extent, int max); + +/** + * Sets the extent. + * @param extent The extent + */ +void setExtent(int extent); + +/** + * Sets the maximum value of the range. + * @param max The maximum value + */ +void setMaximum(int max); + +/** + * Sets the minimum value of the range. + * @param min The minimum value + */ +void setMinimum(int min); + +/** + * Sets the current value. + * @param value The current value + */ +void setValue(int value); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/RectangleFigure.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/RectangleFigure.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.RectangleFigure; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Shape; +import dwtx.draw2d.Graphics; + +/** + * Draws a rectangle whose size is determined by the bounds set to it. + */ +public class RectangleFigure : Shape { + +/** + * Creates a RectangleFigure. + */ +public this() { } + +/** + * @see Shape#fillShape(Graphics) + */ +protected void fillShape(Graphics graphics) { + graphics.fillRectangle(getBounds()); +} + +/** + * @see Shape#outlineShape(Graphics) + */ +protected void outlineShape(Graphics graphics) { + Rectangle r = getBounds(); + int x = r.x + lineWidth / 2; + int y = r.y + lineWidth / 2; + int w = r.width - Math.max(1, lineWidth); + int h = r.height - Math.max(1, lineWidth); + graphics.drawRectangle(x, y, w, h); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/RelativeBendpoint.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/RelativeBendpoint.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.RelativeBendpoint; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PrecisionPoint; +import dwtx.draw2d.Bendpoint; +import dwtx.draw2d.Connection; + +/** + * RelativeBendpoint is a Bendpoint that calculates its location based on its distance + * from the start and end points of the {@link Connection}, as well as its weight. See + * {@link #setWeight(float)} for a description of what behavior different weights will + * provide. + */ +public class RelativeBendpoint + : Bendpoint +{ + +private Connection connection; +private float weight = 0.5f; +private Dimension d1, d2; + +/** + * Constructs a new RelativeBendpoint. + * + * @since 2.0 + */ +public this() { } + +/** + * Constructs a new RelativeBendpoint and associates it with the given Connection. + * @param conn The Connection this Bendpoint is associated with + * @since 2.0 + */ +public this(Connection conn) { + setConnection(conn); +} + +/** + * Returns the Connection this Bendpoint is associated with. + * @return The Connection this Bendpoint is associated with + * @since 2.0 + */ +protected Connection getConnection() { + return connection; +} + +/** + * Calculates and returns this Bendpoint's new location. + * @return This Bendpoint's new location + * @since 2.0 + */ +public Point getLocation() { + PrecisionPoint a1 = new PrecisionPoint(getConnection().getSourceAnchor().getReferencePoint()); + PrecisionPoint a2 = new PrecisionPoint(getConnection().getTargetAnchor().getReferencePoint()); + + getConnection().translateToRelative(a1); + getConnection().translateToRelative(a2); + + return new PrecisionPoint((a1.preciseX() + d1.preciseWidth()) + * (1f - weight) + weight * (a2.preciseX() + d2.preciseWidth()), + (a1.preciseY() + d1.preciseHeight()) * (1f - weight) + weight + * (a2.preciseY() + d2.preciseHeight())); + } + +/** + * Sets the Connection this bendpoint should be associated with. + * @param conn The Connection this bendpoint should be associated with + * @since 2.0 + */ +public void setConnection(Connection conn) { + connection = conn; +} + +/** + * Sets the Dimensions representing the X and Y distances this Bendpoint is from the start + * and end points of the Connection. These Dimensions are generally set once and are used + * in calculating the Bendpoint's location. + * @param dim1 The X and Y distances this Bendpoint is from the start of the Connection + * @param dim2 The X and Y distances this Bendpoint is from the end of the Connection + * @since 2.0 + */ +public void setRelativeDimensions(Dimension dim1, Dimension dim2) { + d1 = dim1; + d2 = dim2; +} + +/** + * Sets the weight this Bendpoint should use to calculate its location. The weight should + * be between 0.0 and 1.0. A weight of 0.0 will cause the Bendpoint to follow the start + * point, while a weight of 1.0 will cause the Bendpoint to follow the end point. A weight + * of 0.5 (the default) will cause the Bendpoint to maintain its original aspect ratio + * between the start and end points. + * @param w The weight + * @since 2.0 + */ +public void setWeight(float w) { + weight = w; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/RelativeLocator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/RelativeLocator.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.RelativeLocator; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.PrecisionRectangle; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Locator; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.PositionConstants; + +/** + * Places a handle relative to a figure's bounds. The placement is determined by + * indicating the figure to which the placement is relative, and two floating-point value + * indicating the horizontal and vertical offset from that figure's top-left corner. The + * values (0.0, 0.0) would indicate the figure's top-left corner, while the values (1.0, + * 1.0) would indicate the figure's bottom-right corner. + *

+ * Constants such as {@link PositionConstants#NORTH NORTH} and {@link + * PositionConstants#SOUTH SOUTH} can be used to set the placement. + */ +public class RelativeLocator + : Locator +{ + +private double relativeX; +private double relativeY; +private IFigure reference; + +/** + * Null constructor. The reference figure must be set before use. The relative locations + * will default to (0.0, 0.0). + * @since 2.0 + */ +public this() { + relativeX = 0.0; + relativeY = 0.0; +} + +/** + * Constructs a RelativeLocator with the given reference figure and relative location. The + * location is a constant from {@link PositionConstants} used as a convenient and readable + * way to set both the relativeX and relativeY values. + * @param reference the reference figure + * @param location one of NORTH, NORTH_EAST, etc. + * @since 2.0 + */ +public this(IFigure reference, int location) { + setReferenceFigure(reference); + switch (location & PositionConstants.NORTH_SOUTH) { + case PositionConstants.NORTH: + relativeY = 0; break; + case PositionConstants.SOUTH: + relativeY = 1.0; break; + default: + relativeY = 0.5; + } + + switch (location & PositionConstants.EAST_WEST) { + case PositionConstants.WEST: + relativeX = 0; break; + case PositionConstants.EAST: + relativeX = 1.0; break; + default: + relativeX = 0.5; + } +} + +/** + * Constructs a RelativeLocator with the given reference Figure and offset ratios. + * @param reference the reference figure + * @param relativeX the relative X offset + * @param relativeY the relative Y offset + * @since 2.0 + */ +public this(IFigure reference, double relativeX, double relativeY) { + setReferenceFigure(reference); + this.relativeX = relativeX; + this.relativeY = relativeY; +} + +/** + * Returns the Reference Box in the Reference Figure's coordinate system. + * The returned Rectangle may be by reference, and should not be modified. + * @return the reference box + * @since 2.0 + */ +protected Rectangle getReferenceBox() { + return getReferenceFigure().getBounds(); +} + +/** + * Returns the Figure this locator is relative to. + * @return the reference figure + * @since 2.0 + */ +protected IFigure getReferenceFigure() { + return reference; +} + +/** + * Relocates the target using the relative offset locations. + * @see dwtx.draw2d.Locator#relocate(dwtx.draw2d.IFigure) + */ +public void relocate(IFigure target) { + IFigure reference = getReferenceFigure(); + Rectangle targetBounds = new PrecisionRectangle(getReferenceBox().getResized(-1, -1)); + reference.translateToAbsolute(targetBounds); + target.translateToRelative(targetBounds); + targetBounds.resize(1, 1); + + Dimension targetSize = target.getPreferredSize(); + + targetBounds.x + += cast(int) (targetBounds.width * relativeX - ((targetSize.width + 1) / 2)); + targetBounds.y + += cast(int) (targetBounds.height * relativeY - ((targetSize.height + 1) / 2)); + targetBounds.setSize(targetSize); + target.setBounds(targetBounds); +} + +/** + * Sets the reference figure this locator uses to place the target figure. + * @param reference the reference figure + * @since 2.0 + */ +public void setReferenceFigure(IFigure reference) { + this.reference = reference; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/RotatableDecoration.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/RotatableDecoration.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.RotatableDecoration; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.IFigure; + +/** + * An IFigure that can be rotated. + */ +public interface RotatableDecoration + : IFigure +{ + +/** + * Sets the location of this figure. + * @param p The location + */ +void setLocation(Point p); + +/** + * Sets the reference point used to determine the rotation angle. + * @param p The reference point + */ +void setReferencePoint(Point p); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/RoundedRectangle.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/RoundedRectangle.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.RoundedRectangle; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Shape; +import dwtx.draw2d.Graphics; + +/** + * Draws a Rectangle whose corners are rounded in appearance. The size of the rectangle is + * determined by the bounds set to it. + */ +public class RoundedRectangle : Shape { + +/** The width and height radii applied to each corner. */ +protected Dimension corner; + +/** + * Constructs a round cornered rectangle. + */ +public this() { + corner = new Dimension(8, 8); +} + +/** + * @see Shape#fillShape(Graphics) + */ +protected void fillShape(Graphics graphics) { + graphics.fillRoundRectangle(getBounds(), corner.width, corner.height); +} + +/** + * @see Shape#outlineShape(Graphics) + */ +protected void outlineShape(Graphics graphics) { + Rectangle f = Rectangle.SINGLETON; + Rectangle r = getBounds(); + f.x = r.x + lineWidth / 2; + f.y = r.y + lineWidth / 2; + f.width = r.width - lineWidth; + f.height = r.height - lineWidth; + graphics.drawRoundRectangle(f, corner.width, corner.height); +} + +/** + * Sets the dimensions of each corner. This will form the radii of the arcs which form the + * corners. + * + * @param d the dimensions of the corner + * @since 2.0 + */ +public void setCornerDimensions(Dimension d) { + corner.width = d.width; + corner.height = d.height; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/RoutingAnimator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/RoutingAnimator.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,216 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.RoutingAnimator; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.Animator; +import dwtx.draw2d.RoutingListener; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Connection; +import dwtx.draw2d.Animation; + +/** + * Animates the routing of a connection. The animator will capture the effects of the + * connection's router, and the play back the placement of the routing, interpolating the + * intermediate routes. + *

+ * To use a routing animator, hook it as a routing listener for the connection whose + * points are to be animated, by calling {@link + * PolylineConnection#addRoutingListener(RoutingListener)}. An animator is active + * only when the Animation utility is activated. + * + * @since 3.2 + */ +public class RoutingAnimator : Animator , RoutingListener { + +static const RoutingAnimator INSTANCE; +static this(){ + INSTANCE = new RoutingAnimator(); +} +/** + * Constructs a routing animator for use with one or more connections. The default + * instance ({@link #getDefault()} can be used on any number of connections. + * + * @since 3.2 + */ +protected this() { } + +/** + * Overridden to sync initial and final states. + * @see Animator#playbackStarting(IFigure) + */ +public void playbackStarting(IFigure connection) { + reconcileStates(cast(Connection)connection); +} + +/** + * Returns the current state of the connection. Currently, this is a copy of the list of + * points. However this Object could change in future releases and should not be + * considered API. + * @see Animator#getCurrentState(IFigure) + */ +protected Object getCurrentState(IFigure connection) { + return (cast(Connection)connection).getPoints().getCopy(); +} + +/** + * Returns the default instance. + * @return the default instance + * @since 3.2 + */ +public static RoutingAnimator getDefault() { + return INSTANCE; +} + +/** + * Hooks invalidate for animation purposes. + * @see RoutingListener#invalidate(Connection) + */ +public final void invalidate(Connection conn) { + if (Animation.isInitialRecording()) + Animation.hookAnimator(conn, this); +} + +/** + * Plays back the interpolated state. + * @see Animator#playback(IFigure) + */ +protected bool playback(IFigure figure) { + Connection conn = cast(Connection) figure; + + PointList list1 = cast(PointList)Animation.getInitialState(this, conn); + PointList list2 = cast(PointList)Animation.getFinalState(this, conn); + if (list1 is null) { + conn.setVisible(false); + return true; + } + + float progress = Animation.getProgress(); + if (list1.size() is list2.size()) { + Point pt1 = new Point(), pt2 = new Point(); + PointList points = conn.getPoints(); + points.removeAllPoints(); + for (int i = 0; i < list1.size(); i++) { + list1.getPoint(pt2, i); + list2.getPoint(pt1, i); + pt1.x = cast(int)Math.round(pt1.x * progress + (1 - progress) * pt2.x); + pt1.y = cast(int)Math.round(pt1.y * progress + (1 - progress) * pt2.y); + points.addPoint(pt1); + } + conn.setPoints(points); + } + return true; +} + +/** + * Hooks post routing for animation purposes. + * @see RoutingListener#postRoute(Connection) + */ +public final void postRoute(Connection connection) { + if (Animation.isFinalRecording()) + Animation.hookNeedsCapture(connection, this); +} + +private void reconcileStates(Connection conn) { + PointList points1 = cast(PointList)Animation.getInitialState(this, conn); + PointList points2 = cast(PointList)Animation.getFinalState(this, conn); + + if (points1 !is null && points1.size() !is points2.size()) { + Point p = new Point(), q = new Point(); + + int size1 = points1.size() - 1; + int size2 = points2.size() - 1; + + int i1 = size1; + int i2 = size2; + + double current1 = 1.0; + double current2 = 1.0; + + double prev1 = 1.0; + double prev2 = 1.0; + + while (i1 > 0 || i2 > 0) { + if (Math.abs(current1 - current2) < 0.1 + && i1 > 0 && i2 > 0) { + //Both points are the same, use them and go on; + prev1 = current1; + prev2 = current2; + i1--; + i2--; + current1 = cast(double)i1 / size1; + current2 = cast(double)i2 / size2; + } else if (current1 < current2) { + //2 needs to catch up + // current1 < current2 < prev1 + points1.getPoint(p, i1); + points1.getPoint(q, i1 + 1); + + p.x = cast(int)(((q.x * (current2 - current1) + p.x * (prev1 - current2)) + / (prev1 - current1))); + p.y = cast(int)(((q.y * (current2 - current1) + p.y * (prev1 - current2)) + / (prev1 - current1))); + + points1.insertPoint(p, i1 + 1); + + prev1 = prev2 = current2; + i2--; + current2 = cast(double)i2 / size2; + + } else { + //1 needs to catch up + // current2< current1 < prev2 + + points2.getPoint(p, i2); + points2.getPoint(q, i2 + 1); + + p.x = cast(int)(((q.x * (current1 - current2) + p.x * (prev2 - current1)) + / (prev2 - current2))); + p.y = cast(int)(((q.y * (current1 - current2) + p.y * (prev2 - current1)) + / (prev2 - current2))); + + points2.insertPoint(p, i2 + 1); + + prev2 = prev1 = current1; + i1--; + current1 = cast(double)i1 / size1; + } + } + } +} + +/** + * This callback is unused. Reserved for possible future use. + * @see RoutingListener#remove(Connection) + */ +public final void remove(Connection connection) { } + +/** + * Hooks route to intercept routing during animation playback. + * @see RoutingListener#route(Connection) + */ +public final bool route(Connection conn) { + return Animation.isAnimating() && Animation.hookPlayback(conn, this); +} + +/** + * This callback is unused. Reserved for possible future use. + * @see RoutingListener#setConstraint(Connection, Object) + */ +public final void setConstraint(Connection connection, Object constraint) { } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/RoutingListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/RoutingListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.RoutingListener; + +import dwt.dwthelper.utils; +import dwtx.draw2d.Connection; + +/** + * Classes which implement this interface provide callback hooks for various routing + * related events. + *

+ * Instances can be hooked to a {@link PolylineConnection} by calling {@link + * PolylineConnection#addRoutingListener(RoutingListener)}. + * @since 3.2 + */ +public interface RoutingListener { + +/** + * Called when the connection has been invalidated. + * @param connection the connection + * @since 3.2 + */ +void invalidate(Connection connection); + +/** + * Called after normal routing has completed. + * @param connection the routed connection + * @since 3.2 + */ +void postRoute(Connection connection); + +/** + * Called when a connection has been removed from its router. + * @param connection the connection + * @since 3.2 + */ +void remove(Connection connection); + +/** + * Called prior to routing occurring. A listener may intercept routing by + * returning true. If intercepted, the connection's + * ConnectionRouter will not perform routing. + * @param connection the connection being routed + * @return true if routing has been performed by the listener + * @since 3.2 + */ +bool route(Connection connection); + +/** + * Called when the connection's routing constraint has been set or initialized. + * @param connection the connection + * @param constraint the new constraint + * @since 3.2 + */ +void setConstraint(Connection connection, Object constraint); + +/** + * A stub implementation which implements all required methods. + * @since 3.2 + */ +class Stub : RoutingListener { + public void invalidate(Connection connection) { } + public void postRoute(Connection connection) { } + public void remove(Connection connection) { } + public bool route(Connection connection) { + return false; + } + public void setConstraint(Connection connection, Object constraint) { } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/SWTEventDispatcher.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/SWTEventDispatcher.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,575 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +module dwtx.draw2d.SWTEventDispatcher; + +import dwt.dwthelper.utils; + +import dwt.DWT; +import dwt.accessibility.AccessibleControlEvent; +import dwt.accessibility.AccessibleControlListener; +import dwt.accessibility.AccessibleEvent; +import dwt.accessibility.AccessibleListener; +import dwt.events.DisposeEvent; +import dwt.events.TraverseEvent; +import dwt.graphics.Cursor; +import dwt.widgets.Control; +import dwtx.draw2d.EventDispatcher; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Figure; +import dwtx.draw2d.MouseEvent; +import dwtx.draw2d.FocusEvent; +import dwtx.draw2d.KeyEvent; +import dwtx.draw2d.ToolTipHelper; +static import dwt.widgets.Control; +import dwtx.draw2d.FocusTraverseManager; +static import dwt.events.FocusEvent; +static import dwt.events.KeyEvent; +static import dwt.events.MouseEvent; + +/** + * The SWTEventDispatcher provides draw2d with the ability to dispatch DWT Events. The + * {@link dwtx.draw2d.LightweightSystem} adds DWT event listeners on its Canvas. + * When the Canvas receives an DWT event, it calls the appropriate dispatcher method in + * SWTEventDispatcher. + */ +public class SWTEventDispatcher + : EventDispatcher +{ + +/** + * Used to tell if any button is pressed without regard to the specific button. + * @deprecated Use {@link DWT#BUTTON_MASK} instead. + */ +protected static const int ANY_BUTTON = DWT.BUTTON_MASK; + +private bool figureTraverse = true; + +private bool captured; +private IFigure root; +private IFigure mouseTarget; +private IFigure cursorTarget; +private IFigure focusOwner; +private IFigure hoverSource; + +private MouseEvent currentEvent; +private Cursor cursor; +/** The control this dispatcher is listening to. */ +protected dwt.widgets.Control.Control control; + +private ToolTipHelper toolTipHelper; +private FocusTraverseManager focusManager; + +/** + * Implements {@link EventDispatcher.AccessibilityDispatcher} but + * does nothing in the implementation. + */ +protected class FigureAccessibilityDispatcher + : AccessibilityDispatcher +{ + /** @see AccessibleControlListener#getChildAtPoint(AccessibleControlEvent) */ + public void getChildAtPoint(AccessibleControlEvent e) { } + /** @see AccessibleControlListener#getChildCount(AccessibleControlEvent) */ + public void getChildCount(AccessibleControlEvent e) { } + /** @see AccessibleControlListener#getChildren(AccessibleControlEvent) */ + public void getChildren(AccessibleControlEvent e) { } + /** @see AccessibleControlListener#getDefaultAction(AccessibleControlEvent) */ + public void getDefaultAction(AccessibleControlEvent e) { } + /** @see AccessibleListener#getDescription(AccessibleEvent) */ + public void getDescription(AccessibleEvent e) { } + /** @see AccessibleControlListener#getFocus(AccessibleControlEvent) */ + public void getFocus(AccessibleControlEvent e) { } + /** @see AccessibleListener#getHelp(AccessibleEvent) */ + public void getHelp(AccessibleEvent e) { } + /** @see AccessibleListener#getKeyboardShortcut(AccessibleEvent) */ + public void getKeyboardShortcut(AccessibleEvent e) { } + /** @see AccessibleControlListener#getLocation(AccessibleControlEvent) */ + public void getLocation(AccessibleControlEvent e) { } + /** @see AccessibleListener#getName(AccessibleEvent) */ + public void getName(AccessibleEvent e) { } + /** @see AccessibleControlListener#getRole(AccessibleControlEvent) */ + public void getRole(AccessibleControlEvent e) { } + /** @see AccessibleControlListener#getSelection(AccessibleControlEvent) */ + public void getSelection(AccessibleControlEvent e) { } + /** @see AccessibleControlListener#getState(AccessibleControlEvent) */ + public void getState(AccessibleControlEvent e) { } + /** @see AccessibleControlListener#getValue(AccessibleControlEvent) */ + public void getValue(AccessibleControlEvent e) { } +} + +public this(){ + focusManager = new FocusTraverseManager(); +} + +/** + * @see EventDispatcher#dispatchFocusGained(dwt.events.FocusEvent) + */ +public void dispatchFocusGained(dwt.events.FocusEvent.FocusEvent e) { + IFigure currentFocusOwner = getFocusTraverseManager().getCurrentFocusOwner(); + + /* + * Upon focus gained, if there is no current focus owner, + * set focus on first focusable child. + */ + if (currentFocusOwner is null) + currentFocusOwner = + getFocusTraverseManager().getNextFocusableFigure(root, focusOwner); + setFocus(currentFocusOwner); +} + +/** + * @see EventDispatcher#dispatchFocusLost(dwt.events.FocusEvent) + */ +public void dispatchFocusLost(dwt.events.FocusEvent.FocusEvent e) { + setFocus(null); +} + +/** + * @see EventDispatcher#dispatchKeyPressed(dwt.events.KeyEvent) + */ +public void dispatchKeyPressed(dwt.events.KeyEvent.KeyEvent e) { + if (focusOwner !is null) { + KeyEvent event = new KeyEvent(this, focusOwner, e); + focusOwner.handleKeyPressed(event); + } +} + +/** + * @see EventDispatcher#dispatchKeyReleased(dwt.events.KeyEvent) + */ +public void dispatchKeyReleased(dwt.events.KeyEvent.KeyEvent e) { + if (focusOwner !is null) { + KeyEvent event = new KeyEvent(this, focusOwner, e); + focusOwner.handleKeyReleased(event); + } +} + +/** + * @see EventDispatcher#dispatchKeyTraversed(TraverseEvent) + */ +public void dispatchKeyTraversed(TraverseEvent e) { + if (!figureTraverse) + return; + IFigure nextFigure = null; + + if (e.detail is DWT.TRAVERSE_TAB_NEXT) + nextFigure = getFocusTraverseManager().getNextFocusableFigure(root, focusOwner); + else if (e.detail is DWT.TRAVERSE_TAB_PREVIOUS) + nextFigure = + getFocusTraverseManager().getPreviousFocusableFigure(root, focusOwner); + + if (nextFigure !is null) { + e.doit = false; + setFocus(nextFigure); + } +} + +/** + * @see EventDispatcher#dispatchMouseHover(dwt.events.MouseEvent) + */ +public void dispatchMouseHover(dwt.events.MouseEvent.MouseEvent me) { + receive(me); + if (mouseTarget !is null) + mouseTarget.handleMouseHover(currentEvent); + /* + * Check Tooltip source. + * Get Tooltip source's Figure. + * Set that tooltip as the lws contents on the helper. + */ + if (hoverSource !is null) { + toolTipHelper = getToolTipHelper(); + IFigure tip = hoverSource.getToolTip(); + Control control = cast(Control)me.getSource(); + dwt.graphics.Point.Point absolute; + absolute = control.toDisplay(new dwt.graphics.Point.Point(me.x, me.y)); + toolTipHelper.displayToolTipNear(hoverSource, tip, absolute.x, absolute.y); + } +} + +/** + * @see EventDispatcher#dispatchMouseDoubleClicked(dwt.events.MouseEvent) + */ +public void dispatchMouseDoubleClicked(dwt.events.MouseEvent.MouseEvent me) { + receive(me); + if (mouseTarget !is null) + mouseTarget.handleMouseDoubleClicked(currentEvent); +} + +/** + * @see EventDispatcher#dispatchMouseEntered(dwt.events.MouseEvent) + */ +public void dispatchMouseEntered(dwt.events.MouseEvent.MouseEvent me) { + receive(me); +} + +/** + * @see EventDispatcher#dispatchMouseExited(dwt.events.MouseEvent) + */ +public void dispatchMouseExited(dwt.events.MouseEvent.MouseEvent me) { + setHoverSource(null, me); + if (mouseTarget !is null) { + currentEvent = + new MouseEvent(me.x, me.y, this, mouseTarget, me.button, me.stateMask); + mouseTarget.handleMouseExited(currentEvent); + releaseCapture(); + mouseTarget = null; + } +} + +/** + * @see EventDispatcher#dispatchMousePressed(dwt.events.MouseEvent) + */ +public void dispatchMousePressed(dwt.events.MouseEvent.MouseEvent me) { + receive(me); + if (mouseTarget !is null) { + mouseTarget.handleMousePressed(currentEvent); + if (currentEvent.isConsumed()) + setCapture(mouseTarget); + } +} + +/** + * @see EventDispatcher#dispatchMouseMoved(dwt.events.MouseEvent) + */ +public void dispatchMouseMoved(dwt.events.MouseEvent.MouseEvent me) { + receive(me); + if (mouseTarget !is null) { + if ((me.stateMask & DWT.BUTTON_MASK) !is 0) + mouseTarget.handleMouseDragged(currentEvent); + else + mouseTarget.handleMouseMoved(currentEvent); + } +} + +/** + * @see EventDispatcher#dispatchMouseReleased(dwt.events.MouseEvent) + */ +public void dispatchMouseReleased(dwt.events.MouseEvent.MouseEvent me) { + receive(me); + if (mouseTarget !is null) { + mouseTarget.handleMouseReleased(currentEvent); + } + releaseCapture(); + receive(me); +} + +/** + * @see EventDispatcher#getAccessibilityDispatcher() + */ +protected AccessibilityDispatcher getAccessibilityDispatcher() { + return null; +} + +/** + * Returns the current mouse event. + * @return the current mouse event; can be null + */ +protected MouseEvent getCurrentEvent() { + return currentEvent; +} + +private IFigure getCurrentToolTip() { + if (hoverSource !is null) + return hoverSource.getToolTip(); + else + return null; +} + +/** + * Returns the figure that the cursor is over. + * @return the cursor target + */ +protected IFigure getCursorTarget() { + return cursorTarget; +} + +/** + * Returns the ToolTipHelper used to display tooltips on hover events. + * @return the ToolTipHelper + */ +protected ToolTipHelper getToolTipHelper() { + if (toolTipHelper is null) + toolTipHelper = new ToolTipHelper(control); + return toolTipHelper; +} + +/** + * Returns the FocusTraverseManager which is used to determine which figure will get focus + * when a TAB or ALT+TAB key sequence occurs. + * @return the FocusTraverseManager + */ +protected final FocusTraverseManager getFocusTraverseManager() { + if (focusManager is null) { + focusManager = new FocusTraverseManager(); + } + return focusManager; +} + +/** + * @see EventDispatcher#getFocusOwner() + */ +/*package*/ IFigure getFocusOwner() { + return focusOwner; +} + +/** + * Returns the figure that is the target of mouse events. This may not be the figure + * beneath the cursor because another figure may have captured the mouse and will continue + * to get mouse events until capture is released. + * @return the mouse target + */ +protected IFigure getMouseTarget() { + return mouseTarget; +} + +/** + * Returns the root figure for this dispatcher. + * @return the root figure + */ +protected IFigure getRoot() { + return root; +} + +/** + * @see EventDispatcher#isCaptured() + */ +public bool isCaptured() { + return captured; +} + +private void receive(dwt.events.MouseEvent.MouseEvent me) { + currentEvent = null; + updateFigureUnderCursor(me); + int state = me.stateMask; + if (captured) { + if (mouseTarget !is null) + currentEvent = new MouseEvent(me.x, me.y, this, mouseTarget, me.button, state); + } else { + IFigure f = root.findMouseEventTargetAt(me.x, me.y); + if (f is mouseTarget) { + if (mouseTarget !is null) + currentEvent = + new MouseEvent(me.x, me.y, this, mouseTarget, me.button, state); + return; + } + if (mouseTarget !is null) { + currentEvent = new MouseEvent(me.x, me.y, this, mouseTarget, me.button, state); + mouseTarget.handleMouseExited(currentEvent); + } + setMouseTarget(f); + if (mouseTarget !is null) { + currentEvent = new MouseEvent(me.x, me.y, this, mouseTarget, me.button, state); + mouseTarget.handleMouseEntered(currentEvent); + } + } +} + +/** + * @see EventDispatcher#releaseCapture() + */ +protected void releaseCapture() { + captured = false; +} + +/** + * @see EventDispatcher#requestFocus(IFigure) + */ +public void requestFocus(IFigure fig) { + setFocus(fig); +} + +/** + * @see EventDispatcher#requestRemoveFocus(IFigure) + */ +public void requestRemoveFocus(IFigure fig) { + if (getFocusOwner() is fig) + setFocus(null); + if (mouseTarget is fig) + mouseTarget = null; + if (cursorTarget is fig) + cursorTarget = null; + if (hoverSource is fig) + hoverSource = null; + getFocusTraverseManager().setCurrentFocusOwner(null); +} + +/** + * @see EventDispatcher#setCapture(IFigure) + */ +protected void setCapture(IFigure figure) { + captured = true; + mouseTarget = figure; +} + +/** + * @see EventDispatcher#setControl(Control) + */ +public void setControl(Control c) { + if (c is control) + return; + if (control !is null && !control.isDisposed()) + throw new RuntimeException( + "Can not set control again once it has been set"); //$NON-NLS-1$ + if (c !is null) + c.addDisposeListener(new class() dwt.events.DisposeListener.DisposeListener { + public void widgetDisposed(DisposeEvent e) { + if (toolTipHelper !is null) + toolTipHelper.dispose(); + } + }); + control = c; +} + +/** + * Sets the mouse cursor. + * @param c the new cursor + */ +protected void setCursor(Cursor c) { + if (c is null && cursor is null) { + return; + } else if ((c !is cursor) || (!c.opEquals(cursor))) { + cursor = c; + if (control !is null && !control.isDisposed()) + control.setCursor(c); + } +} + +/** + * Enables key traversal via TAB and ALT+TAB if traverse is true. + * Disables it otherwise. + * @param traverse whether key traversal should be enabled + */ +public void setEnableKeyTraversal(bool traverse) { + figureTraverse = traverse; +} + +/** + * Sets the figure under the mouse cursor. + * @param f the new figure under the cursor + */ +protected void setFigureUnderCursor(IFigure f) { + if (cursorTarget is f) + return; + cursorTarget = f; + updateCursor(); +} + +/** + * Sets the focus figure. If the figure currently with focus is not null, + * {@link IFigure#handleFocusLost(FocusEvent)} is called on the current focused figure. If + * the new focus figure is not null, this will call + * {@link IFigure#handleFocusGained(FocusEvent)} on the new focused figure. + * @param fig the new focus figure + */ +protected void setFocus(IFigure fig) { + if (fig is focusOwner) + return; + FocusEvent fe = new FocusEvent(focusOwner, fig); + IFigure oldOwner = focusOwner; + focusOwner = fig; + if (oldOwner !is null) + oldOwner.handleFocusLost(fe); + if (fig !is null) + getFocusTraverseManager().setCurrentFocusOwner(fig); + if (focusOwner !is null) + focusOwner.handleFocusGained(fe); +} + +/** + * Sets the figure that the mouse cursor is hovering over. + * @param figure the new hover source + * @param me the mouse event + */ +protected void setHoverSource(Figure figure, dwt.events.MouseEvent.MouseEvent me) { + hoverSource = figure; + if (figure !is null) { + Control control = cast(Control)me.getSource(); + dwt.graphics.Point.Point absolute; + absolute = control.toDisplay(new dwt.graphics.Point.Point(me.x, me.y)); + toolTipHelper = getToolTipHelper(); + toolTipHelper.updateToolTip( + hoverSource, getCurrentToolTip(), absolute.x, absolute.y); + } else if (toolTipHelper !is null) { + // Update with null to clear hoverSource in ToolTipHelper + toolTipHelper.updateToolTip(hoverSource, getCurrentToolTip(), me.x, me.y); + } +} + +/** + * Sets the given figure to be the target of future mouse events. + * @param figure the new mouse target + */ +protected void setMouseTarget(IFigure figure) { + mouseTarget = figure; +} + +/** + * @see EventDispatcher#setRoot(IFigure) + */ +public void setRoot(IFigure figure) { + root = figure; +} + +/** + * @see EventDispatcher#updateCursor() + */ +protected void updateCursor() { + Cursor newCursor = null; + if (cursorTarget !is null) + newCursor = cursorTarget.getCursor(); + setCursor(newCursor); +} + +/** + * Updates the figure under the cursor, unless the mouse is captured, in which case all + * mouse events will be routed to the figure that captured the mouse. + * @param me the mouse event + */ +protected void updateFigureUnderCursor(dwt.events.MouseEvent.MouseEvent me) { + if (!captured) { + IFigure f = root.findFigureAt(me.x, me.y); + setFigureUnderCursor(f); + if (cast(Figure)cursorTarget !is hoverSource) + updateHoverSource(me); + } +} + +/** + * Updates the figure that will receive hover events. The hover source must have a + * tooltip. If the figure under the mouse doesn't have a tooltip set, this method will + * walk up the ancestor hierarchy until either a figure with a tooltip is found or it + * gets to the root figure. + * @param me the mouse event + */ +protected void updateHoverSource(dwt.events.MouseEvent.MouseEvent me) { + /* + * Derive source from figure under cursor. + * Set the source in setHoverSource(); + * If figure.getToolTip() is null, get parent's toolTip + * Continue parent traversal until a toolTip is found or root is reached. + */ + if (cursorTarget !is null) { + bool sourceFound = false; + Figure source = cast(Figure)cursorTarget; + while (!sourceFound && source.getParent() !is null) { + if (source.getToolTip() !is null) + sourceFound = true; + else + source = cast(Figure)source.getParent(); + } + setHoverSource(source, me); + } else { + setHoverSource(null, me); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/SWTGraphics.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/SWTGraphics.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,1220 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +module dwtx.draw2d.SWTGraphics; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +static import dwt.graphics.Rectangle; +import dwt.DWT; +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.FontMetrics; +import dwt.graphics.GC; +import dwt.graphics.Image; +import dwt.graphics.Path; +import dwt.graphics.Pattern; +import dwt.graphics.TextLayout; +import dwt.graphics.Transform; +import dwt.widgets.Display; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Graphics; + +/** + * A concrete implementation of Graphics using an DWT + * GC. There are 2 states contained in this graphics class -- the + * applied state which is the actual state of the GC and the current state which + * is the current state of this graphics object. Certain properties can be + * changed multiple times and the GC won't be updated until it's actually used. + *

+ * WARNING: This class is not intended to be subclassed. + */ +public class SWTGraphics + : Graphics +{ + +/** + * An internal type used to represent and update the GC's clipping. + * @since 3.1 + */ +interface Clipping { + /** + * Sets the clip's bounding rectangle into the provided argument and returns it for convenince. + * @param rect the rect + * @return the given rect + * @since 3.1 + */ + Rectangle getBoundingBox(Rectangle rect); + Clipping getCopy(); + void intersect(int left, int top, int right, int bottom); + void scale(float horizontal, float vertical); + void setOn(GC gc, int translateX, int translateY); + void translate(float dx, float dy); +} + +/** + * Any state stored in this class is only applied when it is needed by a + * specific graphics call. + * @since 3.1 + */ +static class LazyState { + Color bgColor; + Color fgColor; + Font font; + int graphicHints; + int lineWidth; + Clipping relativeClip; +} + +static class RectangleClipping : Clipping { + private float top, left, bottom, right; + + this(float left, float top, float right, float bottom) { + this.left = left; + this.right = right; + this.bottom = bottom; + this.top = top; + } + + this(dwt.graphics.Rectangle.Rectangle rect) { + left = rect.x; + top = rect.y; + right = rect.x + rect.width; + bottom = rect.y + rect.height; + } + + this(Rectangle rect) { + left = rect.x; + top = rect.y; + right = rect.right(); + bottom = rect.bottom(); + } + + public Rectangle getBoundingBox(Rectangle rect) { + rect.x = cast(int)left; + rect.y = cast(int)top; + rect.width = cast(int)Math.ceil(right) - rect.x; + rect.height = cast(int)Math.ceil(bottom) - rect.y; + return rect; + } + + public Clipping getCopy() { + return new RectangleClipping(left, top, right, bottom); + } + + public void intersect(int left, int top, int right, int bottom) { + this.left = Math.max(this.left, left); + this.right = Math.min(this.right, right); + this.top = Math.max(this.top, top); + this.bottom = Math.min(this.bottom, bottom); + if (right < left || bottom < top) { + //width and height of -1 to avoid ceiling function from re-adding a pixel. + this.right = left - 1; + this.bottom = top - 1; + } + } + + public void scale(float horz, float vert) { + left /= horz; + right /= horz; + top /= vert; + bottom /= vert; + } + + public void setOn(GC gc, int translateX, int translateY) { + int xInt = cast(int)Math.floor(left); + int yInt = cast(int)Math.floor(top); + gc.setClipping(xInt + translateX, yInt + translateY, + cast(int)Math.ceil(right) - xInt, + cast(int)Math.ceil(bottom) - yInt); + } + + public void translate(float dx, float dy) { + left += dx; + right += dx; + top += dy; + bottom += dy; + } +} + +/** + * Contains the entire state of the Graphics. + */ +static class State + : LazyState + , Cloneable +{ + float affineMatrix[]; + int alpha; + Pattern bgPattern; + int dx, dy; + + Pattern fgPattern; + int lineDash[]; + + this(){ + } + + public Object clone() { + auto res = new State; + res.affineMatrix = this.affineMatrix.dup; + res.alpha = this.alpha; + res.bgPattern = this.bgPattern; + res.dx = this.dx; + res.dy = this.dy; + res.fgPattern = this.fgPattern; + res.lineDash = this.lineDash.dup; + return res; + } + + /** + * Copies all state information from the given State to this State + * @param state The State to copy from + */ + public void copyFrom(State state) { + bgColor = state.bgColor; + fgColor = state.fgColor; + lineWidth = state.lineWidth; + dx = state.dx; + dy = state.dy; + bgPattern = state.bgPattern; + fgPattern = state.fgPattern; + font = state.font; + lineDash = state.lineDash; + graphicHints = state.graphicHints; + affineMatrix = state.affineMatrix; + relativeClip = state.relativeClip; + alpha = state.alpha; + } +} + +static const int AA_MASK; +static const int AA_SHIFT; +static const int AA_WHOLE_NUMBER = 1; +static const int ADVANCED_GRAPHICS_MASK; +static const int ADVANCED_HINTS_DEFAULTS; +static const int ADVANCED_HINTS_MASK; +static const int ADVANCED_SHIFT; +static const int CAP_MASK; +static const int CAP_SHIFT; +static const int FILL_RULE_MASK; +static const int FILL_RULE_SHIFT; +static const int FILL_RULE_WHOLE_NUMBER = -1; +static const int INTERPOLATION_MASK; +static const int INTERPOLATION_SHIFT; +static const int INTERPOLATION_WHOLE_NUMBER = 1; +static const int JOIN_MASK; +static const int JOIN_SHIFT; +static const int LINE_STYLE_MASK; + +static const int TEXT_AA_MASK; +static const int TEXT_AA_SHIFT; +static const int XOR_MASK; +static const int XOR_SHIFT; + +static this() { + XOR_SHIFT = 3; + CAP_SHIFT = 4; + JOIN_SHIFT = 6; + AA_SHIFT = 8; + TEXT_AA_SHIFT = 10; + INTERPOLATION_SHIFT = 12; + FILL_RULE_SHIFT = 14; + ADVANCED_SHIFT = 15; + + LINE_STYLE_MASK = 7; + AA_MASK = 3 << AA_SHIFT; + CAP_MASK = 3 << CAP_SHIFT; + FILL_RULE_MASK = 1 << FILL_RULE_SHIFT; + INTERPOLATION_MASK = 3 << INTERPOLATION_SHIFT; + JOIN_MASK = 3 << JOIN_SHIFT; + TEXT_AA_MASK = 3 << TEXT_AA_SHIFT; + XOR_MASK = 1 << XOR_SHIFT; + ADVANCED_GRAPHICS_MASK = 1 << ADVANCED_SHIFT; + + ADVANCED_HINTS_MASK = TEXT_AA_MASK | AA_MASK | INTERPOLATION_MASK; + ADVANCED_HINTS_DEFAULTS = ((DWT.DEFAULT + AA_WHOLE_NUMBER) << TEXT_AA_SHIFT) + | ((DWT.DEFAULT + AA_WHOLE_NUMBER) << AA_SHIFT) + | ((DWT.DEFAULT + INTERPOLATION_WHOLE_NUMBER) << INTERPOLATION_SHIFT); +} + +private const LazyState appliedState; +private const State currentState; + +private bool elementsNeedUpdate; +private GC gc; + +private bool sharedClipping; +private List stack; + +private int stackPointer = 0; +Transform transform; +private int translateX = 0; +private int translateY = 0; + +/** + * Constructs a new SWTGraphics that draws to the Canvas using the given GC. + * @param gc the GC + */ +public this(GC gc) { + appliedState = new LazyState(); + currentState = new State(); + stack = new ArrayList(); + this.gc = gc; + init(); +} + +/** + * If the background color has changed, this change will be pushed to the GC. Also calls + * {@link #checkGC()}. + */ +protected final void checkFill() { + if (!currentState.bgColor.opEquals(appliedState.bgColor) && currentState.bgPattern is null) + gc.setBackground(appliedState.bgColor = currentState.bgColor); + checkGC(); +} + +/** + * If the rendering hints or the clip region has changed, these changes will be pushed to + * the GC. Rendering hints include anti-alias, xor, join, cap, line style, fill rule, + * interpolation, and other settings. + */ +protected /+final+/ void checkGC() { + if (appliedState.relativeClip !is currentState.relativeClip) { + appliedState.relativeClip = currentState.relativeClip; + currentState.relativeClip.setOn(gc, translateX, translateY); + } + if (appliedState.graphicHints !is currentState.graphicHints) { + reconcileHints(appliedState.graphicHints, currentState.graphicHints); + appliedState.graphicHints = currentState.graphicHints; + } +} + +/** + * If the line width, line style, foreground or background colors have changed, these + * changes will be pushed to the GC. Also calls {@link #checkGC()}. + */ +protected /+final+/ void checkPaint() { + checkGC(); + if (!currentState.fgColor.opEquals(appliedState.fgColor) && currentState.fgPattern is null) + gc.setForeground(appliedState.fgColor = currentState.fgColor); + if (appliedState.lineWidth !is currentState.lineWidth) + gc.setLineWidth(appliedState.lineWidth = currentState.lineWidth); + if (!currentState.bgColor.opEquals(appliedState.bgColor) && currentState.bgPattern is null) + gc.setBackground(appliedState.bgColor = currentState.bgColor); +} + +/** + * @since 3.1 + */ +private void checkSharedClipping() { + if (sharedClipping) { + sharedClipping = false; + + bool previouslyApplied = (appliedState is currentState.relativeClip); + currentState.relativeClip = currentState.relativeClip.getCopy(); + if (previouslyApplied) + appliedState.relativeClip = currentState.relativeClip; + } +} + +/** + * If the font has changed, this change will be pushed to the GC. Also calls + * {@link #checkPaint()} and {@link #checkFill()}. + */ +protected /+final+/ void checkText() { + checkPaint(); + if (!appliedState.font.opEquals(currentState.font)) + gc.setFont(appliedState.font = currentState.font); +} + +/** + * @see Graphics#clipRect(Rectangle) + */ +public void clipRect(Rectangle rect) { + if (currentState.relativeClip is null) + throw new IllegalStateException("The current clipping area does not " //$NON-NLS-1$ + "support intersection."); //$NON-NLS-1$ + checkSharedClipping(); + currentState.relativeClip.intersect(rect.x, rect.y, rect.right(), rect.bottom()); + appliedState.relativeClip = null; +} + +/** + * @see Graphics#dispose() + */ +public void dispose() { + while (stackPointer > 0) + popState(); + if (transform !is null) + transform.dispose(); +} + +/** + * @see Graphics#drawArc(int, int, int, int, int, int) + */ +public void drawArc(int x, int y, int width, int height, int offset, int length) { + checkPaint(); + gc.drawArc(x + translateX, y + translateY, width, height, offset, length); +} + +/** + * @see Graphics#drawFocus(int, int, int, int) + */ +public void drawFocus(int x, int y, int w, int h) { + checkPaint(); + gc.drawFocus(x + translateX, y + translateY, w + 1, h + 1); +} + +/** + * @see Graphics#drawImage(Image, int, int) + */ +public void drawImage(Image srcImage, int x, int y) { + checkGC(); + gc.drawImage(srcImage, x + translateX, y + translateY); +} + +/** + * @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int) + */ +public void drawImage(Image srcImage, + int x1, int y1, int w1, int h1, + int x2, int y2, int w2, int h2) { + checkGC(); + gc.drawImage(srcImage, x1, y1, w1, h1, x2 + translateX, y2 + translateY, w2, h2); +} + +/** + * @see Graphics#drawLine(int, int, int, int) + */ +public void drawLine(int x1, int y1, int x2, int y2) { + checkPaint(); + gc.drawLine(x1 + translateX, y1 + translateY, x2 + translateX, y2 + translateY); +} + +/** + * @see Graphics#drawOval(int, int, int, int) + */ +public void drawOval(int x, int y, int width, int height) { + checkPaint(); + gc.drawOval(x + translateX, y + translateY, width, height); +} + +/** + * This method requires advanced graphics support. A check should be made to + * ensure advanced graphics is supported in the user's environment before + * calling this method. See {@link GC#getAdvanced()}. + * + * @see Graphics#drawPath(Path) + */ +public void drawPath(Path path) { + checkPaint(); + initTransform(false); + gc.drawPath(path); +} + +/** + * @see Graphics#drawPoint(int, int) + */ +public void drawPoint(int x, int y) { + checkPaint(); + gc.drawPoint(x + translateX, y + translateY); +} + +/** + * @see Graphics#drawPolygon(int[]) + */ +public void drawPolygon(int[] points) { + checkPaint(); + try { + translatePointArray(points, translateX, translateY); + gc.drawPolygon(points); + } finally { + translatePointArray(points, -translateX, -translateY); + } +} + +/** + * @see Graphics#drawPolygon(PointList) + */ +public void drawPolygon(PointList points) { + drawPolygon(points.toIntArray()); +} + +/** + * @see Graphics#drawPolyline(int[]) + */ +public void drawPolyline(int[] points) { + checkPaint(); + try { + translatePointArray(points, translateX, translateY); + gc.drawPolyline(points); + } finally { + translatePointArray(points, -translateX, -translateY); + } +} + +/** + * @see Graphics#drawPolyline(PointList) + */ +public void drawPolyline(PointList points) { + drawPolyline(points.toIntArray()); +} + +/** + * @see Graphics#drawRectangle(int, int, int, int) + */ +public void drawRectangle(int x, int y, int width, int height) { + checkPaint(); + gc.drawRectangle(x + translateX, y + translateY, width, height); +} + +/** + * @see Graphics#drawRoundRectangle(Rectangle, int, int) + */ +public void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight) { + checkPaint(); + gc.drawRoundRectangle(r.x + translateX, r.y + translateY, r.width, r.height, + arcWidth, arcHeight); +} + +/** + * @see Graphics#drawString(String, int, int) + */ +public void drawString(String s, int x, int y) { + checkText(); + gc.drawString(s, x + translateX, y + translateY, true); +} + +/** + * @see Graphics#drawText(String, int, int) + */ +public void drawText(String s, int x, int y) { + checkText(); + gc.drawText(s, x + translateX, y + translateY, true); +} + +/** + * @see Graphics#drawTextLayout(TextLayout, int, int, int, int, Color, Color) + */ +public void drawTextLayout(TextLayout layout, int x, int y, int selectionStart, + int selectionEnd, Color selectionForeground, Color selectionBackground) { + //$TODO probably just call checkPaint since Font and BG color don't apply + checkText(); + layout.draw(gc, x + translateX, y + translateY, selectionStart, selectionEnd, + selectionForeground, selectionBackground); +} + +/** + * @see Graphics#fillArc(int, int, int, int, int, int) + */ +public void fillArc(int x, int y, int width, int height, int offset, int length) { + checkFill(); + gc.fillArc(x + translateX, y + translateY, width, height, offset, length); +} + +/** + * @see Graphics#fillGradient(int, int, int, int, bool) + */ +public void fillGradient(int x, int y, int w, int h, bool vertical) { + checkPaint(); + gc.fillGradientRectangle(x + translateX, y + translateY, w, h, vertical); +} + +/** + * @see Graphics#fillOval(int, int, int, int) + */ +public void fillOval(int x, int y, int width, int height) { + checkFill(); + gc.fillOval(x + translateX, y + translateY, width, height); +} + +/** + * This method requires advanced graphics support. A check should be made to + * ensure advanced graphics is supported in the user's environment before + * calling this method. See {@link GC#getAdvanced()}. + * + * @see Graphics#fillPath(Path) + */ +public void fillPath(Path path) { + checkFill(); + initTransform(false); + gc.fillPath(path); +} + +/** + * @see Graphics#fillPolygon(int[]) + */ +public void fillPolygon(int[] points) { + checkFill(); + try { + translatePointArray(points, translateX, translateY); + gc.fillPolygon(points); + } finally { + translatePointArray(points, -translateX, -translateY); + } +} + +/** + * @see Graphics#fillPolygon(PointList) + */ +public void fillPolygon(PointList points) { + fillPolygon(points.toIntArray()); +} + +/** + * @see Graphics#fillRectangle(int, int, int, int) + */ +public void fillRectangle(int x, int y, int width, int height) { + checkFill(); + gc.fillRectangle(x + translateX, y + translateY, width, height); +} + +/** + * @see Graphics#fillRoundRectangle(Rectangle, int, int) + */ +public void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight) { + checkFill(); + gc.fillRoundRectangle(r.x + translateX, r.y + translateY, r.width, r.height, + arcWidth, arcHeight); +} + +/** + * @see Graphics#fillString(String, int, int) + */ +public void fillString(String s, int x, int y) { + checkText(); + gc.drawString(s, x + translateX, y + translateY, false); +} + +/** + * @see Graphics#fillText(String, int, int) + */ +public void fillText(String s, int x, int y) { + checkText(); + gc.drawText(s, x + translateX, y + translateY, false); +} + +/** + * @see Graphics#getAlpha() + */ +public int getAlpha() { + return currentState.alpha; +} + +/** + * @see Graphics#getAntialias() + */ +public int getAntialias() { + return ((currentState.graphicHints & AA_MASK) >> AA_SHIFT) - AA_WHOLE_NUMBER; +} + +/** + * @see Graphics#getBackgroundColor() + */ +public Color getBackgroundColor() { + return currentState.bgColor; +} + +/** + * @see Graphics#getClip(Rectangle) + */ +public Rectangle getClip(Rectangle rect) { + if (currentState.relativeClip !is null) { + currentState.relativeClip.getBoundingBox(rect); + return rect; + } + throw new IllegalStateException( + "Clipping can no longer be queried due to transformations"); //$NON-NLS-1$ +} + +/** + * @see Graphics#getFillRule() + */ +public int getFillRule() { + return ((currentState.graphicHints & FILL_RULE_MASK) >> FILL_RULE_SHIFT) - FILL_RULE_WHOLE_NUMBER; +} + +/** + * @see Graphics#getFont() + */ +public Font getFont() { + return currentState.font; +} + +/** + * @see Graphics#getFontMetrics() + */ +public FontMetrics getFontMetrics() { + checkText(); + return gc.getFontMetrics(); +} + +/** + * @see Graphics#getForegroundColor() + */ +public Color getForegroundColor() { + return currentState.fgColor; +} + +/** + * @see Graphics#getInterpolation() + */ +public int getInterpolation() { + return ((currentState.graphicHints & INTERPOLATION_MASK) >> INTERPOLATION_SHIFT) + - INTERPOLATION_WHOLE_NUMBER; +} + +/** + * @see Graphics#getLineCap() + */ +public int getLineCap() { + return (currentState.graphicHints & CAP_MASK) >> CAP_SHIFT; +} + +/** + * @see Graphics#getLineJoin() + */ +public int getLineJoin() { + return (currentState.graphicHints & JOIN_MASK) >> JOIN_SHIFT; +} + +/** + * @see Graphics#getLineStyle() + */ +public int getLineStyle() { + return currentState.graphicHints & LINE_STYLE_MASK; +} + +/** + * @see Graphics#getLineWidth() + */ +public int getLineWidth() { + return currentState.lineWidth; +} + +/** + * @see Graphics#getTextAntialias() + */ +public int getTextAntialias() { + return ((currentState.graphicHints & TEXT_AA_MASK) >> TEXT_AA_SHIFT) - AA_WHOLE_NUMBER; +} + +/** + * @see Graphics#getXORMode() + */ +public bool getXORMode() { + return (currentState.graphicHints & XOR_MASK) !is 0; +} + +/** + * Called by constructor, initializes all State information for currentState + */ +protected void init() { + currentState.bgColor = appliedState.bgColor = gc.getBackground(); + currentState.fgColor = appliedState.fgColor = gc.getForeground(); + currentState.font = appliedState.font = gc.getFont(); + currentState.lineWidth = appliedState.lineWidth = gc.getLineWidth(); + currentState.graphicHints |= gc.getLineStyle(); + currentState.graphicHints |= gc.getLineCap() << CAP_SHIFT; + currentState.graphicHints |= gc.getLineJoin() << JOIN_SHIFT; + currentState.graphicHints |= gc.getAdvanced() ? ADVANCED_GRAPHICS_MASK : 0; + currentState.graphicHints |= gc.getXORMode() ? XOR_MASK : 0; + + appliedState.graphicHints = currentState.graphicHints; + + currentState.relativeClip = new RectangleClipping(gc.getClipping()); + currentState.lineDash = gc.getLineDash(); + currentState.alpha = gc.getAlpha(); +} + +private void initTransform(bool force) { + if (!force && translateX is 0 && translateY is 0) + return; + if (transform is null) { + transform = new Transform(Display.getCurrent()); + elementsNeedUpdate = true; + transform.translate(translateX, translateY); + translateX = 0; + translateY = 0; + gc.setTransform(transform); + currentState.graphicHints |= ADVANCED_GRAPHICS_MASK; + } +} + +/** + * @see Graphics#popState() + */ +public void popState() { + stackPointer--; + restoreState(cast(State)stack.get(stackPointer)); +} + +/** + * @see Graphics#pushState() + */ +public void pushState() { + if (currentState.relativeClip is null) + throw new IllegalStateException("The clipping has been modified in" //$NON-NLS-1$ + "a way that cannot be saved and restored."); //$NON-NLS-1$ +// try { + State s; + currentState.dx = translateX; + currentState.dy = translateY; + + if (elementsNeedUpdate) { + elementsNeedUpdate = false; + transform.getElements(currentState.affineMatrix = new float[6]); + } + if (stack.size() > stackPointer) { + s = cast(State)stack.get(stackPointer); + s.copyFrom(currentState); + } else { + stack.add(currentState.clone()); + } + sharedClipping = true; + stackPointer++; +// } catch (CloneNotSupportedException e) { +// throw new RuntimeException(e); +// } +} + +private void reconcileHints(int applied, int hints) { + if (applied !is hints) { + int changes = hints ^ applied; + + if ((changes & LINE_STYLE_MASK) !is 0) + gc.setLineStyle(hints & LINE_STYLE_MASK); + + if ((changes & XOR_MASK) !is 0) + gc.setXORMode((hints & XOR_MASK) !is 0); + + //Check to see if there is anything remaining + changes &= ~(XOR_MASK | LINE_STYLE_MASK); + if (changes is 0) + return; + + if ((changes & JOIN_MASK) !is 0) + gc.setLineJoin((hints & JOIN_MASK) >> JOIN_SHIFT); + if ((changes & CAP_MASK) !is 0) + gc.setLineCap((hints & CAP_MASK) >> CAP_SHIFT); + + if ((changes & INTERPOLATION_MASK) !is 0) + gc.setInterpolation(((hints & INTERPOLATION_MASK) >> INTERPOLATION_SHIFT) - INTERPOLATION_WHOLE_NUMBER); + + if ((changes & FILL_RULE_MASK) !is 0) + gc.setFillRule(((hints & FILL_RULE_MASK) >> FILL_RULE_SHIFT) - FILL_RULE_WHOLE_NUMBER); + + if ((changes & AA_MASK) !is 0) + gc.setAntialias(((hints & AA_MASK) >> AA_SHIFT) - AA_WHOLE_NUMBER); + if ((changes & TEXT_AA_MASK) !is 0) + gc.setTextAntialias(((hints & TEXT_AA_MASK) >> TEXT_AA_SHIFT) - AA_WHOLE_NUMBER); + + // If advanced was flagged, but none of the conditions which trigger advanced + // actually got applied, force advanced graphics on. + if ((changes & ADVANCED_GRAPHICS_MASK) !is 0) { + if ((hints & ADVANCED_GRAPHICS_MASK) !is 0 && !gc.getAdvanced()) + gc.setAdvanced(true); + } + } +} + +/** + * @see Graphics#restoreState() + */ +public void restoreState() { + restoreState(cast(State)stack.get(stackPointer - 1)); +} + +/** + * Sets all State information to that of the given State, called by restoreState() + * @param s the State + */ +protected void restoreState(State s) { + /* + * We must set the transformation matrix first since it affects things like clipping + * regions and patterns. + */ + setAffineMatrix(s.affineMatrix); + currentState.relativeClip = s.relativeClip; + sharedClipping = true; + + //If the GC is currently advanced, but it was not when pushed, revert + if (gc.getAdvanced() && (s.graphicHints & ADVANCED_GRAPHICS_MASK) is 0) { + //Set applied clip to null to force a re-setting of the clipping. + appliedState.relativeClip = null; + gc.setAdvanced(false); + appliedState.graphicHints &= ~ADVANCED_HINTS_MASK; + appliedState.graphicHints |= ADVANCED_HINTS_DEFAULTS; + } + + setBackgroundColor(s.bgColor); + setBackgroundPattern(s.bgPattern); + + setForegroundColor(s.fgColor); + setForegroundPattern(s.fgPattern); + + setAlpha(s.alpha); + setLineWidth(s.lineWidth); + setFont(s.font); + //This method must come last because above methods will incorrectly set advanced state + setGraphicHints(s.graphicHints); + + translateX = currentState.dx = s.dx; + translateY = currentState.dy = s.dy; +} + +/** + * This method requires advanced graphics support. A check should be made to + * ensure advanced graphics is supported in the user's environment before + * calling this method. See {@link GC#getAdvanced()}. + * + * @see Graphics#rotate(float) + */ +public void rotate(float degrees) { + //Flush clipping, patter, etc., before applying transform + checkGC(); + initTransform(true); + transform.rotate(degrees); + gc.setTransform(transform); + elementsNeedUpdate = true; + + //Can no longer operate or maintain clipping + appliedState.relativeClip = currentState.relativeClip = null; +} + +/** + * @see Graphics#scale(double) + */ +public void scale(double factor) { + scale(cast(float)factor, cast(float)factor); +} + +/** + * This method requires advanced graphics support. A check should be made to + * ensure advanced graphics is supported in the user's environment before + * calling this method. See {@link GC#getAdvanced()}. + * + * @see dwtx.draw2d.Graphics#scale(float, float) + */ +public void scale(float horizontal, float vertical) { + //Flush any clipping before scaling + checkGC(); + + initTransform(true); + transform.scale(horizontal, vertical); + gc.setTransform(transform); + elementsNeedUpdate = true; + + checkSharedClipping(); + if (currentState.relativeClip !is null) + currentState.relativeClip.scale(horizontal, vertical); +} + +private void setAffineMatrix(float[] m) { + if (!elementsNeedUpdate && currentState.affineMatrix is m) + return; + currentState.affineMatrix = m; + if (m !is null) + transform.setElements(m[0], m[1], m[2], m[3], m[4], m[5]); + else if (transform !is null) { + transform.dispose(); + transform = null; + elementsNeedUpdate = false; + } + gc.setTransform(transform); +} + +/** + * This method requires advanced graphics support. A check should be made to + * ensure advanced graphics is supported in the user's environment before + * calling this method. See {@link GC#getAdvanced()}. + * + * @see Graphics#setAlpha(int) + */ +public void setAlpha(int alpha) { + currentState.graphicHints |= ADVANCED_GRAPHICS_MASK; + if (currentState.alpha !is alpha) + gc.setAlpha(this.currentState.alpha = alpha); +} + +/** + * This method requires advanced graphics support. A check should be made to + * ensure advanced graphics is supported in the user's environment before + * calling this method. See {@link GC#getAdvanced()}. + * + * @see Graphics#setAntialias(int) + */ +public void setAntialias(int value) { + currentState.graphicHints &= ~AA_MASK; + currentState.graphicHints |= ADVANCED_GRAPHICS_MASK | (value + AA_WHOLE_NUMBER) << AA_SHIFT; +} + +/** + * @see Graphics#setBackgroundColor(Color) + */ +public void setBackgroundColor(Color color) { + currentState.bgColor = color; + if (currentState.bgPattern !is null) { + currentState.bgPattern = null; + //Force the BG color to be stale + appliedState.bgColor = null; + } +} + +/** + * @see Graphics#setBackgroundPattern(Pattern) + */ +public void setBackgroundPattern(Pattern pattern) { + currentState.graphicHints |= ADVANCED_GRAPHICS_MASK; + if (currentState.bgPattern is pattern) + return; + currentState.bgPattern = pattern; + + if (pattern !is null) + initTransform(true); + gc.setBackgroundPattern(pattern); +} + +/** + * This method requires advanced graphics support. A check should be made to + * ensure advanced graphics is supported in the user's environment before + * calling this method. See {@link GC#getAdvanced()}. + * + * @see Graphics#setClip(Path) + */ +public void setClip(Path path) { + initTransform(false); + gc.setClipping(path); + appliedState.relativeClip = currentState.relativeClip = null; +} + +/** + * @see Graphics#setClip(Rectangle) + */ +public void setClip(Rectangle rect) { + currentState.relativeClip = new RectangleClipping(rect); +} + +/** + * @see Graphics#setFillRule(int) + */ +public void setFillRule(int rule) { + currentState.graphicHints &= ~FILL_RULE_MASK; + currentState.graphicHints |= (rule + FILL_RULE_WHOLE_NUMBER) << FILL_RULE_SHIFT; +} + +/** + * @see Graphics#setFont(Font) + */ +public void setFont(Font f) { + currentState.font = f; +} + +/** + * @see Graphics#setForegroundColor(Color) + */ +public void setForegroundColor(Color color) { + currentState.fgColor = color; + if (currentState.fgPattern !is null) { + currentState.fgPattern = null; + //Force fgColor to be stale + appliedState.fgColor = null; + } +} + +/** + * @see Graphics#setForegroundPattern(Pattern) + */ +public void setForegroundPattern(Pattern pattern) { + currentState.graphicHints |= ADVANCED_GRAPHICS_MASK; + if (currentState.fgPattern is pattern) + return; + currentState.fgPattern = pattern; + + if (pattern !is null) + initTransform(true); + gc.setForegroundPattern(pattern); +} + +private void setGraphicHints(int hints) { + currentState.graphicHints = hints; +} + +/** + * This method requires advanced graphics support. A check should be made to + * ensure advanced graphics is supported in the user's environment before + * calling this method. See {@link GC#getAdvanced()}. + * + * @see Graphics#setInterpolation(int) + */ +public void setInterpolation(int interpolation) { + //values range [-1, 3] + currentState.graphicHints &= ~INTERPOLATION_MASK; + currentState.graphicHints |= ADVANCED_GRAPHICS_MASK | (interpolation + INTERPOLATION_WHOLE_NUMBER) << INTERPOLATION_SHIFT; +} + +/** + * @see Graphics#setLineCap(int) + */ +public void setLineCap(int cap) { + currentState.graphicHints &= ~CAP_MASK; + currentState.graphicHints |= cap << CAP_SHIFT; +} + +/** + * @see Graphics#setLineDash(int[]) + */ +public void setLineDash(int[] dashes) { + if (dashes !is null) { + int copy[] = new int[dashes.length]; + for (int i = 0; i < dashes.length; i++) { + int dash = dashes[i]; + if (dash <= 0) + DWT.error(DWT.ERROR_INVALID_ARGUMENT); + copy[i] = dash; + } + currentState.lineDash = copy; + setLineStyle(DWT.LINE_CUSTOM); + gc.setLineDash(copy); + } else { + currentState.lineDash = null; + setLineStyle(DWT.LINE_SOLID); + } +} + +/** + * @see Graphics#setLineJoin(int) + */ +public void setLineJoin(int join) { + currentState.graphicHints &= ~JOIN_MASK; + currentState.graphicHints |= join << JOIN_SHIFT; +} + +/** + * @see Graphics#setLineStyle(int) + */ +public void setLineStyle(int style) { + currentState.graphicHints &= ~LINE_STYLE_MASK; + currentState.graphicHints |= style; +} + +/** + * @see Graphics#setLineWidth(int) + */ +public void setLineWidth(int width) { + currentState.lineWidth = width; +} + +/** + * This method requires advanced graphics support. A check should be made to + * ensure advanced graphics is supported in the user's environment before + * calling this method. See {@link GC#getAdvanced()}. + * + * @see Graphics#setTextAntialias(int) + */ +public void setTextAntialias(int value) { + currentState.graphicHints &= ~TEXT_AA_MASK; + currentState.graphicHints |= ADVANCED_GRAPHICS_MASK | (value + AA_WHOLE_NUMBER) << TEXT_AA_SHIFT; +} + +/** + * @see Graphics#setXORMode(bool) + */ +public void setXORMode(bool xor) { + currentState.graphicHints &= ~XOR_MASK; + if (xor) + currentState.graphicHints |= XOR_MASK; +} + +/** + * This method requires advanced graphics support. A check should be made to + * ensure advanced graphics is supported in the user's environment before + * calling this method. See {@link GC#getAdvanced()}. + * + * @see Graphics#shear(float, float) + */ +public void shear(float horz, float vert) { + //Flush any clipping changes before shearing + checkGC(); + initTransform(true); + float matrix[] = new float[6]; + transform.getElements(matrix); + transform.setElements( + matrix[0] + matrix[2] * vert, + matrix[1] + matrix[3] * vert, + matrix[0] * horz + matrix[2], + matrix[1] * horz + matrix[3], + matrix[4], + matrix[5]); + + gc.setTransform(transform); + elementsNeedUpdate = true; + //Can no longer track clipping changes + appliedState.relativeClip = currentState.relativeClip = null; +} + +/** + * This method may require advanced graphics support if using a transform, + * in this case, a check should be made to ensure advanced graphics is + * supported in the user's environment before calling this method. See + * {@link GC#getAdvanced()}. + * + * @see Graphics#translate(int, int) + */ +public void translate(int dx, int dy) { + if (dx is 0 && dy is 0) + return; + if (transform !is null) { + //Flush clipping, pattern, etc. before applying transform + checkGC(); + transform.translate(dx, dy); + elementsNeedUpdate = true; + gc.setTransform(transform); + } else { + translateX += dx; + translateY += dy; + } + checkSharedClipping(); + if (currentState.relativeClip !is null) + currentState.relativeClip.translate(-dx, -dy); +} + +/** + * This method requires advanced graphics support. A check should be made to + * ensure advanced graphics is supported in the user's environment before + * calling this method. See {@link GC#getAdvanced()}. + * + * @see Graphics#translate(float, float) + */ +public void translate(float dx, float dy) { + initTransform(true); + checkGC(); + transform.translate(dx, dy); + elementsNeedUpdate = true; + gc.setTransform(transform); + checkSharedClipping(); + if (currentState.relativeClip !is null) + currentState.relativeClip.translate(-dx, -dy); +} + +private void translatePointArray(int[] points, int translateX, int translateY) { + if (translateX is 0 && translateY is 0) + return; + for (int i = 0; (i + 1) < points.length; i += 2) { + points[i] += translateX; + points[i + 1] += translateY; + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ScalableFigure.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ScalableFigure.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ScalableFigure; + +import dwt.dwthelper.utils; +import dwtx.draw2d.IFigure; + +/** + * A figure that can be scaled. + * @author Eric Bordeau + * @since 2.1.1 + */ +public interface ScalableFigure : IFigure { + +/** + * Returns the current scale. + * @return the current scale + */ +double getScale(); + +/** + * Sets the new scale factor. + * @param scale the scale + */ +void setScale(double scale); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ScalableFreeformLayeredPane.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ScalableFreeformLayeredPane.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ScalableFreeformLayeredPane; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.Translatable; +import dwtx.draw2d.FreeformLayeredPane; +import dwtx.draw2d.ScalableFigure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.ScaledGraphics; + +/** + * @author hudsonr + * @since 2.1 + */ +public class ScalableFreeformLayeredPane + : FreeformLayeredPane + , ScalableFigure +{ + +private double scale = 1.0; + +/** + * @see dwtx.draw2d.Figure#getClientArea() + */ +public Rectangle getClientArea(Rectangle rect) { + super.getClientArea(rect); + rect.width /= scale; + rect.height /= scale; + rect.x /= scale; + rect.y /= scale; + return rect; +} + +/** + * Returns the current zoom scale level. + * @return the scale + */ +public double getScale() { + return scale; +} + +/** + * @see dwtx.draw2d.IFigure#isCoordinateSystem() + */ +public bool isCoordinateSystem() { + return true; +} + +/** + * @see dwtx.draw2d.Figure#paintClientArea(Graphics) + */ +protected void paintClientArea(Graphics graphics) { + if (getChildren().isEmpty()) + return; + if (scale is 1.0) { + super.paintClientArea(graphics); + } else { + ScaledGraphics g = new ScaledGraphics(graphics); + bool optimizeClip = getBorder() is null || getBorder().isOpaque(); + if (!optimizeClip) + g.clipRect(getBounds().getCropped(getInsets())); + g.scale(scale); + g.pushState(); + paintChildren(g); + g.dispose(); + graphics.restoreState(); + } +} + +/** + * Sets the zoom level + * @param newZoom The new zoom level + */ +public void setScale(double newZoom) { + if (scale is newZoom) + return; + scale = newZoom; + superFireMoved(); //For AncestorListener compatibility + getFreeformHelper().invalidate(); + repaint(); +} + +/** + * @see dwtx.draw2d.Figure#translateToParent(Translatable) + */ +public void translateToParent(Translatable t) { + t.performScale(scale); +} + +/** + * @see dwtx.draw2d.Figure#translateFromParent(Translatable) + */ +public void translateFromParent(Translatable t) { + t.performScale(1 / scale); +} + +/** + * @see dwtx.draw2d.Figure#useLocalCoordinates() + */ +protected final bool useLocalCoordinates() { + return false; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ScalableLayeredPane.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ScalableLayeredPane.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ScalableLayeredPane; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.Translatable; +import dwtx.draw2d.LayeredPane; +import dwtx.draw2d.ScalableFigure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.ScaledGraphics; + +/** + * A non-freeform, scalable layered pane. + * @author Eric Bordeau + * @since 2.1.1 + */ +public class ScalableLayeredPane + : LayeredPane + , ScalableFigure +{ + +private double scale = 1.0; + +/** + * @see IFigure#getClientArea(Rectangle) + */ +public Rectangle getClientArea(Rectangle rect) { + super.getClientArea(rect); + rect.width /= scale; + rect.height /= scale; + rect.x /= scale; + rect.y /= scale; + return rect; +} + +/** + * @see Figure#getPreferredSize(int, int) + */ +public Dimension getMinimumSize(int wHint, int hHint) { + Dimension d = super.getMinimumSize(cast(int) (wHint / getScale()), cast(int)(hHint / getScale())); + int w = getInsets().getWidth(); + int h = getInsets().getHeight(); + return d.getExpanded(-w, -h) + .scale(scale) + .expand(w, h); +} + +/** + * @see Figure#getPreferredSize(int, int) + */ +public Dimension getPreferredSize(int wHint, int hHint) { + Dimension d = super.getPreferredSize(cast(int) (wHint / getScale()), cast(int)(hHint / getScale())); + int w = getInsets().getWidth(); + int h = getInsets().getHeight(); + return d.getExpanded(-w, -h) + .scale(scale) + .expand(w, h); +} + +/** + * Returns the scale level, default is 1.0. + * @return the scale level + */ +public double getScale() { + return scale; +} + +/** + * @see dwtx.draw2d.IFigure#isCoordinateSystem() + */ +public bool isCoordinateSystem() { + return true; +} + +/** + * @see dwtx.draw2d.Figure#paintClientArea(Graphics) + */ +protected void paintClientArea(Graphics graphics) { + if (getChildren().isEmpty()) + return; + if (scale is 1.0) { + super.paintClientArea(graphics); + } else { + ScaledGraphics g = new ScaledGraphics(graphics); + bool optimizeClip = getBorder() is null || getBorder().isOpaque(); + if (!optimizeClip) + g.clipRect(getBounds().getCropped(getInsets())); + g.scale(scale); + g.pushState(); + paintChildren(g); + g.dispose(); + graphics.restoreState(); + } +} + +/** + * Sets the zoom level + * @param newZoom The new zoom level + */ +public void setScale(double newZoom) { + if (scale is newZoom) + return; + scale = newZoom; + fireMoved(); //for AncestorListener compatibility + revalidate(); + repaint(); +} + +/** + * @see dwtx.draw2d.Figure#translateFromParent(Translatable) + */ +public void translateFromParent(Translatable t) { + t.performScale(1 / scale); +} + +/** + * @see dwtx.draw2d.Figure#translateToParent(Translatable) + */ +public void translateToParent(Translatable t) { + t.performScale(scale); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ScaledGraphics.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ScaledGraphics.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,885 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.ScaledGraphics; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwt.DWT; +import dwt.graphics.Color; +import dwt.graphics.Font; +import dwt.graphics.FontData; +import dwt.graphics.FontMetrics; +import dwt.graphics.Image; +import dwt.graphics.Path; +import dwt.graphics.PathData; +import dwt.graphics.TextLayout; +import dwt.graphics.TextStyle; +import dwt.widgets.Display; +static import dwt.graphics.Rectangle; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.FigureUtilities; + +/** + * A Graphics object able to scale all operations based on the current scale factor. + */ +public class ScaledGraphics + : Graphics +{ +alias Graphics.translate translate; +alias Graphics.fillRectangle fillRectangle; + +private static class FontHeightCache { + Font font; + int height; +} + +static class FontKey { + Font font; + int height; + protected this() { } + protected this(Font font, int height) { + this.font = font; + this.height = height; + } + + public override int opEquals(Object obj) { + return ((cast(FontKey)obj).font.opEquals(font) + && (cast(FontKey)obj).height is height); + } + + public override hash_t toHash() { + return font.toHash() ^ height; + } + + protected void setValues(Font font, int height) { + this.font = font; + this.height = height; + } +} + +/** + * The internal state of the scaled graphics. + */ +protected static class State { + private double appliedX; + private double appliedY; + private Font font; + private int lineWidth; + private double zoom; + + /** + * Constructs a new, uninitialized State object. + */ + protected this() { } + + /** + * Constructs a new State object and initializes the properties based on the given + * values. + * + * @param zoom the zoom factor + * @param x the x offset + * @param y the y offset + * @param font the font + * @param lineWidth the line width + */ + protected this(double zoom, double x, double y, Font font, int lineWidth) { + this.zoom = zoom; + this.appliedX = x; + this.appliedY = y; + this.font = font; + this.lineWidth = lineWidth; + } + + /** + * Sets all the properties of the state object. + * @param zoom the zoom factor + * @param x the x offset + * @param y the y offset + * @param font the font + * @param lineWidth the line width + */ + protected void setValues(double zoom, double x, double y, + Font font, int lineWidth) { + this.zoom = zoom; + this.appliedX = x; + this.appliedY = y; + this.font = font; + this.lineWidth = lineWidth; + } +} + +private static int[][] intArrayCache; +private final Rectangle tempRECT; + +static this(){ + intArrayCache = new int[][](8); + for (int i = 0; i < intArrayCache.length; i++) + intArrayCache[i] = new int[i + 1]; +} +private void instanceInit(){ + tempRECT = new Rectangle(); + localCache = new FontHeightCache(); + targetCache = new FontHeightCache(); + stack = new ArrayList(); + fontKey = new FontKey(); + fontDataCache = new HashMap(); + fontCache = new HashMap(); +} +private bool allowText = true; +//private static final Point PT = new Point(); +private Map fontCache; +private Map fontDataCache; +private FontKey fontKey; +private double fractionalX; +private double fractionalY; +private Graphics graphics; +private FontHeightCache localCache; +private Font localFont; +private int localLineWidth; +private List stack; +private int stackPointer = 0; +private FontHeightCache targetCache; + +double zoom = 1.0; + +/** + * Constructs a new ScaledGraphics based on the given Graphics object. + * @param g the base graphics object + */ +public this(Graphics g) { + instanceInit(); + graphics = g; + localFont = g.getFont(); + localLineWidth = g.getLineWidth(); +} + +/** @see Graphics#clipRect(Rectangle) */ +public void clipRect(Rectangle r) { + graphics.clipRect(zoomClipRect(r)); +} + +Font createFont(FontData data) { + return new Font(Display.getCurrent(), data); +} + +private Path createScaledPath(Path path) { + PathData p = path.getPathData(); + for (int i = 0; i < p.points.length; i += 2) { + p.points[i] = cast(float) (p.points[i] * zoom + fractionalX); + p.points[i + 1] = cast(float) (p.points[i + 1] * zoom + fractionalY); + } + Path scaledPath = new Path(path.getDevice()); + int index = 0; + for (int i = 0; i < p.types.length; i++) { + byte type = p.types[i]; + switch (type) { + case DWT.PATH_MOVE_TO: + scaledPath.moveTo(p.points[index], p.points[index + 1]); + index += 2; + break; + case DWT.PATH_LINE_TO: + scaledPath.lineTo(p.points[index], p.points[index + 1]); + index += 2; + break; + case DWT.PATH_CUBIC_TO: + scaledPath.cubicTo(p.points[index], p.points[index + 1], + p.points[index + 2], p.points[index + 3], p.points[index + 4], + p.points[index + 5]); + index += 6; + break; + case DWT.PATH_QUAD_TO: + scaledPath.quadTo(p.points[index], p.points[index + 1], + p.points[index + 2], p.points[index + 3]); + index += 4; + break; + case DWT.PATH_CLOSE: + scaledPath.close(); + break; + } + } + return scaledPath; +} + +/** @see Graphics#dispose() */ +public void dispose() { + //Remove all states from the stack + while (stackPointer > 0) { + popState(); + } + + //Dispose fonts + Iterator iter = fontCache.values().iterator(); + while (iter.hasNext()) { + Font font = (cast(Font)iter.next()); + font.dispose(); + } + +} + +/** @see Graphics#drawArc(int, int, int, int, int, int) */ +public void drawArc(int x, int y, int w, int h, int offset, int sweep) { + Rectangle z = zoomRect(x, y, w, h); + if (z.isEmpty() || sweep is 0) + return; + graphics.drawArc(z, offset, sweep); +} + +/** @see Graphics#drawFocus(int, int, int, int) */ +public void drawFocus(int x, int y, int w, int h) { + graphics.drawFocus(zoomRect(x, y, w, h)); +} + +/** @see Graphics#drawImage(Image, int, int) */ +public void drawImage(Image srcImage, int x, int y) { + dwt.graphics.Rectangle.Rectangle size = srcImage.getBounds(); + graphics.drawImage(srcImage, 0, 0, size.width, size.height, + cast(int)(Math.floor((x * zoom + fractionalX))), + cast(int)(Math.floor((y * zoom + fractionalY))), + cast(int)(Math.floor((size.width * zoom + fractionalX))), + cast(int)(Math.floor((size.height * zoom + fractionalY)))); +} + +/** @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int) */ +public void drawImage(Image srcImage, int sx, int sy, int sw, int sh, + int tx, int ty, int tw, int th) { + //"t" is target rectangle, "s" = source + + Rectangle t = zoomRect(tx, ty, tw, th); + if (!t.isEmpty()) + graphics.drawImage(srcImage, sx, sy, sw, sh, t.x, t.y, t.width, t.height); +} + +/** @see Graphics#drawLine(int, int, int, int) */ +public void drawLine(int x1, int y1, int x2, int y2) { + graphics.drawLine( + cast(int)(Math.floor((x1 * zoom + fractionalX))), + cast(int)(Math.floor((y1 * zoom + fractionalY))), + cast(int)(Math.floor((x2 * zoom + fractionalX))), + cast(int)(Math.floor((y2 * zoom + fractionalY)))); +} + +/** @see Graphics#drawOval(int, int, int, int) */ +public void drawOval(int x, int y, int w, int h) { + graphics.drawOval(zoomRect(x, y, w, h)); +} + +/** @see Graphics#drawPath(Path) */ +public void drawPath(Path path) { + Path scaledPath = createScaledPath(path); + try { + graphics.drawPath(scaledPath); + } finally { + scaledPath.dispose(); + } +} + +/** @see Graphics#drawPoint(int, int) */ +public void drawPoint(int x, int y) { + graphics.drawPoint(cast(int)Math.floor(x * zoom + fractionalX), + cast(int)Math.floor(y * zoom + fractionalY)); +} + +/** + * @see Graphics#drawPolygon(int[]) + */ +public void drawPolygon(int[] points) { + graphics.drawPolygon(zoomPointList(points)); +} + +/** @see Graphics#drawPolygon(PointList) */ +public void drawPolygon(PointList points) { + graphics.drawPolygon(zoomPointList(points.toIntArray())); +} + +/** + * @see Graphics#drawPolyline(int[]) + */ +public void drawPolyline(int[] points) { + graphics.drawPolyline(zoomPointList(points)); +} + +/** @see Graphics#drawPolyline(PointList) */ +public void drawPolyline(PointList points) { + graphics.drawPolyline(zoomPointList(points.toIntArray())); +} + +/** @see Graphics#drawRectangle(int, int, int, int) */ +public void drawRectangle(int x, int y, int w, int h) { + graphics.drawRectangle(zoomRect(x, y, w, h)); +} + +/** @see Graphics#drawRoundRectangle(Rectangle, int, int) */ +public void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight) { + graphics.drawRoundRectangle(zoomRect(r.x, r.y, r.width, r.height), + cast(int)(arcWidth * zoom), + cast(int)(arcHeight * zoom)); +} + +/** @see Graphics#drawString(String, int, int) */ +public void drawString(String s, int x, int y) { + if (allowText) + graphics.drawString(s, zoomTextPoint(x, y)); +} + +/** @see Graphics#drawText(String, int, int) */ +public void drawText(String s, int x, int y) { + if (allowText) + graphics.drawText(s, zoomTextPoint(x, y)); +} + +/** + * @see Graphics#drawText(String, int, int, int) + */ +public void drawText(String s, int x, int y, int style) { + if (allowText) + graphics.drawText(s, zoomTextPoint(x, y), style); +} + +/** + * @see Graphics#drawTextLayout(TextLayout, int, int, int, int, Color, Color) + */ +public void drawTextLayout(TextLayout layout, int x, int y, int selectionStart, + int selectionEnd, Color selectionForeground, Color selectionBackground) { + TextLayout scaled = zoomTextLayout(layout); + graphics.drawTextLayout(scaled, + cast(int)Math.floor(x * zoom + fractionalX), + cast(int)Math.floor(y * zoom + fractionalY), + selectionStart, selectionEnd, selectionBackground, selectionForeground); + scaled.dispose(); +} + +/** @see Graphics#fillArc(int, int, int, int, int, int) */ +public void fillArc(int x, int y, int w, int h, int offset, int sweep) { + Rectangle z = zoomFillRect(x, y, w, h); + if (z.isEmpty() || sweep is 0) + return; + graphics.fillArc(z, offset, sweep); +} + +/** @see Graphics#fillGradient(int, int, int, int, bool) */ +public void fillGradient(int x, int y, int w, int h, bool vertical) { + graphics.fillGradient(zoomFillRect(x, y, w, h), vertical); +} + +/** @see Graphics#fillOval(int, int, int, int) */ +public void fillOval(int x, int y, int w, int h) { + graphics.fillOval(zoomFillRect(x, y, w, h)); +} + +/** @see Graphics#fillPath(Path) */ +public void fillPath(Path path) { + Path scaledPath = createScaledPath(path); + try { + graphics.fillPath(scaledPath); + } finally { + scaledPath.dispose(); + } +} + +/** + * @see Graphics#fillPolygon(int[]) + */ +public void fillPolygon(int[] points) { + graphics.fillPolygon(zoomPointList(points)); +} + +/** @see Graphics#fillPolygon(PointList) */ +public void fillPolygon(PointList points) { + graphics.fillPolygon(zoomPointList(points.toIntArray())); +} + +/** @see Graphics#fillRectangle(int, int, int, int) */ +public void fillRectangle(int x, int y, int w, int h) { + graphics.fillRectangle(zoomFillRect(x, y, w, h)); +} + +/** @see Graphics#fillRoundRectangle(Rectangle, int, int) */ +public void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight) { + graphics.fillRoundRectangle(zoomFillRect(r.x, r.y, r.width, r.height), + cast(int)(arcWidth * zoom), + cast(int)(arcHeight * zoom)); +} + +/** @see Graphics#fillString(String, int, int) */ +public void fillString(String s, int x, int y) { + if (allowText) + graphics.fillString(s, zoomTextPoint(x, y)); +} + +/** @see Graphics#fillText(String, int, int) */ +public void fillText(String s, int x, int y) { + if (allowText) + graphics.fillText(s, zoomTextPoint(x, y)); +} + +/** + * @see Graphics#getAbsoluteScale() + */ +public double getAbsoluteScale() { + return zoom * graphics.getAbsoluteScale(); +} + +/** + * @see Graphics#getAlpha() + */ +public int getAlpha() { + return graphics.getAlpha(); +} + +/** + * @see Graphics#getAntialias() + */ +public int getAntialias() { + return graphics.getAntialias(); +} + +/** @see Graphics#getBackgroundColor() */ +public Color getBackgroundColor() { + return graphics.getBackgroundColor(); +} + +Font getCachedFont(FontKey key) { + Font font = cast(Font)fontCache.get(key); + if (font !is null) + return font; + key = new FontKey(key.font, key.height); + FontData data = key.font.getFontData()[0]; + data.setHeight(key.height); + Font zoomedFont = createFont(data); + fontCache.put(key, zoomedFont); + return zoomedFont; +} + +FontData getCachedFontData(Font f) { + FontData data = cast(FontData)fontDataCache.get(f); + if (data !is null) + return data; + data = getLocalFont().getFontData()[0]; + fontDataCache.put(f, data); + return data; +} + +/** @see Graphics#getClip(Rectangle) */ +public Rectangle getClip(Rectangle rect) { + graphics.getClip(rect); + int x = cast(int)(rect.x / zoom); + int y = cast(int)(rect.y / zoom); + /* + * If the clip rectangle is queried, perform an inverse zoom, and take the ceiling of + * the resulting double. This is necessary because forward scaling essentially performs + * a floor() function. Without this, figures will think that they don't need to paint + * when actually they do. + */ + rect.width = cast(int)Math.ceil(rect.right() / zoom) - x; + rect.height = cast(int)Math.ceil(rect.bottom() / zoom) - y; + rect.x = x; + rect.y = y; + return rect; +} + +/** + * @see Graphics#getFillRule() + */ +public int getFillRule() { + return graphics.getFillRule(); +} + +/** @see Graphics#getFont() */ +public Font getFont() { + return getLocalFont(); +} + +/** @see Graphics#getFontMetrics() */ +public FontMetrics getFontMetrics() { + return FigureUtilities.getFontMetrics(localFont); +} + +/** @see Graphics#getForegroundColor() */ +public Color getForegroundColor() { + return graphics.getForegroundColor(); +} + +/** + * @see Graphics#getInterpolation() + */ +public int getInterpolation() { + return graphics.getInterpolation(); +} + +/** + * @see Graphics#getLineCap() + */ +public int getLineCap() { + return graphics.getLineCap(); +} + +/** + * @see Graphics#getLineJoin() + */ +public int getLineJoin() { + return graphics.getLineJoin(); +} + +/** @see Graphics#getLineStyle() */ +public int getLineStyle() { + return graphics.getLineStyle(); +} + +/** @see Graphics#getLineWidth() */ +public int getLineWidth() { + return getLocalLineWidth(); +} + +private Font getLocalFont() { + return localFont; +} + +private int getLocalLineWidth() { + return localLineWidth; +} + +/** + * @see Graphics#getTextAntialias() + */ +public int getTextAntialias() { + return graphics.getTextAntialias(); +} + +/** @see Graphics#getXORMode() */ +public bool getXORMode() { + return graphics.getXORMode(); +} + +/** @see Graphics#popState() */ +public void popState() { + graphics.popState(); + stackPointer--; + restoreLocalState(cast(State)stack.get(stackPointer)); +} + +/** @see Graphics#pushState() */ +public void pushState() { + State s; + if (stack.size() > stackPointer) { + s = cast(State)stack.get(stackPointer); + s.setValues(zoom, fractionalX, fractionalY, getLocalFont(), localLineWidth); + } else { + stack.add(new State(zoom, fractionalX, fractionalY, getLocalFont(), + localLineWidth)); + } + stackPointer++; + + graphics.pushState(); +} + +private void restoreLocalState(State state) { + this.fractionalX = state.appliedX; + this.fractionalY = state.appliedY; + setScale(state.zoom); + setLocalFont(state.font); + setLocalLineWidth(state.lineWidth); +} + +/** @see Graphics#restoreState() */ +public void restoreState() { + graphics.restoreState(); + restoreLocalState(cast(State)stack.get(stackPointer - 1)); +} + +/** @see Graphics#scale(double) */ +public void scale(double amount) { + setScale(zoom * amount); +} + +/** + * @see Graphics#setAlphacast(int) + */ +public void setAlpha(int alpha) { + graphics.setAlpha(alpha); +} + +/** + * @see Graphics#setAntialiascast(int) + */ +public void setAntialias(int value) { + graphics.setAntialias(value); +} + +/** @see Graphics#setBackgroundColor(Color) */ +public void setBackgroundColor(Color rgb) { + graphics.setBackgroundColor(rgb); +} + +/** @see Graphics#setClip(Path) */ +public void setClip(Path path) { + Path scaledPath = createScaledPath(path); + try { + graphics.setClip(scaledPath); + } finally { + scaledPath.dispose(); + } +} + +/** @see Graphics#setClip(Rectangle) */ +public void setClip(Rectangle r) { + graphics.setClip(zoomClipRect(r)); +} + +/** + * @see Graphics#setFillRulecast(int) + */ +public void setFillRule(int rule) { + graphics.setFillRule(rule); +} + +/** @see Graphics#setFontcast(Font) */ +public void setFont(Font f) { + setLocalFont(f); +} + +/** @see Graphics#setForegroundColor(Color) */ +public void setForegroundColor(Color rgb) { + graphics.setForegroundColor(rgb); +} + +/** + * @see dwtx.draw2d.Graphics#setInterpolationcast(int) + */ +public void setInterpolation(int interpolation) { + graphics.setInterpolation(interpolation); +} + +/** + * @see Graphics#setLineCapcast(int) + */ +public void setLineCap(int cap) { + graphics.setLineCap(cap); +} + +/** + * @see Graphics#setLineDash(int[]) + */ +public void setLineDash(int[] dash) { + graphics.setLineDash(dash); +} + +/** + * @see Graphics#setLineJoincast(int) + */ +public void setLineJoin(int join) { + graphics.setLineJoin(join); +} + +/** @see Graphics#setLineStylecast(int) */ +public void setLineStyle(int style) { + graphics.setLineStyle(style); +} + +/** @see Graphics#setLineWidthcast(int) */ +public void setLineWidth(int width) { + setLocalLineWidth(width); +} + +private void setLocalFont(Font f) { + localFont = f; + graphics.setFont(zoomFont(f)); +} + +private void setLocalLineWidth(int width) { + localLineWidth = width; + graphics.setLineWidth(zoomLineWidth(width)); +} + +void setScale(double value) { + if (zoom is value) + return; + this.zoom = value; + graphics.setFont(zoomFont(getLocalFont())); + graphics.setLineWidth(zoomLineWidth(localLineWidth)); +} + +/** + * @see Graphics#setTextAntialiascast(int) + */ +public void setTextAntialias(int value) { + graphics.setTextAntialias(value); +} + +/** @see Graphics#setXORMode(bool) */ +public void setXORMode(bool b) { + graphics.setXORMode(b); +} + +/** @see Graphics#translate(int, int) */ +public void translate(int dx, int dy) { + // fractionalX/Y is the fractional part left over from previous + // translates that gets lost in the integer approximation. + double dxFloat = dx * zoom + fractionalX; + double dyFloat = dy * zoom + fractionalY; + fractionalX = dxFloat - Math.floor(dxFloat); + fractionalY = dyFloat - Math.floor(dyFloat); + graphics.translate(cast(int)Math.floor(dxFloat), cast(int)Math.floor(dyFloat)); +} + +/** @see Graphics#translate(float, float) */ +public void translate(float dx, float dy) { + double dxFloat = dx * zoom + fractionalX; + double dyFloat = dy * zoom + fractionalY; + fractionalX = dxFloat - Math.floor(dxFloat); + fractionalY = dyFloat - Math.floor(dyFloat); + graphics.translate(cast(int)Math.floor(dxFloat), cast(int)Math.floor(dyFloat)); +} + +private Rectangle zoomClipRect(Rectangle r) { + tempRECT.x = cast(int)(Math.floor(r.x * zoom + fractionalX)); + tempRECT.y = cast(int)(Math.floor(r.y * zoom + fractionalY)); + tempRECT.width = cast(int)(Math.ceil(((r.x + r.width) * zoom + fractionalX))) - tempRECT.x; + tempRECT.height = cast(int)(Math.ceil(((r.y + r.height) * zoom + fractionalY))) - tempRECT.y; + return tempRECT; +} + +private Rectangle zoomFillRect(int x, int y, int w, int h) { + tempRECT.x = cast(int)(Math.floor((x * zoom + fractionalX))); + tempRECT.y = cast(int)(Math.floor((y * zoom + fractionalY))); + tempRECT.width = cast(int)(Math.floor(((x + w - 1) * zoom + fractionalX))) - tempRECT.x + 1; + tempRECT.height = cast(int)(Math.floor(((y + h - 1) * zoom + fractionalY))) - tempRECT.y + 1; + return tempRECT; +} + +Font zoomFont(Font f) { + if (f is null) + f = Display.getCurrent().getSystemFont(); + FontData data = getCachedFontData(f); + int zoomedFontHeight = zoomFontHeight(data.getHeight()); + allowText = zoomedFontHeight > 0; + fontKey.setValues(f, zoomedFontHeight); + return getCachedFont(fontKey); +} + +int zoomFontHeight(int height) { + return cast(int)(zoom * height); +} + +int zoomLineWidth(int w) { + return w; +} + +private int[] zoomPointList(int[] points) { + int[] scaled = null; + + // Look in cache for a integer array with the same length as 'points' + for (int i = 0; i < intArrayCache.length; i++) { + if (intArrayCache[i].length is points.length) { + scaled = intArrayCache[i]; + + // Move this integer array up one notch in the array + if (i !is 0) { + int[] temp = intArrayCache[i - 1]; + intArrayCache[i - 1] = scaled; + intArrayCache[i] = temp; + } + } + } + + // If no match is found, take the one that is last and resize it. + if (scaled is null) { + intArrayCache[intArrayCache.length - 1] = new int[points.length]; + scaled = intArrayCache[intArrayCache.length - 1]; + } + + // Scale the points + for (int i = 0; (i + 1) < points.length; i += 2) { + scaled[i] = cast(int)(Math.floor((points[i] * zoom + fractionalX))); + scaled[i + 1] = cast(int)(Math.floor((points[i + 1] * zoom + fractionalY))); + } + return scaled; +} + +private Rectangle zoomRect(int x, int y, int w, int h) { + tempRECT.x = cast(int)(Math.floor(x * zoom + fractionalX)); + tempRECT.y = cast(int)(Math.floor(y * zoom + fractionalY)); + tempRECT.width = cast(int)(Math.floor(((x + w) * zoom + fractionalX))) - tempRECT.x; + tempRECT.height = cast(int)(Math.floor(((y + h) * zoom + fractionalY))) - tempRECT.y; + return tempRECT; +} + +private TextLayout zoomTextLayout(TextLayout layout) { + TextLayout zoomed = new TextLayout(Display.getCurrent()); + zoomed.setText(layout.getText()); + + int zoomWidth = -1; + + if (layout.getWidth() !is -1) + zoomWidth = (cast(int)(layout.getWidth() * zoom)); + + if (zoomWidth < -1 || zoomWidth is 0) + return null; + + zoomed.setFont(zoomFont(layout.getFont())); + zoomed.setAlignment(layout.getAlignment()); + zoomed.setAscent(layout.getAscent()); + zoomed.setDescent(layout.getDescent()); + zoomed.setOrientation(layout.getOrientation()); + zoomed.setSegments(layout.getSegments()); + zoomed.setSpacing(layout.getSpacing()); + zoomed.setTabs(layout.getTabs()); + + zoomed.setWidth(zoomWidth); + int length = layout.getText().length; + if (length > 0) { + int start = 0, offset = 1; + TextStyle style = null, lastStyle = layout.getStyle(0); + for (; offset <= length; offset++) { + if (offset !is length + && (style = layout.getStyle(offset)) is lastStyle) + continue; + int end = offset - 1; + + if (lastStyle !is null) { + TextStyle zoomedStyle = new TextStyle(zoomFont(lastStyle.font), + lastStyle.foreground, lastStyle.background); + zoomedStyle.metrics = lastStyle.metrics; + zoomedStyle.rise = lastStyle.rise; + zoomedStyle.strikeout = lastStyle.strikeout; + zoomedStyle.underline = lastStyle.underline; + zoomed.setStyle(zoomedStyle, start, end); + } + lastStyle = style; + start = offset; + } + } + return zoomed; +} + +private Point zoomTextPoint(int x, int y) { + if (localCache.font !is localFont) { + //Font is different, re-calculate its height + FontMetrics metric = FigureUtilities.getFontMetrics(localFont); + localCache.height = metric.getHeight() - metric.getDescent(); + localCache.font = localFont; + } + if (targetCache.font !is graphics.getFont()) { + FontMetrics metric = graphics.getFontMetrics(); + targetCache.font = graphics.getFont(); + targetCache.height = metric.getHeight() - metric.getDescent(); + } + return new Point((cast(int)(Math.floor((x * zoom) + fractionalX))), + cast(int)(Math.floor((y + localCache.height - 1) * zoom + - targetCache.height + 1 + fractionalY))); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/SchemeBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/SchemeBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,336 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.SchemeBorder; + +import dwt.dwthelper.utils; + + + +import dwt.graphics.Color; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Border; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.AbstractBorder; +import dwtx.draw2d.ColorConstants; + +/** + * SchemeBorder allows the creation of borders based on + * {@link SchemeBorder.Scheme Schemes}. A Scheme is a class whose only purpose is + * to carry border specific information. SchemeBorder renders the border based on the + * information given by the Scheme set to it. + */ +public class SchemeBorder + : AbstractBorder + //, ColorConstants +{ + +/** The {@link SchemeBorder.Scheme} associated with this SchemeBorder **/ +protected Scheme scheme = null; + +/** Arrays of Colors, used for shadow or highlight effects **/ +protected static const Color[] + DARKEST_DARKER, + LIGHTER_DARKER, + DARKER_LIGHTER; + +static this(){ + DARKEST_DARKER = [ColorConstants.buttonDarkest, ColorConstants.buttonDarker]; + LIGHTER_DARKER = [ColorConstants.buttonLightest, ColorConstants.buttonDarker]; + DARKER_LIGHTER = [ColorConstants.buttonDarker, ColorConstants.buttonLightest]; +} + +/** + * Holds a set of information about a border, which can be changed to create a wide range + * of schemes. Provides support for border opacity, size, highlight side and shadow side + * colors. + */ +public static class Scheme { + private Insets insets; + private bool isOpaque_ = false; + + /** Arrays of Colors, used for highlight and shadow effecsts */ + protected Color[] + highlight, + shadow; + + /** + * Constructs a default border Scheme with no border sides. + * @since 2.0 + */ + protected this() { } + + /** + * Constructs a border Scheme with the specified highlight and shadow colors. The size + * of the border depends on the number of colors passed in for each parameter. + * Hightlight colors are used in the top and left sides of the border, and Shadow + * colors are used in the bottom and right sides of the border. + * + * @param highlight the hightlight colors + * @param shadow the shadow colors + * @since 2.0 + */ + public this(Color[] highlight, Color[] shadow) { + this.highlight = highlight; + this.shadow = shadow; + init(); + } + + /** + * Constructs a border scheme with the specified colors. The input colors serve as + * both highlight and shadow colors. The size of the border is the number of colors + * passed in as input. Hightlight colors are used in the top and left sides of the + * border, and Shadow colors are used in the bottom and right sides of the border. + * + * @param colors the colors to be used for the border + * @since 2.0 + */ + public this(Color[] colors) { + highlight = shadow = colors; + init(); + } + + /** + * Calculates and returns the Insets for this border Scheme. The calculations depend + * on the number of colors passed in as input. + * + * @return the Insets used by this border + * @since 2.0 + */ + protected Insets calculateInsets() { + int tl = getHighlight().length; + int br = getShadow().length; + return new Insets(tl, tl, br, br); + } + + /** + * Calculates and retuns the opaque state of this border scheme. Returns + * false if any of the border colors are null. This is done + * to prevent the appearance of underlying pixels since the border color is + * null. + * + * @return true if this border is opaque + * @since 2.0 + */ + protected bool calculateOpaque() { + Color colors [] = getHighlight(); + for (int i = 0; i < colors.length; i++) + if (colors[i] is null) + return false; + colors = getShadow(); + for (int i = 0; i < colors.length; i++) + if (colors[i] is null) + return false; + return true; + } + + /** + * Returns the highlight colors of this border scheme as an array of Colors. + * + * @return the highlight colors + * @since 2.0 + */ + protected Color[] getHighlight() { + return highlight; + } + + /** + * Returns the Insets required by this Scheme. + * + * @return the Insets + * @since 2.0 + */ + protected Insets getInsets() { + return insets; + } + + /** + * Returns the shadow colors of this border scheme as an array of Colors. + * + * @return the shadow colors + * @since 2.0 + */ + protected Color[] getShadow() { + return shadow; + } + + /** + * Calculates and initializes the properties of this border scheme. + * + * @since 2.0 + */ + protected void init() { + insets = calculateInsets(); + isOpaque_ = calculateOpaque(); + } + + /** + * Returns whether this border should be opaque or not. + * + * @return true if this border is opaque + * @since 2.0 + */ + protected bool isOpaque() { + return isOpaque_; + } +} + +/** + * Interface which defines some commonly used schemes for the border. These schemes can be + * given as input to the {@link SchemeBorder SchemeBorder} to generate appropriate borders. + */ +public struct SCHEMES { + + /** Schemes used for shadow and highlight effects **/ + public static Scheme + BUTTON_CONTRAST, + BUTTON_RAISED, + BUTTON_PRESSED, + RAISED, + LOWERED, + RIDGED, + ETCHED; + + static this(){ + BUTTON_CONTRAST = new Scheme( + [ColorConstants.button, ColorConstants.buttonLightest], + DARKEST_DARKER + ); + BUTTON_RAISED = new Scheme( + [ColorConstants.buttonLightest], + DARKEST_DARKER + ); + BUTTON_PRESSED = new Scheme( + DARKEST_DARKER, + [ColorConstants.buttonLightest] + ); + RAISED = new Scheme( + [ColorConstants.buttonLightest], + [ColorConstants.buttonDarkest] + ); + LOWERED = new Scheme( + [ColorConstants.buttonDarkest], + [ColorConstants.buttonLightest] + ); + RIDGED = new Scheme(LIGHTER_DARKER, DARKER_LIGHTER); + ETCHED = new Scheme(DARKER_LIGHTER, LIGHTER_DARKER); + } +} + +/** + * Constructs a default SchemeBorder with no scheme defined. + * @since 2.0 + */ +protected this() { } + +/** + * Constructs a SchemeBorder with the Scheme given as input. + * + * @param scheme the Scheme to be used by this border + * @since 2.0 + */ +public this(Scheme scheme) { + setScheme(scheme); +} + +/** + * @see Border#getInsets(IFigure) + */ +public Insets getInsets(IFigure figure) { + return getScheme().getInsets(); +} + +/** + * Returns the scheme used by this border. + * + * @return the Scheme used by this border + * @since 2.0 + */ +protected Scheme getScheme() { + return scheme; +} + +/** + * Returns the opaque state of this border. Returns true indicating that this + * will fill in the area enclosed by the border. + * + * @see Border#isOpaque() + */ +public bool isOpaque() { + return true; +} + +/** + * Sets the Scheme for this border to the Scheme given as input. + * + * @param scheme the Scheme for this border + * @since 2.0 + */ +protected void setScheme(Scheme scheme) { + this.scheme = scheme; +} + +/** + * @see Border#paint(IFigure, Graphics, Insets) + */ +public void paint(IFigure figure, Graphics g, Insets insets) { + Color [] tl = scheme.getHighlight(); + Color [] br = scheme.getShadow(); + + paint (g, figure, insets, tl, br); +} + +/** + * Paints the border using the information in the set Scheme and the inputs given. Side + * widths are determined by the number of colors in the Scheme for each side. + * + * @param graphics the graphics object + * @param fig the figure this border belongs to + * @param insets the insets + * @param tl the highlight (top/left) colors + * @param br the shadow (bottom/right) colors + */ +protected void paint(Graphics graphics, IFigure fig, + Insets insets, Color[] tl, Color[] br) { + graphics.setLineWidth(1); + graphics.setLineStyle(Graphics.LINE_SOLID); + graphics.setXORMode(false); + + Rectangle rect = getPaintRectangle(fig, insets); + + int top = rect.y; + int left = rect.x; + int bottom = rect.bottom() - 1; + int right = rect.right() - 1; + Color color; + + for (int i = 0; i < br.length; i++) { + color = br[i]; + graphics.setForegroundColor(color); + graphics.drawLine(right - i, bottom - i, right - i, top + i); + graphics.drawLine(right - i, bottom - i, left + i, bottom - i); + } + + right--; + bottom--; + + for (int i = 0; i < tl.length; i++) { + color = tl[i]; + graphics.setForegroundColor(color); + graphics.drawLine(left + i, top + i, right - i, top + i); + graphics.drawLine(left + i, top + i, left + i, bottom - i); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ScrollBar.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ScrollBar.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,667 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ScrollBar; + +import dwt.dwthelper.utils; + +import dwtx.dwtxhelper.Bean; + +import dwt.graphics.Color; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.Transposer; + +import dwtx.draw2d.Orientable; +import dwtx.draw2d.Figure; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.RangeModel; +import dwtx.draw2d.Clickable; +import dwtx.draw2d.MouseMotionListener; +import dwtx.draw2d.MouseListener; +import dwtx.draw2d.MouseEvent; +import dwtx.draw2d.FigureUtilities; +import dwtx.draw2d.ColorConstants; +import dwtx.draw2d.Button; +import dwtx.draw2d.RangeModel; +import dwtx.draw2d.DefaultRangeModel; +import dwtx.draw2d.ArrowButton; +import dwtx.draw2d.ButtonBorder; +import dwtx.draw2d.ChangeListener; +import dwtx.draw2d.ChangeEvent; +import dwtx.draw2d.Panel; +import dwtx.draw2d.ScrollBarLayout; +import dwtx.draw2d.SchemeBorder; +import dwtx.draw2d.ActionListener; +import dwtx.draw2d.ActionEvent; + +/** + * Provides for the scrollbars used by the {@link ScrollPane}. A ScrollBar is made up of + * five essential Figures: An 'Up' arrow button, a 'Down' arrow button, a draggable + * 'Thumb', a 'Pageup' button, and a 'Pagedown' button. + */ +public class ScrollBar + : Figure + , Orientable, PropertyChangeListener +{ + +private static const int ORIENTATION_FLAG = Figure.MAX_FLAG << 1; +/** @see Figure#MAX_FLAG */ +protected static const int MAX_FLAG = ORIENTATION_FLAG; + +private static const Color COLOR_TRACK; +static this(){ + COLOR_TRACK = FigureUtilities.mixColors( + ColorConstants.white, + ColorConstants.button); +} + +private RangeModel rangeModel = null; +private IFigure thumb; +private Clickable pageUp_, pageDown_; +private Clickable buttonUp, buttonDown; +/** + * Listens to mouse events on the scrollbar to take care of scrolling. + */ +protected ThumbDragger thumbDragger; + +private bool isHorizontal_ = false; + +private int pageIncrement = 50; +private int stepIncrement = 10; + +/** + * Transposes from vertical to horizontal if needed. + */ +protected /+final+/ Transposer transposer; + +private void instanceInit(){ + thumbDragger = new ThumbDragger(); + transposer = new Transposer(); + setRangeModel(new DefaultRangeModel()); +} + +/** + * Constructs a ScrollBar. ScrollBar orientation is vertical by default. Call + * {@link #setHorizontal(bool)} with true to set horizontal orientation. + * + * @since 2.0 + */ +public this() { + instanceInit(); + initialize(); +} + +/** + * Creates the default 'Up' ArrowButton for the ScrollBar. + * + * @return the up button + * @since 2.0 + */ +protected Clickable createDefaultUpButton() { + Button buttonUp = new ArrowButton(); + buttonUp.setBorder(new ButtonBorder(ButtonBorder.SCHEMES.BUTTON_SCROLLBAR)); + return buttonUp; +} + +/** + * Creates the default 'Down' ArrowButton for the ScrollBar. + * + * @return the down button + * @since 2.0 + */ +protected Clickable createDefaultDownButton() { + Button buttonDown = new ArrowButton(); + buttonDown.setBorder(new ButtonBorder(ButtonBorder.SCHEMES.BUTTON_SCROLLBAR)); + return buttonDown; +} + +/** + * Creates the pagedown Figure for the Scrollbar. + * + * @return the page down figure + * @since 2.0 + */ +protected Clickable createPageDown() { + return createPageUp(); +} + +/** + * Creates the pageup Figure for the Scrollbar. + * + * @return the page up figure + * @since 2.0 + */ +protected Clickable createPageUp() { + Clickable clickable = new Clickable(); + clickable.setOpaque(true); + clickable.setBackgroundColor(COLOR_TRACK); + clickable.setRequestFocusEnabled(false); + clickable.setFocusTraversable(false); + clickable.addChangeListener( dgChangeListener( (ChangeEvent evt, Clickable clickable_){ + if (clickable_.getModel().isArmed()) + clickable_.setBackgroundColor(ColorConstants.black); + else + clickable_.setBackgroundColor(COLOR_TRACK); + }, clickable)); + return clickable; +} + +/** + * Creates the Scrollbar's "thumb", the draggable Figure that indicates the Scrollbar's + * position. + * + * @return the thumb figure + * @since 2.0 + */ +protected IFigure createDefaultThumb() { + Panel thumb = new Panel(); + thumb.setMinimumSize(new Dimension(6, 6)); + thumb.setBackgroundColor(ColorConstants.button); + + thumb.setBorder(new SchemeBorder(SchemeBorder.SCHEMES.BUTTON_CONTRAST)); + return thumb; +} + +/** + * Returns the figure used as the up button. + * @return the up button + */ +protected IFigure getButtonUp() { + // TODO: The set method takes a Clickable while the get method returns an IFigure. + // Change the get method to return Clickable (since that's what it's typed as). + return buttonUp; +} + +/** + * Returns the figure used as the down button. + * @return the down button + */ +protected IFigure getButtonDown() { + // TODO: The set method takes a Clickable while the get method returns an IFigure. + // Change the get method to return Clickable (since that's what it's typed as). + return buttonDown; +} + +/** + * Returns the extent. + * @return the extent + * @see RangeModel#getExtent() + */ +public int getExtent() { + return getRangeModel().getExtent(); +} + +/** + * Returns the minumum value. + * @return the minimum + * @see RangeModel#getMinimum() + */ +public int getMinimum() { + return getRangeModel().getMinimum(); +} + +/** + * Returns the maximum value. + * @return the maximum + * @see RangeModel#getMaximum() + */ +public int getMaximum() { + return getRangeModel().getMaximum(); +} + +/** + * Returns the figure used for page down. + * @return the page down figure + */ +protected IFigure getPageDown() { + // TODO: The set method takes a Clickable while the get method returns an IFigure. + // Change the get method to return Clickable (since that's what it's typed as). + return pageDown_; +} + +/** + * Returns the the amound the scrollbar will move when the page up or page down areas are + * pressed. + * @return the page increment + */ +public int getPageIncrement() { + return pageIncrement; +} + +/** + * Returns the figure used for page up. + * @return the page up figure + */ +protected IFigure getPageUp() { + // TODO: The set method takes a Clickable while the get method returns an IFigure. + // Change the get method to return Clickable (since that's what it's typed as). + return pageUp_; +} + +/** + * Returns the range model for this scrollbar. + * @return the range model + */ +public RangeModel getRangeModel() { + return rangeModel; +} + +/** + * Returns the amount the scrollbar will move when the up or down arrow buttons are + * pressed. + * @return the step increment + */ +public int getStepIncrement() { + return stepIncrement; +} + +/** + * Returns the figure used as the scrollbar's thumb. + * @return the thumb figure + */ +protected IFigure getThumb() { + return thumb; +} + +/** + * Returns the current scroll position of the scrollbar. + * @return the current value + * @see RangeModel#getValue() + */ +public int getValue() { + return getRangeModel().getValue(); +} + +/** + * Returns the size of the range of allowable values. + * @return the value range + */ +protected int getValueRange() { + return getMaximum() - getExtent() - getMinimum(); +} + +/** + * Initilization of the ScrollBar. Sets the Scrollbar to have a ScrollBarLayout with + * vertical orientation. Creates the Figures that make up the components of the ScrollBar. + * + * @since 2.0 + */ +protected void initialize() { + setLayoutManager(new ScrollBarLayout(transposer)); + setUpClickable(createDefaultUpButton()); + setDownClickable(createDefaultDownButton()); + setPageUp(createPageUp()); + setPageDown(createPageDown()); + setThumb(createDefaultThumb()); +} + +/** + * Returns true if this scrollbar is orientated horizontally, + * false otherwise. + * @return whether this scrollbar is horizontal + */ +public bool isHorizontal() { + return isHorizontal_; +} + +private void pageDown() { + setValue(getValue() + getPageIncrement()); +} + +private void pageUp() { + setValue(getValue() - getPageIncrement()); +} + +/** + * @see PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) + */ +public void propertyChange(PropertyChangeEvent event) { + if (null !is cast(RangeModel)event.getSource() ) { + setEnabled(getRangeModel().isEnabled()); + if (RangeModel.PROPERTY_VALUE.equals(event.getPropertyName())) { + firePropertyChange("value", event.getOldValue(), //$NON-NLS-1$ + event.getNewValue()); + revalidate(); + } + if (RangeModel.PROPERTY_MINIMUM.equals(event.getPropertyName())) { + firePropertyChange("value", event.getOldValue(), //$NON-NLS-1$ + event.getNewValue()); + revalidate(); + } + if (RangeModel.PROPERTY_MAXIMUM.equals(event.getPropertyName())) { + firePropertyChange("value", event.getOldValue(), //$NON-NLS-1$ + event.getNewValue()); + revalidate(); + } + if (RangeModel.PROPERTY_EXTENT.equals(event.getPropertyName())) { + firePropertyChange("value", event.getOldValue(), //$NON-NLS-1$ + event.getNewValue()); + revalidate(); + } + } +} + +/** + * @see IFigure#revalidate() + */ +public void revalidate() { + // Override default revalidate to prevent going up the parent chain. Reason for this + // is that preferred size never changes unless orientation changes. + invalidate(); + getUpdateManager().addInvalidFigure(this); +} + +/** + * Does nothing because this doesn't make sense for a scrollbar. + * @see Orientable#setDirection(int) + */ +public void setDirection(int direction) { + //Doesn't make sense for Scrollbar. +} + +/** + * Sets the Clickable that represents the down arrow of the Scrollbar to down. + * + * @param down the down button + * @since 2.0 + */ +public void setDownClickable(Clickable down) { + if (buttonDown !is null) { + remove(buttonDown); + } + buttonDown = down; + if (buttonDown !is null) { + if (auto b = cast(Orientable)buttonDown ) + b.setDirection(isHorizontal() + ? Orientable.EAST + : Orientable.SOUTH); + buttonDown.setFiringMethod(Clickable.REPEAT_FIRING); + buttonDown.addActionListener(dgActionListener( (ActionEvent evt){ + stepDown(); + })); + add(buttonDown, stringcast(ScrollBarLayout.DOWN_ARROW)); + } +} + +/** + * Sets the Clickable that represents the up arrow of the Scrollbar to up. + * + * @param up the up button + * @since 2.0 + */ +public void setUpClickable(Clickable up) { + if (buttonUp !is null) { + remove(buttonUp); + } + buttonUp = up; + if (up !is null) { + if (auto o = cast(Orientable)up ) + o.setDirection(isHorizontal() + ? Orientable.WEST + : Orientable.NORTH); + buttonUp.setFiringMethod(Clickable.REPEAT_FIRING); + buttonUp.addActionListener(dgActionListener( (ActionEvent evt){ + stepUp(); + })); + add(buttonUp, stringcast(ScrollBarLayout.UP_ARROW)); + } +} + +/** + * @see IFigure#setEnabled(bool) + */ +public void setEnabled(bool value) { + if (isEnabled() is value) + return; + super.setEnabled(value); + setChildrenEnabled(value); + if (getThumb() !is null) { + getThumb().setVisible(value); + revalidate(); + } +} + +/** + * Sets the extent of the Scrollbar to ext + * + * @param ext the extent + * @since 2.0 + */ +public void setExtent(int ext) { + if (getExtent() is ext) + return; + getRangeModel().setExtent(ext); +} + +/** + * Sets the orientation of the ScrollBar. If true, the Scrollbar will have + * a horizontal orientation. If false, the scrollBar will have a vertical + * orientation. + * + * @param value true if the scrollbar should be horizontal + * @since 2.0 + */ +public final void setHorizontal(bool value) { + setOrientation(value ? HORIZONTAL : VERTICAL); +} + +/** + * Sets the maximum position to max. + * + * @param max the maximum position + * @since 2.0 + */ +public void setMaximum(int max) { + if (getMaximum() is max) + return; + getRangeModel().setMaximum(max); +} + +/** + * Sets the minimum position to min. + * + * @param min the minumum position + * @since 2.0 + */ +public void setMinimum(int min) { + if (getMinimum() is min) + return; + getRangeModel().setMinimum(min); +} + +/** + * @see Orientable#setOrientation(int) + */ +public void setOrientation(int value) { + if ((value is HORIZONTAL) is isHorizontal()) + return; + isHorizontal_ = value is HORIZONTAL; + transposer.setEnabled(isHorizontal_); + + setChildrenOrientation(value); + super.revalidate(); +} + +/** + * Sets the ScrollBar to scroll increment pixels when its pageup or pagedown + * buttons are pressed. (Note that the pageup and pagedown buttons are NOT the + * arrow buttons, they are the figures between the arrow buttons and the ScrollBar's + * thumb figure). + * + * @param increment the new page increment + * @since 2.0 + */ +public void setPageIncrement(int increment) { + pageIncrement = increment; +} + +/** + * Sets the pagedown button to the passed Clickable. The pagedown button is the figure + * between the down arrow button and the ScrollBar's thumb figure. + * + * @param down the page down figure + * @since 2.0 + */ +public void setPageDown(Clickable down) { + if (pageDown_ !is null) + remove(pageDown_); + pageDown_ = down; + if (pageDown_ !is null) { + pageDown_.setFiringMethod(Clickable.REPEAT_FIRING); + pageDown_.addActionListener(dgActionListener( (ActionEvent evt){ + pageDown(); + })); + add(down,stringcast( ScrollBarLayout.PAGE_DOWN)); + } +} + +/** + * Sets the pageup button to the passed Clickable. The pageup button is the rectangular + * figure between the down arrow button and the ScrollBar's thumb figure. + * + * @param up the page up figure + * @since 2.0 + */ +public void setPageUp(Clickable up) { + if (pageUp_ !is null) + remove(pageUp_); + pageUp_ = up; + if (pageUp_ !is null) { + pageUp_.setFiringMethod(Clickable.REPEAT_FIRING); + pageUp_.addActionListener(dgActionListener((ActionEvent evt){ + pageUp(); + })); + add(pageUp_, stringcast(ScrollBarLayout.PAGE_UP)); + } +} + +/** + * Sets the ScrollBar's RangeModel to the passed value. + * + * @param rangeModel the new range model + * @since 2.0 + */ +public void setRangeModel(RangeModel rangeModel) { + if (this.rangeModel !is null) + this.rangeModel.removePropertyChangeListener(this); + this.rangeModel = rangeModel; + rangeModel.addPropertyChangeListener(this); +} + +/** + * Sets the ScrollBar's step increment to the passed value. The step increment indicates + * how many pixels the ScrollBar will scroll when its up or down arrow button is pressed. + * + * @param increment the new step increment + * @since 2.0 + */ +public void setStepIncrement(int increment) { + stepIncrement = increment; +} + +/** + * Sets the ScrollBar's thumb to the passed Figure. The thumb is the draggable component + * of the ScrollBar that indicates the ScrollBar's position. + * + * @param figure the thumb figure + * @since 2.0 + */ +public void setThumb(IFigure figure) { + if (thumb !is null) { + thumb.removeMouseListener(thumbDragger); + thumb.removeMouseMotionListener(thumbDragger); + remove(thumb); + } + thumb = figure; + if (thumb !is null) { + thumb.addMouseListener(thumbDragger); + thumb.addMouseMotionListener(thumbDragger); + add(thumb, stringcast(ScrollBarLayout.THUMB)); + } +} + +/** + * Sets the value of the Scrollbar to v + * + * @param v the new value + * @since 2.0 + */ +public void setValue(int v) { + getRangeModel().setValue(v); +} + +/** + * Causes the ScrollBar to scroll down (or right) by the value of its step increment. + * + * @since 2.0 + */ +protected void stepDown() { + setValue(getValue() + getStepIncrement()); +} + +/** + * Causes the ScrollBar to scroll up (or left) by the value of its step increment. + * + * @since 2.0 + */ +protected void stepUp() { + setValue(getValue() - getStepIncrement()); +} + +class ThumbDragger + : MouseMotionListener.Stub + , MouseListener +{ + protected Point start; + protected int dragRange; + protected int revertValue; + protected bool armed; + public this() { } + + public void mousePressed(MouseEvent me) { + armed = true; + start = me.getLocation(); + Rectangle area = new Rectangle(transposer.t(getClientArea())); + Dimension thumbSize = transposer.t(getThumb().getSize()); + if (getButtonUp() !is null) + area.height -= transposer.t(getButtonUp().getSize()).height; + if (getButtonDown() !is null) + area.height -= transposer.t(getButtonDown().getSize()).height; + Dimension sizeDifference = new Dimension(area.width, + area.height - thumbSize.height); + dragRange = sizeDifference.height; + revertValue = getValue(); + me.consume(); + } + + public void mouseDragged(MouseEvent me) { + if (!armed) + return; + Dimension difference = transposer.t(me.getLocation().getDifference(start)); + int change = getValueRange() * difference.height / dragRange; + setValue(revertValue + change); + me.consume(); + } + + public void mouseReleased(MouseEvent me) { + if (!armed) + return; + armed = false; + me.consume(); + } + + public void mouseDoubleClicked(MouseEvent me) { } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ScrollBarLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ScrollBarLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ScrollBarLayout; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.Transposer; + +import dwtx.draw2d.AbstractLayout; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.ScrollBar; + +/** + * Lays out the Figures that make up a ScrollBar. + */ +public class ScrollBarLayout + : AbstractLayout +{ + +/** Used as a constraint for the up arrow figure. */ +public static final String UP_ARROW = "up arrow"; //$NON-NLS-1$ +/** Used as a constraint for the down arrow figure. */ +public static final String DOWN_ARROW = "down arrow"; //$NON-NLS-1$ +/** Used as a constraint for the thumb figure. */ +public static final String THUMB = "thumb"; //$NON-NLS-1$ +/** Used as a constraint for the page up figure. */ +public static final String PAGE_UP = "page_up"; //$NON-NLS-1$ +/** Used as a constraint for the page down figure. */ +public static final String PAGE_DOWN = "page_down"; //$NON-NLS-1$ + +IFigure up, down, thumb, pageUp, pageDown; + +/** + * Transposes values if the ScrollBar is horizontally oriented. When used properly, the + * layout manager just needs to code for one case: vertical orientation. + */ +protected final Transposer transposer; + +/** + * Constructs a ScrollBarLayout. If the given Transposer is enabled, the Scrollbar will + * be horizontally oriented. Otherwise, the ScrollBar will be vertically oriented. + * + * @param t the Transposer + * @since 2.0 + */ +public this(Transposer t) { + transposer = t; +} + +/** + * @see AbstractLayout#setConstraint(IFigure, Object) + */ +public void setConstraint(IFigure figure, Object constraint_) { + String constraint = stringcast(constraint_); + if (constraint.equals(UP_ARROW)) + up = figure; + else if (constraint.equals(DOWN_ARROW)) + down = figure; + else if (constraint.equals(THUMB)) + thumb = figure; + else if (constraint.equals(PAGE_UP)) + pageUp = figure; + else if (constraint.equals(PAGE_DOWN)) + pageDown = figure; +} + +/** + * @see AbstractLayout#calculatePreferredSize(IFigure, int, int) + */ +protected Dimension calculatePreferredSize(IFigure parent, int w, int h) { + Insets insets = transposer.t(parent.getInsets()); + Dimension d = new Dimension(16, 16 * 4); + d.expand(insets.getWidth(), insets.getHeight()); + return transposer.t(d); +} + +/** + * @see LayoutManager#layout(IFigure) + */ +public void layout(IFigure parent) { + ScrollBar scrollBar = cast(ScrollBar) parent; + + Rectangle trackBounds = layoutButtons(scrollBar); + + int extent = scrollBar.getExtent(); + int max = scrollBar.getMaximum(); + int min = scrollBar.getMinimum(); + int totalRange = max - min; + int valueRange = totalRange - extent; + if ((valueRange < 1) || (!scrollBar.isEnabled())) { + Rectangle boundsUpper = new Rectangle(trackBounds), + boundsLower = new Rectangle(trackBounds); + boundsUpper.height /= 2; + boundsLower.y += boundsUpper.height; + boundsLower.height = trackBounds.height - boundsUpper.height; + if (pageUp !is null) + pageUp.setBounds(transposer.t(boundsUpper)); + if (pageDown !is null) + pageDown.setBounds(transposer.t(boundsLower)); + return; + } + + if (totalRange is 0) + return; + int thumbHeight = Math.max(thumb is null ? 0 : thumb.getMinimumSize().height, + trackBounds.height * extent / totalRange); + + if (thumb !is null) + thumb.setVisible(trackBounds.height > thumbHeight); + + int thumbY = trackBounds.y + (trackBounds.height - thumbHeight) + * (scrollBar.getValue() - min) / valueRange; + + Rectangle thumbBounds = new Rectangle( + trackBounds.x, + thumbY, + trackBounds.width, + thumbHeight); + + if (thumb !is null) + thumb.setBounds(transposer.t(thumbBounds)); + + if (pageUp !is null) + pageUp.setBounds(transposer.t(new Rectangle( + trackBounds.x, + trackBounds.y, + trackBounds.width, + thumbBounds.y - trackBounds.y))); + + if (pageDown !is null) + pageDown.setBounds(transposer.t(new Rectangle( + trackBounds.x , + thumbBounds.y + thumbHeight, + trackBounds.width, + trackBounds.bottom() - thumbBounds.bottom()))); +} + +/** + * Places the buttons and returns the Rectangle into which the track should be placed. + * The track consists of the pageup, pagedown, and thumb figures. The Rectangle returned + * should be transposed correctly, that is, it should be vertically oriented. Users of + * the rectangle will re-transpose it for horizontal use. + * + * @param scrollBar the scrollbar whose buttons are being layed out + * @return the Rectangle into which the track should be placed + * @since 2.0 + */ +protected Rectangle layoutButtons(ScrollBar scrollBar) { + Rectangle bounds = transposer.t(scrollBar.getClientArea()); + Dimension buttonSize = new Dimension( + bounds.width, + Math.min(bounds.width, bounds.height / 2)); + + if (up !is null) + up.setBounds(transposer.t( + new Rectangle(bounds.getTopLeft(), buttonSize))); + if (down !is null) { + Rectangle r = new Rectangle ( + bounds.x, bounds.bottom() - buttonSize.height, + buttonSize.width, buttonSize.height); + down.setBounds(transposer.t(r)); + } + + Rectangle trackBounds = bounds.getCropped( + new Insets( + (up is null) ? 0 : buttonSize.height, 0, + (down is null) ? 0 : buttonSize.height, 0)); + + return trackBounds; +} + +/** + * @see LayoutManager#remove(IFigure) + */ +public void remove(IFigure child) { + if (child is up) { + up = null; + } else if (child is down) { + down = null; + } else if (child is thumb) { + thumb = null; + } else if (child is pageUp) { + pageUp = null; + } else if (child is pageDown) { + pageDown = null; + } +} +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ScrollPane.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ScrollPane.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,337 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ScrollPane; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.Figure; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.ScrollBar; +import dwtx.draw2d.Viewport; +import dwtx.draw2d.ScrollPaneLayout; + +/** + * A class which implements automatic horizontal and/or vertical scrolling for a single + * IFigure child. + *

+ * ScrollBar visibilites are represented by integer class constants: + *

    + *
  • NEVER: Never show the ScrollBar + *
  • AUTOMATIC: Show as needed, when the ScrollPane can no longer contain its view + *
  • ALWAYS: Always show the ScrollBar + *
+ * To use, instantiate a ScrollPane object and call its setView(IFigure) method passing + * the IFigure that is desired to have scrolling ability. + */ +public class ScrollPane + : Figure +{ + +/** Constant indicating to never show the ScrollBar */ +public static const int NEVER = 0; +/** Constant indicating to show as needed, when the ScrollPane can't contain its view */ +public static const int AUTOMATIC = 1; +/** Constant indicating to always show the ScrollBar */ +public static const int ALWAYS = 2; + +/** The viewport being scrolled */ +protected Viewport viewport; +/** The horizontal scrollbar */ +protected ScrollBar hBar; +/** The vertical scrollbar */ +protected ScrollBar vBar; +private int + hVisibility = AUTOMATIC, + vVisibility = AUTOMATIC; + +/** + * Constructs a new ScrollPane with a ScrollPaneLayout. + * + * @since 2.0 + */ +public this() { + setLayoutManager(new ScrollPaneLayout()); +} + +/** + * Creates a new horizontally oriented ScrollBar and adds it to this ScrollPane. + * + * @since 2.0 + */ +protected void createHorizontalScrollBar() { + ScrollBar bar = new ScrollBar(); + bar.setHorizontal(true); + setHorizontalScrollBar(bar); +} + +/** + * Creates a new Viewport and adds it to this ScrollPane. + * + * @since 2.0 + */ +protected void createViewport() { + setViewport(new Viewport()); +} + +/** + * Creates a new vertically oriented ScrollBar and adds it to this ScrollPane. + * + * @since 2.0 + */ +protected void createVerticalScrollBar() { + ScrollBar bar = new ScrollBar(); + setVerticalScrollBar(bar); +} + +/** + * Returns the ScrollPane's horizontal ScrollBar. + * + * @return the horizontal scrollbar + * @since 2.0 + */ +public ScrollBar getHorizontalScrollBar() { + if (hBar is null) + createHorizontalScrollBar(); + return hBar; +} + +/** + * Returns the visibility of the ScrollPane's horizontal ScrollBar. These are represented + * by the integer class constants {@link #NEVER}, {@link #AUTOMATIC}, and {@link #ALWAYS}. + * The default is {@link #AUTOMATIC}. + * + * @return the visiblity of the horizontal scrollbar + * @since 2.0 + */ +public int getHorizontalScrollBarVisibility() { + return hVisibility; +} + +/** + * Returns the ScrollPane's vertical ScrollBar. + * + * @return teh vertical scrollbar + * @since 2.0 + */ +public ScrollBar getVerticalScrollBar() { + if (vBar is null) + createVerticalScrollBar(); + return vBar; +} + +/** + * Returns the visibility of the ScrollPane's vertical ScrollBar. These are represented + * by the integer class constants {@link #NEVER}, {@link #AUTOMATIC}, and {@link #ALWAYS}. + * The default is {@link #AUTOMATIC}. + * + * @return the visibility of the vertical scrollbar + * @since 2.0 + */ +public int getVerticalScrollBarVisibility() { + return vVisibility; +} + +/** + * Returns the contents of the viewport. + * @return the contents of the viewport + */ +public IFigure getContents() { + return getView(); +} + +/** + * Returns the ScrollPane's view. The view is the IFigure that is the contents of the + * ScrollPane. + * @return the contents + * @deprecated use getContents() + * @since 2.0 + */ +public IFigure getView() { + return getViewport().getContents(); +} + +/** + * Returns the ScrollPane's {@link Viewport}. + * + * @return the viewport + * @since 2.0 + */ +public Viewport getViewport() { + if (viewport is null) + createViewport(); + return viewport; +} + +/** + * Returns true because ScrollPanes are always opaque. + * @see IFigure#isOpaque() + */ +public bool isOpaque() { + return true; +} + +/** + * Scrolls the Scrollpane horizontally x pixels from its left-most position. + * + * @param x the amount to scroll horizontally + * @since 2.0 + */ +public void scrollHorizontalTo(int x) { + getViewport().setHorizontalLocation(x); +} + +/** + * Scrolls the Scrollpane horizontally from its left-most position by location.x pixels + * and vertically from its top-most position by location.y pixels. + * + * @param location the point to scroll to + * @since 2.0 + */ +public void scrollTo(Point location) { + scrollHorizontalTo(location.x); + scrollVerticalTo(location.y); +} + +/** + * Scrolls the Scrollpane vertically y pixels from its top-most position. + * + * @param y the amount to scroll vertically + * @since 2.0 + */ +public void scrollVerticalTo(int y) { + getViewport().setVerticalLocation(y); +} + +/** + * Sets the contents of the current viewport. + * @param figure the contents of the viewport + */ +public void setContents(IFigure figure) { + setView(figure); +} + +/** + * Sets the ScrollPane's horizontal ScrollBar to the passed ScrollBar. + * + * @param bar the new horizontal scrollbar + * @since 2.0 + */ +public void setHorizontalScrollBar(ScrollBar bar) { + if (hBar !is null) { + remove(hBar); + hBar.getRangeModel().removePropertyChangeListener(hBar); + } + hBar = bar; + if (hBar !is null) { + add(hBar); + hBar.setRangeModel(getViewport().getHorizontalRangeModel()); + } +} + +/** + * Sets the horizontal ScrollBar visibility of the ScrollPane to the passed value. These + * are represented by the integer class constants {@link #NEVER}, {@link #AUTOMATIC}, and + * {@link #ALWAYS}. The default is {@link #AUTOMATIC}. + * + * @param v the new horizontal visibility + * @since 2.0 + */ +public void setHorizontalScrollBarVisibility(int v) { + if (hVisibility is v) + return; + hVisibility = v; + revalidate(); +} + +/** + * Sets both the horizontal and vertical ScrollBar visibilities of the ScrollPane to the + * passed value. These are represented by the integer class constants {@link #NEVER}, + * {@link #AUTOMATIC}, and {@link #ALWAYS}. The default is {@link #AUTOMATIC}. + * + * @param v the new vertical and horizontal visibility + * @since 2.0 + */ +public void setScrollBarVisibility(int v) { + setHorizontalScrollBarVisibility(v); + setVerticalScrollBarVisibility(v); +} + +/** + * Sets the ScrollPane's vertical ScrollBar to the passed Scrollbar. + * + * @param bar the new vertical scrollbar + * @since 2.0 + */ +public void setVerticalScrollBar(ScrollBar bar) { + if (vBar !is null) { + remove(vBar); + vBar.getRangeModel().removePropertyChangeListener(vBar); + } + vBar = bar; + if (vBar !is null) { + add(vBar); + vBar.setRangeModel(getViewport().getVerticalRangeModel()); + } +} + +/** + * Sets the vertical ScrollBar visibility of the ScrollPane to the passed value. These are + * represented by the integer class constants {@link #NEVER}, {@link #AUTOMATIC}, and + * {@link #ALWAYS}. The default is {@link #AUTOMATIC}. + * + * @param v the new vertical scrollbar visibility + * @since 2.0 + */ +public void setVerticalScrollBarVisibility(int v) { + if (vVisibility is v) + return; + vVisibility = v; + revalidate(); +} + +/** + * Sets the ScrollPane's view to the passed IFigure. The view is the top-level IFigure + * which represents the contents of the ScrollPane. + * @param figure the new contents + * @deprecated call setContents(IFigure) instead + * @since 2.0 + */ +public void setView(IFigure figure) { + getViewport().setContents(figure); +} + +/** + * Sets the ScrollPane's Viewport to the passed value. + * + * @param vp the new viewport + * @since 2.0 + */ +public void setViewport(Viewport vp) { + if (viewport !is null) + remove(viewport); + viewport = vp; + if (vp !is null) + add(vp, 0); +} + +/** + * @see IFigure#validate() + */ +public void validate() { + super.validate(); + getHorizontalScrollBar().validate(); + getVerticalScrollBar().validate(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ScrollPaneLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ScrollPaneLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ScrollPaneLayout; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; + +import dwtx.draw2d.AbstractHintLayout; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.ScrollPane; +import dwtx.draw2d.ScrollBar; +import dwtx.draw2d.Viewport; +import dwtx.draw2d.ScrollPaneSolver; + +/** + * The ScrollPaneLayout is responsible for laying out the {@link Viewport} and + * {@link ScrollBar ScrollBars} of a {@link ScrollPane}. + */ +public class ScrollPaneLayout + : AbstractHintLayout +{ + +/** @see ScrollPane#NEVER */ +protected static const int NEVER = ScrollPane.NEVER; +/** @see ScrollPane#AUTOMATIC */ +protected static const int AUTO = ScrollPane.AUTOMATIC; +/** @see ScrollPane#ALWAYS */ +protected static const int ALWAYS = ScrollPane.ALWAYS; + +/** + * @see AbstractHintLayout#calculateMinimumSize(IFigure, int, int) + */ +public Dimension calculateMinimumSize(IFigure figure, int w, int h) { + ScrollPane scrollpane = cast(ScrollPane)figure; + Insets insets = scrollpane.getInsets(); + Dimension d = scrollpane.getViewport().getMinimumSize(w, h); + return d.getExpanded(insets.getWidth(), insets.getHeight()); +} + +/** + * Calculates and returns the preferred size of the container based on the given hints. + * If the given ScrollPane's (container) horizontal and vertical scroll bar + * visibility is not {@link ScrollPane#NEVER}, then space for those bars is always + * deducted from the hints (whether or not we actually need the scroll bars). + * + * @param container the ScrollPane whose preferred size needs to be calculated + * @param wHint the width hint + * @param hHint the height hint + * @return the preferred size of the given container + * @since 2.0 + */ +protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) { + ScrollPane scrollpane = cast(ScrollPane)container; + ScrollBar hBar = scrollpane.getHorizontalScrollBar(); + ScrollBar vBar = scrollpane.getVerticalScrollBar(); + Insets insets = scrollpane.getInsets(); + + int reservedWidth = insets.getWidth(); + int reservedHeight = insets.getHeight(); + + if (scrollpane.getVerticalScrollBarVisibility() !is ScrollPane.NEVER) + reservedWidth += vBar.getPreferredSize().width; + if (scrollpane.getHorizontalScrollBarVisibility() !is ScrollPane.NEVER) + reservedHeight += hBar.getPreferredSize().height; + + if (wHint > -1) + wHint = Math.max(0, wHint - reservedWidth); + if (hHint > -1) + hHint = Math.max(0, hHint - reservedHeight); + + return scrollpane + .getViewport() + .getPreferredSize(wHint, hHint) + .getExpanded(reservedWidth, reservedHeight); +} + +/** + * @see dwtx.draw2d.LayoutManager#layout(IFigure) + */ +public void layout(IFigure parent) { + ScrollPane scrollpane = cast(ScrollPane)parent; + Viewport viewport = scrollpane.getViewport(); + ScrollBar hBar = scrollpane.getHorizontalScrollBar(), + vBar = scrollpane.getVerticalScrollBar(); + + ScrollPaneSolver.Result result = ScrollPaneSolver.solve( + parent.getClientArea(), + viewport, + scrollpane.getHorizontalScrollBarVisibility(), + scrollpane.getVerticalScrollBarVisibility(), + vBar.getPreferredSize().width, + hBar.getPreferredSize().height); + + if (result.showV) { + vBar.setBounds(new Rectangle( + result.viewportArea.right(), + result.viewportArea.y, + result.insets.right, + result.viewportArea.height)); + } + if (result.showH) { + hBar.setBounds(new Rectangle( + result.viewportArea.x, + result.viewportArea.bottom(), + result.viewportArea.width, + result.insets.bottom)); + } + vBar.setVisible(result.showV); + hBar.setVisible(result.showH); + + int vStepInc = vBar.getStepIncrement(); + int vPageInc = vBar.getRangeModel().getExtent() - vStepInc; + if (vPageInc < vStepInc) + vPageInc = vStepInc; + vBar.setPageIncrement(vPageInc); + + int hStepInc = hBar.getStepIncrement(); + int hPageInc = hBar.getRangeModel().getExtent() - hStepInc; + if (hPageInc < hStepInc) + hPageInc = hStepInc; + hBar.setPageIncrement(hPageInc); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ScrollPaneSolver.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ScrollPaneSolver.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ScrollPaneSolver; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Viewport; + +/** + * This class handles the calculation of solving for the area of a + * {@link dwtx.draw2d.ScrollPane}'s viewport and insets. Also determines if + * the horizontal and vertical scrollbars should be visible. + */ +public class ScrollPaneSolver { + +/** Scrollbar visibility constants -- never show scrollbars **/ +public static const int NEVER = 0; +/** Scrollbar visibility constants -- show scrollbars automatically **/ +public static const int AUTOMATIC = 1; +/** Scrollbar visibility constants -- always show scrollbars **/ +public static const int ALWAYS = 2; + +/** + * Container class for the results of ScrollPaneSolver's solve method + */ +public static class Result { + /** Show horizontal scrollbar bool **/ + public bool showH; + + /** Show vertical scrollbar bool **/ + public bool showV; + + /** Area of ScrollPane's viewport **/ + public Rectangle viewportArea; + + /** Insets of ScrollPane **/ + public Insets insets; +} + +/** + * Solves for the viewport area, insets, and visibility of horizontal and vertical + * scrollbars of a ScrollPane + * @param clientArea The ScrollPane's client area + * @param viewport The ScrollPane's Viewport + * @param hVis Horizontal scrollbar visibility + * @param vVis Vertical scrollbar visibility + * @param vBarWidth Width of vertical scrollbar + * @param hBarHeight Height of horizontal scrollbar + * @return the Result + */ +public static Result solve(Rectangle clientArea, Viewport viewport, int hVis, int vVis, + int vBarWidth, int hBarHeight) { + Result result = new Result(); + result.insets = new Insets(); + result.insets.bottom = hBarHeight; + result.insets.right = vBarWidth; + + Dimension available = clientArea.getSize(); + Dimension guaranteed = (new Dimension(available)).shrink( + (vVis is NEVER ? 0 : result.insets.right), + (hVis is NEVER ? 0 : result.insets.bottom)); + guaranteed.width = Math.max(guaranteed.width, 0); + guaranteed.height = Math.max(guaranteed.height, 0); + int wHint = guaranteed.width; + int hHint = guaranteed.height; + + Dimension preferred = viewport.getPreferredSize(wHint, hHint).getCopy(); + Insets viewportInsets = viewport.getInsets(); + /* + * This was calling viewport.getMinimumSize(), but viewport's minimum size was really + * small, and wasn't a function of its contents. + */ + Dimension viewportMinSize = new Dimension( + viewportInsets.getWidth(), viewportInsets.getHeight()); + if (viewport.getContents() !is null) { + if (viewport.getContentsTracksHeight() && hHint > -1) + hHint = Math.max(0, hHint - viewportInsets.getHeight()); + if (viewport.getContentsTracksWidth() && wHint > -1) + wHint = Math.max(0, wHint - viewportInsets.getWidth()); + viewportMinSize.expand( + viewport.getContents().getMinimumSize(wHint, hHint)); + } + + /* + * Adjust preferred size if tracking flags set. Basically, tracking is "compress view + * until its minimum size is reached". + */ + if (viewport.getContentsTracksHeight()) + preferred.height = viewportMinSize.height; + if (viewport.getContentsTracksWidth()) + preferred.width = viewportMinSize.width; + + bool none = available.contains(preferred), + both = !none && preferred.containsProper(guaranteed), + showV = both || preferred.height > available.height, + showH = both || preferred.width > available.width; + + //Adjust for visibility override flags + result.showV = vVis !is NEVER && (showV || vVis is ALWAYS); + result.showH = hVis !is NEVER && (showH || hVis is ALWAYS); + + if (!result.showV) + result.insets.right = 0; + if (!result.showH) + result.insets.bottom = 0; + result.viewportArea = clientArea.getCropped(result.insets); + viewport.setBounds(result.viewportArea); + return result; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Shape.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Shape.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,215 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Shape; + +import dwt.dwthelper.utils; +import dwtx.draw2d.Figure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.ColorConstants; + +/** + * Provides abstract support for a variety of shapes. + *

+ * When customizing shapes, you shouldn't override paintFigure(). Override fillShape() + * and outlineShape() methods instead. + */ +public abstract class Shape + : Figure +{ + +/** The width of this shape's outline. */ +protected int lineWidth = 1; +/** The line style to be used for this shape's outline. */ +protected int lineStyle; + +private bool + fill = true, + outline = true, + xorFill, + xorOutline; + +/** + * Default constructor. + * + * @since 2.0 + */ +public this() { + lineStyle = Graphics.LINE_SOLID; +} + +/** + * Fills the interior of the shape with the background color. + * @param graphics the graphics object + */ +protected abstract void fillShape(Graphics graphics); + +/** + * Returns the line style used to outline this shape. + * @return the line style + */ +public int getLineStyle() { + return lineStyle; +} + +/** + * Returns the line width of this shape's outline. + * @return the line width + */ +public int getLineWidth() { + return lineWidth; +} + +/** + * Returns false as shapes only draw themselves onto other figures, and + * generally dont have rectangular dimensions. + * + * @see Figure#isOpaque() + * @since 2.0 + */ +public bool isOpaque() { + return false; +} + +/** + * Outlines this shape using the foreground color. + * @param graphics the graphics object + */ +protected abstract void outlineShape(Graphics graphics); + +/** + * Paints the shape. Each shape has an outline to draw, and a region to fill within that + * outline. Disabled shapes must visually depict the disabled state. + * + * @see Figure#paintFigure(Graphics) + */ +public void paintFigure(Graphics graphics) { + if (!isEnabled()) { + graphics.translate(1, 1); + graphics.setBackgroundColor(ColorConstants.buttonLightest); + graphics.setForegroundColor(ColorConstants.buttonLightest); + if (fill) { + graphics.setXORMode(xorFill); + fillShape(graphics); + } + if (outline) { + graphics.setXORMode(xorOutline); + graphics.setLineStyle(lineStyle); + graphics.setLineWidth(lineWidth); + outlineShape(graphics); + } + graphics.setBackgroundColor(ColorConstants.buttonDarker); + graphics.setForegroundColor(ColorConstants.buttonDarker); + graphics.translate(-1, -1); + } + if (fill) { + graphics.setXORMode(xorFill); + fillShape(graphics); + } + if (outline) { + graphics.setXORMode(xorOutline); + graphics.setLineStyle(lineStyle); + graphics.setLineWidth(lineWidth); + outlineShape(graphics); + } +} + +/** + * Sets whether this shape should fill its region or not. It repaints this figure. + * + * @param b fill state + * @since 2.0 + */ +public void setFill(bool b) { + if (fill is b) + return; + fill = b; + repaint(); +} + +/** + * Sets whether XOR based fill should be used by the shape. It repaints this figure. + * + * @param b XOR fill state + * @since 2.0 + */ +public void setFillXOR(bool b) { + if (xorFill is b) + return; + xorFill = b; + repaint(); +} + +/** + * Sets the line width to be used to outline the shape. + * + * @param w the new width + * @since 2.0 + */ +public void setLineWidth(int w) { + if (lineWidth is w) + return; + lineWidth = w; + repaint(); +} + +/** + * Sets the style of line to be used by this shape. + * + * @param s the new line style + * @since 2.0 + */ +public void setLineStyle(int s) { + if (lineStyle is s) + return; + lineStyle = s; + repaint(); +} + +/** + * Sets whether the outline should be drawn for this shape. + * + * @param b true if the shape should be outlined + * @since 2.0 + */ +public void setOutline(bool b) { + if (outline is b) + return; + outline = b; + repaint(); +} + +/** + * Sets whether XOR based outline should be used for this shape. + * + * @param b true if the outline should be XOR'ed + * @since 2.0 + */ +public void setOutlineXOR(bool b) { + if (xorOutline is b) + return; + xorOutline = b; + repaint(); +} + +/** + * Sets whether XOR based fill and XOR based outline should be used for this shape. + * + * @param b true if the outline and fill should be XOR'ed + * @since 2.0 + */ +public void setXOR(bool b) { + xorOutline = xorFill = b; + repaint(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ShortestPathConnectionRouter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ShortestPathConnectionRouter.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,317 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ShortestPathConnectionRouter; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.PrecisionPoint; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.graph.Path; +import dwtx.draw2d.graph.ShortestPathRouter; +import dwtx.draw2d.AbstractRouter; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.LayoutListener; +import dwtx.draw2d.FigureListener; +import dwtx.draw2d.Connection; +import dwtx.draw2d.Bendpoint; + +/** + * Routes multiple connections around the children of a given container figure. + * @author Whitney Sorenson + * @author Randy Hudson + * @since 3.1 + */ +public final class ShortestPathConnectionRouter + : AbstractRouter +{ + +private class LayoutTracker : LayoutListenerStub { + public void postLayout(IFigure container) { + processLayout(); + } + public void remove(IFigure child) { + removeChild(child); + } + public void setConstraint(IFigure child, Object constraint) { + addChild(child); + } +} + +private Map constraintMap; +private Map figuresToBounds; +private Map connectionToPaths; +private bool isDirty; +private ShortestPathRouter algorithm; +private IFigure container; +private Set staleConnections; +private LayoutListener listener; + +private FigureListener figureListener; +private void initFigureListener(){ + figureListener = new class() FigureListener { + public void figureMoved(IFigure source) { + Rectangle newBounds = source.getBounds().getCopy(); + if (algorithm.updateObstacle(cast(Rectangle)figuresToBounds.get(cast(Object)source), newBounds)) { + queueSomeRouting(); + isDirty = true; + } + + figuresToBounds.put(cast(Object)source, newBounds); + } + }; +} +private bool ignoreInvalidate; + +/** + * Creates a new shortest path router with the given container. The container + * contains all the figure's which will be treated as obstacles for the connections to + * avoid. Any time a child of the container moves, one or more connections will be + * revalidated to process the new obstacle locations. The connections being routed must + * not be contained within the container. + * + * @param container the container + */ +public this(IFigure container) { + initFigureListener(); + constraintMap = new HashMap(); + algorithm = new ShortestPathRouter(); + staleConnections = new HashSet(); + listener = new LayoutTracker(); + isDirty = false; + algorithm = new ShortestPathRouter(); + this.container = container; +} + +void addChild(IFigure child) { + if (connectionToPaths is null) + return; + if (figuresToBounds.containsKey(cast(Object)child)) + return; + Rectangle bounds = child.getBounds().getCopy(); + algorithm.addObstacle(bounds); + figuresToBounds.put(cast(Object)child, bounds); + child.addFigureListener(figureListener); + isDirty = true; +} + +private void hookAll() { + figuresToBounds = new HashMap(); + for (int i = 0; i < container.getChildren().size(); i++) + addChild(cast(IFigure)container.getChildren().get(i)); + container.addLayoutListener(listener); +} + +private void unhookAll() { + container.removeLayoutListener(listener); + if (figuresToBounds !is null) { + Iterator figureItr = figuresToBounds.keySet().iterator(); + while (figureItr.hasNext()) { + //Must use iterator's remove to avoid concurrent modification + IFigure child = cast(IFigure)figureItr.next(); + figureItr.remove(); + removeChild(child); + } + figuresToBounds = null; + } +} + +/** + * Gets the constraint for the given {@link Connection}. The constraint is the paths + * list of bend points for this connection. + * + * @param connection The connection whose constraint we are retrieving + * @return The constraint + */ +public Object getConstraint(Connection connection) { + return constraintMap.get(cast(Object)connection); +} + +/** + * Returns the default spacing maintained on either side of a connection. The default + * value is 4. + * @return the connection spacing + * @since 3.2 + */ +public int getSpacing() { + return algorithm.getSpacing(); +} + +/** + * @see ConnectionRouter#invalidate(Connection) + */ +public void invalidate(Connection connection) { + if (ignoreInvalidate) + return; + staleConnections.add(cast(Object)connection); + isDirty = true; +} + +private void processLayout() { + if (staleConnections.isEmpty()) + return; + (cast(Connection)staleConnections.iterator().next()).revalidate(); +} + +private void processStaleConnections() { + Iterator iter = staleConnections.iterator(); + if (iter.hasNext() && connectionToPaths is null) { + connectionToPaths = new HashMap(); + hookAll(); + } + + while (iter.hasNext()) { + Connection conn = cast(Connection)iter.next(); + + Path path = cast(Path)connectionToPaths.get(cast(Object)conn); + if (path is null) { + path = new Path(cast(Object)conn); + connectionToPaths.put(cast(Object)conn, path); + algorithm.addPath(path); + } + + List constraint = cast(List)getConstraint(conn); + if (constraint is null) + constraint = Collections.EMPTY_LIST; + + Point start = conn.getSourceAnchor().getReferencePoint().getCopy(); + Point end = conn.getTargetAnchor().getReferencePoint().getCopy(); + + container.translateToRelative(start); + container.translateToRelative(end); + + path.setStartPoint(start); + path.setEndPoint(end); + + if (!constraint.isEmpty()) { + PointList bends = new PointList(constraint.size()); + for (int i = 0; i < constraint.size(); i++) { + Bendpoint bp = cast(Bendpoint)constraint.get(i); + bends.addPoint(bp.getLocation()); + } + path.setBendPoints(bends); + } else + path.setBendPoints(null); + + isDirty |= path.isDirty; + } + staleConnections.clear(); +} + +void queueSomeRouting() { + if (connectionToPaths is null || connectionToPaths.isEmpty()) + return; + try { + ignoreInvalidate = true; + (cast(Connection)connectionToPaths.keySet().iterator().next()) + .revalidate(); + } finally { + ignoreInvalidate = false; + } +} + +/** + * @see ConnectionRouter#remove(Connection) + */ +public void remove(Connection connection) { + staleConnections.remove(cast(Object)connection); + constraintMap.remove(cast(Object)connection); + if (connectionToPaths is null) + return; + Path path = cast(Path)connectionToPaths.remove(cast(Object)connection); + algorithm.removePath(path); + isDirty = true; + if (connectionToPaths.isEmpty()) { + unhookAll(); + connectionToPaths = null; + } else { + //Make sure one of the remaining is revalidated so that we can re-route again. + queueSomeRouting(); + } +} + +void removeChild(IFigure child) { + if (connectionToPaths is null) + return; + Rectangle bounds = child.getBounds().getCopy(); + bool change = algorithm.removeObstacle(bounds); + figuresToBounds.remove(cast(Object)child); + child.removeFigureListener(figureListener); + if (change) { + isDirty = true; + queueSomeRouting(); + } +} + +/** + * @see ConnectionRouter#route(Connection) + */ +public void route(Connection conn) { + if (isDirty) { + ignoreInvalidate = true; + processStaleConnections(); + isDirty = false; + List updated = algorithm.solve(); + Connection current; + for (int i = 0; i < updated.size(); i++) { + Path path = cast(Path) updated.get(i); + current = cast(Connection)path.data; + current.revalidate(); + + PointList points = path.getPoints().getCopy(); + Point ref1, ref2, start, end; + ref1 = new PrecisionPoint(points.getPoint(1)); + ref2 = new PrecisionPoint(points.getPoint(points.size() - 2)); + current.translateToAbsolute(ref1); + current.translateToAbsolute(ref2); + + start = current.getSourceAnchor().getLocation(ref1).getCopy(); + end = current.getTargetAnchor().getLocation(ref2).getCopy(); + + current.translateToRelative(start); + current.translateToRelative(end); + points.setPoint(start, 0); + points.setPoint(end, points.size() - 1); + + current.setPoints(points); + } + ignoreInvalidate = false; + } +} + +/** + * @see ConnectionRouter#setConstraint(Connection, Object) + */ +public void setConstraint(Connection connection, Object constraint) { + //Connection.setConstraint() already calls revalidate, so we know that a + // route() call will follow. + staleConnections.add(cast(Object)connection); + constraintMap.put(cast(Object)connection, constraint); + isDirty = true; +} + +/** + * Sets the default space that should be maintained on either side of a connection. This + * causes the connections to be separated from each other and from the obstacles. The + * default value is 4. + * + * @param spacing the connection spacing + * @since 3.2 + */ +public void setSpacing(int spacing) { + algorithm.setSpacing(spacing); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/SimpleEtchedBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/SimpleEtchedBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.SimpleEtchedBorder; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.SchemeBorder; +import dwtx.draw2d.Border; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.FigureUtilities; + +/** + * Provides a two pixel wide constant sized border, having an etched look. + */ +public final class SimpleEtchedBorder + : SchemeBorder +{ + +/** The singleton instance of this class */ +public static const Border singleton; + +/** The insets */ +protected static const Insets INSETS; + +static this(){ + singleton = new SimpleEtchedBorder(); + INSETS = new Insets(2); +} +/** + * Constructs a default border having a two pixel wide border. + * + * @since 2.0 + */ +protected this() { } + +/** + * Returns the Insets used by this border. This is a constant value of two pixels in each + * direction. + * @see Border#getInsets(IFigure) + */ +public Insets getInsets(IFigure figure) { + return new Insets(INSETS); +} + +/** + * Returns the opaque state of this border. This border is opaque and takes responsibility + * to fill the region it encloses. + * @see Border#isOpaque() + */ +public bool isOpaque() { + return true; +} + +/** + * @see Border#paint(IFigure, Graphics, Insets) + */ +public void paint(IFigure figure, Graphics g, Insets insets) { + Rectangle rect = getPaintRectangle(figure, insets); + FigureUtilities.paintEtchedBorder(g, rect); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/SimpleLoweredBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/SimpleLoweredBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.SimpleLoweredBorder; + +import dwt.dwthelper.utils; + +import dwt.graphics.Color; +import dwtx.draw2d.SchemeBorder; +import dwtx.draw2d.ColorConstants; + +/** + * Provides a lowered border. + */ +public final class SimpleLoweredBorder + : SchemeBorder +{ + +private static const Scheme DOUBLE; + +static this(){ + DOUBLE = new Scheme( + [ColorConstants.buttonDarkest, ColorConstants.buttonDarker], + [ColorConstants.buttonLightest, ColorConstants.button] ); +} +/** + * Constructs a SimpleLoweredBorder with the predefined button-pressed Scheme set as + * default. + * + * @since 2.0 + */ +public this() { + super(SCHEMES.BUTTON_PRESSED); +} + +/** + * Constructs a SimpleLoweredBorder with the width of all sides provided as input. If + * width is 2, this SimpleLoweredBorder will use the local DOUBLE Scheme, otherwise it + * will use the {@link SchemeBorder.SCHEMES#BUTTON_PRESSED} Scheme. + * + * @param width the width of all the sides of the border + * @since 2.0 + */ +public this(int width) { + super(width is 2 ? DOUBLE : SCHEMES.BUTTON_PRESSED); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/SimpleRaisedBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/SimpleRaisedBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.SimpleRaisedBorder; + +import dwt.dwthelper.utils; + +import dwt.graphics.Color; +import dwtx.draw2d.SchemeBorder; +import dwtx.draw2d.ColorConstants; + +/** + * Provides a raised border. + */ +public class SimpleRaisedBorder + : SchemeBorder +{ + +private static const Scheme DOUBLE; +static this(){ + DOUBLE = new Scheme( + [ColorConstants.buttonLightest, ColorConstants.button], + [ColorConstants.buttonDarkest, ColorConstants.buttonDarker] + ); +} + +/** + * Constructs a SimpleRaisedBorder with the predefined {@link SchemeBorder.SCHEMES#BUTTON_RAISED} + * Scheme set as default. + * + * @since 2.0 + */ +public this() { + super(SCHEMES.BUTTON_RAISED); +} + +/** + * Constructs a SimpleRaisedBorder with the width of all sides provided as input. If + * width is 2, this SimpleRaisedBorder will use the local DOUBLE Scheme, otherwise it will + * use the {@link SchemeBorder.SCHEMES#BUTTON_RAISED} Scheme. + * + * @param width the width of all the sides of the border + * @since 2.0 + */ +public this(int width) { + super(width is 2 ? DOUBLE : SCHEMES.BUTTON_RAISED); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/StackLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/StackLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.StackLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.AbstractHintLayout; +import dwtx.draw2d.IFigure; + +/** + * Figures using the StackLayout as their layout manager have their children placed on top + * of one another. Order of placement is determined by the order in which the children + * were added, first child added placed on the bottom. + */ +public class StackLayout + : AbstractHintLayout +{ + +/** + * Returns the minimum size required by the input container. This is the size of the + * largest child of the container, as all other children fit into this size. + * + * @see AbstractHintLayout#calculateMinimumSize(IFigure, int, int) + */ +protected Dimension calculateMinimumSize(IFigure figure, int wHint, int hHint) { + if (wHint > -1) + wHint = Math.max(0, wHint - figure.getInsets().getWidth()); + if (hHint > -1) + hHint = Math.max(0, hHint - figure.getInsets().getHeight()); + Dimension d = new Dimension(); + List children = figure.getChildren(); + IFigure child; + for (int i = 0; i < children.size(); i++) { + child = cast(IFigure)children.get(i); + if (!isObservingVisibility() || child.isVisible()) + d.union_(child.getMinimumSize(wHint, hHint)); + } + + d.expand(figure.getInsets().getWidth(), + figure.getInsets().getHeight()); + d.union_(getBorderPreferredSize(figure)); + return d; + +} + +/** + * Calculates and returns the preferred size of the given figure. This is the union of + * the preferred sizes of the widest and the tallest of all its children. + * + * @see AbstractLayout#calculatePreferredSize(IFigure, int, int) + */ +protected Dimension calculatePreferredSize(IFigure figure, int wHint, int hHint) { + if (wHint > -1) + wHint = Math.max(0, wHint - figure.getInsets().getWidth()); + if (hHint > -1) + hHint = Math.max(0, hHint - figure.getInsets().getHeight()); + Dimension d = new Dimension(); + List children = figure.getChildren(); + IFigure child; + for (int i = 0; i < children.size(); i++) { + child = cast(IFigure)children.get(i); + if (!isObservingVisibility() || child.isVisible()) + d.union_(child.getPreferredSize(wHint, hHint)); + } + + d.expand(figure.getInsets().getWidth(), + figure.getInsets().getHeight()); + d.union_(getBorderPreferredSize(figure)); + return d; +} + +/** + * @see dwtx.draw2d.LayoutManager#layout(IFigure) + */ +public void layout(IFigure figure) { + Rectangle r = figure.getClientArea(); + List children = figure.getChildren(); + IFigure child; + for (int i = 0; i < children.size(); i++) { + child = cast(IFigure)children.get(i); + child.setBounds(r); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/SubordinateUpdateManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/SubordinateUpdateManager.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.SubordinateUpdateManager; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.UpdateManager; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.GraphicsSource; + +/** + * @deprecated this class is not used + */ +public abstract class SubordinateUpdateManager + : UpdateManager +{ + +/** + * A root figure. + */ +protected IFigure root; + +/** + * A graphics source + */ +protected GraphicsSource graphicsSource; + +/** + * @see UpdateManager#addDirtyRegion(IFigure, int, int, int, int) + */ +public void addDirtyRegion(IFigure f, int x, int y, int w, int h) { + if (getSuperior() is null) + return; + getSuperior().addDirtyRegion(f, x, y, w, h); +} + +/** + * @see UpdateManager#addInvalidFigure(IFigure) + */ +public void addInvalidFigure(IFigure f) { + UpdateManager um = getSuperior(); + if (um is null) + return; + um.addInvalidFigure(f); +} + +/** + * Returns the host figure. + * @return the host figure + */ +protected abstract IFigure getHost(); + +/** + * Returns the superior update manager. + * @return the superior + */ +protected UpdateManager getSuperior() { + if (getHost().getParent() is null) + return null; + return getHost().getParent().getUpdateManager(); +} + +/** + * @see UpdateManager#performUpdate() + */ +public void performUpdate() { + UpdateManager um = getSuperior(); + if (um is null) + return; + um.performUpdate(); +} + +/** + * @see UpdateManager#performUpdate(Rectangle) + */ +public void performUpdate(Rectangle rect) { + UpdateManager um = getSuperior(); + if (um is null) + return; + um.performUpdate(rect); +} + +/** + * @see UpdateManager#setRoot(IFigure) + */ +public void setRoot(IFigure f) { + root = f; +} + +/** + * @see UpdateManager#setGraphicsSource(GraphicsSource) + */ +public void setGraphicsSource(GraphicsSource gs) { + graphicsSource = gs; +} +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/TextUtilities.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/TextUtilities.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +module dwtx.draw2d.TextUtilities; + +import dwt.dwthelper.utils; + + +import dwt.graphics.Font; +import dwt.graphics.FontMetrics; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.FigureUtilities; + +/** + * Provides miscellaneous text operations. Clients may subclass this class if + * necessary. + * + * @author crevells + * @since 3.4 + */ +public class TextUtilities { + + /** + * a singleton default instance + */ + public static TextUtilities INSTANCE; + + static this(){ + INSTANCE = new TextUtilities(); + } + + /** + * Returns the Dimensions of s in Font f. + * + * @param s + * the string + * @param f + * the font + * @return the dimensions of the given string + */ + public Dimension getStringExtents(String s, Font f) { + return FigureUtilities.getStringExtents(s, f); + } + + /** + * Returns the Dimensions of the given text, converting newlines and tabs + * appropriately. + * + * @param s + * the text + * @param f + * the font + * @return the dimensions of the given text + */ + public Dimension getTextExtents(String s, Font f) { + return FigureUtilities.getTextExtents(s, f); + } + + /** + * Gets the font's ascent. + * + * @param font + * @return the font's ascent + */ + public int getAscent(Font font) { + FontMetrics fm = FigureUtilities.getFontMetrics(font); + return fm.getHeight() - fm.getDescent(); + } + + /** + * Gets the font's descent. + * + * @param font + * @return the font's descent + */ + public int getDescent(Font font) { + return FigureUtilities.getFontMetrics(font).getDescent(); + } + + /** + * Returns the largest substring of s in Font f that can be + * confined to the number of pixels in availableWidth. + * + * @param s + * the original string + * @param f + * the font + * @param availableWidth + * the available width + * @return the largest substring that fits in the given width + */ + public int getLargestSubstringConfinedTo(String s, Font f, + int availableWidth) { + FontMetrics metrics = FigureUtilities.getFontMetrics(f); + int min, max; + float avg = metrics.getAverageCharWidth(); + min = 0; + max = s.length + 1; + + // The size of the current guess + int guess = 0, guessSize = 0; + while ((max - min) > 1) { + // Pick a new guess size + // New guess is the last guess plus the missing width in pixels + // divided by the average character size in pixels + guess = guess + cast(int) ((availableWidth - guessSize) / avg); + + if (guess >= max) + guess = max - 1; + if (guess <= min) + guess = min + 1; + + // Measure the current guess + guessSize = getTextExtents(s.substring(0, guess), f).width; + + if (guessSize < availableWidth) + // We did not use the available width + min = guess; + else + // We exceeded the available width + max = guess; + } + return min; + } +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/TitleBarBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/TitleBarBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.TitleBarBorder; + +import dwt.dwthelper.utils; + + + +import dwt.graphics.Color; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.AbstractLabeledBorder; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.ColorConstants; +import dwtx.draw2d.PositionConstants; + +/** + * Border provides a title bar on the Figure for which this is the border of. Generally + * used in conjunction with other borders to create window-like effects. Also provides for + * alignment of the text in the bar. + * + * @see FrameBorder + */ +public class TitleBarBorder + : AbstractLabeledBorder +{ + +private static Color defaultColor; + +private int textAlignment; +private Insets padding; +private Color fillColor; + +static this(){ + defaultColor = ColorConstants.menuBackgroundSelected; +} + +/** + * Constructs a TitleBarBorder with its label set to the name of this class. + * + * @since 2.0 + */ +public this() { + textAlignment = PositionConstants.LEFT; + padding = new Insets(1, 3, 2, 2); + fillColor = defaultColor; +} + +/** + * Constructs a TitleBarBorder with its label set to the passed String. + * + * @param s text of the label + * @since 2.0 + */ +public this(String s) { + this(); + setLabel(s); +} + +/** + * Calculates and returns the Insets for this border. + * + * @param figure the figure on which Insets calculations are based + * @return the calculated Insets + * @since 2.0 + */ +protected Insets calculateInsets(IFigure figure) { + return new Insets(getTextExtents(figure).height + padding.getHeight(), 0, 0, 0); +} + +/** + * Returns the background Color of this TitleBarBorder. + * @return the background color + * @since 2.0 + */ +protected Color getBackgroundColor() { + return fillColor; +} + +/** + * Returns this TitleBarBorder's padding. Padding provides spacing along the sides of the + * TitleBarBorder. The default value is no padding along all sides. + * + * @return the Insets representing the space along the sides of the TitleBarBorder + * @since 2.0 + */ +protected Insets getPadding() { + return padding; +} + +/** + * Returns the alignment of the text in the title bar. Possible values are + * {@link PositionConstants#LEFT}, {@link PositionConstants#CENTER} and + * {@link PositionConstants#RIGHT}. + * + * @return the text alignment + * @since 2.0 + */ +public int getTextAlignment() { + return textAlignment; +} + +/** + * Returns true thereby filling up all the contents within its boundaries, + * eleminating the need by the figure to clip the boundaries and do the same. + * + * @see Border#isOpaque() + */ +public bool isOpaque() { + return true; +} + +/** + * @see Border#paint(IFigure, Graphics, Insets) + */ +public void paint(IFigure figure, Graphics g, Insets insets) { + tempRect.setBounds(getPaintRectangle(figure, insets)); + Rectangle rec = tempRect; + rec.height = Math.min(rec.height, getTextExtents(figure).height + padding.getHeight()); + g.clipRect(rec); + g.setBackgroundColor(fillColor); + g.fillRectangle(rec); + + int x = rec.x + padding.left; + int y = rec.y + padding.top; + + int textWidth = getTextExtents(figure).width; + int freeSpace = rec.width - padding.getWidth() - textWidth; + + if (getTextAlignment() is PositionConstants.CENTER) + freeSpace /= 2; + if (getTextAlignment() !is PositionConstants.LEFT) + x += freeSpace; + + g.setFont(getFont(figure)); + g.setForegroundColor(getTextColor()); + g.drawString(getLabel(), x, y); +} + +/** + * Sets the background color of the area within the boundaries of this border. This is + * required as this border takes responsibility for filling up the region, as + * TitleBarBorders are always opaque. + * + * @param color the background color + * @since 2.0 + */ +public void setBackgroundColor(Color color) { + fillColor = color; +} + +/** + * Sets the padding space to be applied on all sides of the border. The default value is + * no padding on all sides. + * + * @param all the value of the padding on all sides + * @since 2.0 + */ +public void setPadding(int all) { + padding = new Insets(all); + invalidate(); +} + +/** + * Sets the padding space of this TitleBarBorder to the passed value. The default value is + * no padding on all sides. + * + * @param pad the padding + * @since 2.0 + */ +public void setPadding(Insets pad) { + padding = pad; invalidate(); +} + +/** + * Sets the alignment of the text in the title bar. Possible values are + * {@link PositionConstants#LEFT}, {@link PositionConstants#CENTER} and + * {@link PositionConstants#RIGHT}. + * + * @param align the new text alignment + * @since 2.0 + */ +public void setTextAlignment(int align_) { + textAlignment = align_; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Toggle.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Toggle.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Toggle; + +import dwt.dwthelper.utils; + +import dwt.graphics.Image; +import dwtx.draw2d.Clickable; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Label; + +/** + * Basic Rule for Toggle: Whoever creates the toggle is reponsible for response changes + * for it (selection, rollover, etc). Only {@link dwtx.draw2d.CheckBox} does its + * own listening. + */ +public class Toggle + : Clickable +{ + +/** + * Constructs a Toggle with no text or icon. + * + * @since 2.0 + */ +public this() { + super(); + setStyle(STYLE_TOGGLE); +} + +/** + * Constructs a Toggle with passed text and icon + * + * @param text the text + * @param icon the icon + * @since 2.0 + */ +public this(String text, Image icon) { + super(new Label(text, icon), STYLE_TOGGLE); +} + +/** + * Constructs a Toggle with passed IFigure as its contents. + * + * @param contents the contents + * @since 2.0 + */ +public this(IFigure contents) { + super(contents, STYLE_TOGGLE); +} + +/** + * Constructs a Toggle with the passed figure as its contents and the given style. + * @param contents the contents + * @param style the style + */ +public this(IFigure contents, int style) { + super(contents, style); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ToggleButton.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ToggleButton.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ToggleButton; + +import dwt.dwthelper.utils; + + + +import dwt.graphics.Image; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Toggle; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.Label; +import dwtx.draw2d.ColorConstants; + +/** + * A Toggle that appears like a 3-dimensional button. + */ +public class ToggleButton + : Toggle +{ + +/** This ToggleButton's Label **/ +protected Label label = null; + +/** + * Constructs a new ToggleButton with no initial contents. + */ +public this() { + setStyle(STYLE_BUTTON | STYLE_TOGGLE); +} + +/** + * Constructs a ToggleButton with the passed IFigure as its contents. + * + * @param contents the contents of the toggle button + * @since 2.0 + */ +public this(IFigure contents) { + super(contents, STYLE_BUTTON | STYLE_TOGGLE); +} + +/** + * Constructs a ToggleButton with the passed string as its text. + * + * @param text the text to be displayed on the button + * @since 2.0 + */ +public this(String text) { + this(text, null); +} + +/** + * Constructs a ToggleButton with a Label containing the passed text and icon. + * + * @param text the text + * @param normalIcon the icon + * @since 2.0 + */ +public this(String text, Image normalIcon) { + super(new Label(text, normalIcon), STYLE_BUTTON | STYLE_TOGGLE); +} + +/** + * @see dwtx.draw2d.Figure#paintFigure(Graphics) + */ +protected void paintFigure(Graphics graphics) { + if (isSelected() && isOpaque()) { + fillCheckeredRectangle(graphics); + } else { + super.paintFigure(graphics); + } +} + +/** + * Draws a checkered pattern to emulate a toggle button that is in the selected state. + * @param graphics The Graphics object used to paint + */ +protected void fillCheckeredRectangle(Graphics graphics) { + graphics.setBackgroundColor(ColorConstants.button); + graphics.setForegroundColor(ColorConstants.buttonLightest); + Rectangle rect = getClientArea(Rectangle.SINGLETON).crop(new Insets(1, 1, 0, 0)); + graphics.fillRectangle(rect.x, rect.y, rect.width, rect.height); + + graphics.clipRect(rect); + graphics.translate(rect.x, rect.y); + int n = rect.width + rect.height; + for (int i = 1; i < n; i += 2) { + graphics.drawLine(0, i, i, 0); + } + graphics.restoreState(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ToggleModel.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ToggleModel.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ToggleModel; + +import dwt.dwthelper.utils; +import dwtx.draw2d.ButtonModel; + +/** + * ButtonModel that supports toggle buttons. + */ +public class ToggleModel + : ButtonModel +{ + +/** + * Notifies any ActionListeners on this ButtonModel that an action has been performed. + * Sets this ButtonModel's selection to be the opposite of what it was. + * + * @since 2.0 + */ +public void fireActionPerformed() { + setSelected(!isSelected()); + super.fireActionPerformed(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ToolTipHelper.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ToolTipHelper.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,174 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ToolTipHelper; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; +import dwtx.dwtxhelper.Timer; +import dwtx.dwtxhelper.TimerTask; + +import dwt.DWT; +import dwt.events.MouseTrackAdapter; +import dwt.graphics.Point; +import dwt.widgets.Display; +static import dwt.widgets.Control; +static import dwt.graphics.Rectangle; +static import dwt.events.MouseEvent; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.PopUpHelper; +import dwtx.draw2d.ColorConstants; + +/** + * This class is used by SWTEventDispatcher as support to display Figure tooltips on a + * mouse hover event. Tooltips are drawn directly below the cursor unless the display + * does not allow, in which case the tooltip will be drawn directly above the cursor. + * Tooltips will be displayed with a LineBorder. The background of the tooltips will be + * the standard DWT tooltipBackground color unless the Figure's tooltip has set its own + * background. + */ +public class ToolTipHelper + : PopUpHelper +{ + +private Timer timer; +private IFigure currentTipSource; + +/** + * Constructs a ToolTipHelper to be associated with Control c. + * + * @param c the control + * @since 2.0 + */ +public this(dwt.widgets.Control.Control c) { + super(c, DWT.TOOL | DWT.ON_TOP); + getShell().setBackground(ColorConstants.tooltipBackground); + getShell().setForeground(ColorConstants.tooltipForeground); +} + +/* + * Calculates the location where the tooltip will be painted. Returns this as a Point. + * Tooltip will be painted directly below the cursor if possible, otherwise it will be + * painted directly above cursor. + */ +private Point computeWindowLocation(IFigure tip, int eventX, int eventY) { + dwt.graphics.Rectangle.Rectangle clientArea = control.getDisplay().getClientArea(); + Point preferredLocation = new Point(eventX, eventY + 26); + + Dimension tipSize = getLightweightSystem() + .getRootFigure() + .getPreferredSize() + .getExpanded(getShellTrimSize()); + + // Adjust location if tip is going to fall outside display + if (preferredLocation.y + tipSize.height > clientArea.height) + preferredLocation.y = eventY - tipSize.height; + + if (preferredLocation.x + tipSize.width > clientArea.width) + preferredLocation.x -= (preferredLocation.x + tipSize.width) - clientArea.width; + + return preferredLocation; +} + +/** + * Sets the LightWeightSystem's contents to the passed tooltip, and displays the tip. The + * tip will be displayed only if the tip source is different than the previously viewed + * tip source. (i.e. The cursor has moved off of the previous tooltip source figure.) + *

+ * The tooltip will be painted directly below the cursor if possible, otherwise it will be + * painted directly above cursor. + * + * @param hoverSource the figure over which the hover event was fired + * @param tip the tooltip to be displayed + * @param eventX the x coordinate of the hover event + * @param eventY the y coordinate of the hover event + * @since 2.0 + */ +public void displayToolTipNear(IFigure hoverSource, IFigure tip, int eventX, int eventY) { + if (tip !is null && hoverSource !is currentTipSource) { + getLightweightSystem().setContents(tip); + Point displayPoint = computeWindowLocation(tip, eventX, eventY); + Dimension shellSize = getLightweightSystem().getRootFigure() + .getPreferredSize().getExpanded(getShellTrimSize()); + setShellBounds(displayPoint.x, displayPoint.y, shellSize.width, shellSize.height); + show(); + currentTipSource = hoverSource; + timer = new Timer(true); + timer.schedule(new class() TimerTask { + public void run() { + Display.getDefault().syncExec(dgRunnable( { + hide(); + timer.cancel(); + })); + } + }, 5000); + } +} + +/** + * Disposes of the tooltip's shell and kills the timer. + * + * @see PopUpHelper#dispose() + */ +public void dispose() { + if (isShowing()) { + timer.cancel(); + hide(); + } + getShell().dispose(); +} + +/** + * @see PopUpHelper#hookShellListeners() + */ +protected void hookShellListeners() { + // Close the tooltip window if the mouse enters the tooltip + getShell().addMouseTrackListener(new class() MouseTrackAdapter { + public void mouseEnter(dwt.events.MouseEvent.MouseEvent e) { + hide(); + currentTipSource = null; + timer.cancel(); + } + }); +} + +/** + * Displays the hover source's tooltip if a tooltip of another source is currently being + * displayed. + * + * @param figureUnderMouse the figure over which the cursor was when called + * @param tip the tooltip to be displayed + * @param eventX the x coordinate of the cursor + * @param eventY the y coordinate of the cursor + * @since 2.0 + */ +public void updateToolTip(IFigure figureUnderMouse, IFigure tip, int eventX, int eventY) { + /* If the cursor is not on any Figures, it has been moved + off of the control. Hide the tool tip. */ + if (figureUnderMouse is null) { + if (isShowing()) { + hide(); + timer.cancel(); + } + } + // Makes tooltip appear without a hover event if a tip is currently being displayed + if (isShowing() && figureUnderMouse !is currentTipSource) { + hide(); + timer.cancel(); + displayToolTipNear(figureUnderMouse, tip, eventX, eventY); + } else if (!isShowing() && figureUnderMouse !is currentTipSource) + currentTipSource = null; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ToolbarLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ToolbarLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,428 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +module dwtx.draw2d.ToolbarLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.Transposer; +import dwtx.draw2d.AbstractHintLayout; +import dwtx.draw2d.IFigure; + +/** + * Arranges figures in a single row or column. Orientation can be set to produce either + * a row or column layout. This layout tries to fit all children within the parent's + * client area. To do this, it compresses the children by some amount, but will not + * compress them smaller than their minimum size. If a child's preferred size is smaller + * than the row's or column's minor dimension, the layout can be configured to stretch the + * child. + */ +public class ToolbarLayout + : AbstractHintLayout +{ + +/** Space in pixels between Figures **/ +protected int spacing; +/** Sets whether children should "stretch" with their container **/ +protected bool matchWidth; +/** Orientation of layout **/ +protected bool horizontal = false; +/** Alignment of layout **/ +protected int minorAlignment; + +/** Constant for center alignment **/ +public static const int ALIGN_CENTER = 0; +/** Constant for top-left alignment **/ +public static const int ALIGN_TOPLEFT = 1; +/** Constant for bottom-right alignment **/ +public static const int ALIGN_BOTTOMRIGHT = 2; + +/** Constant for horizontal alignment **/ +public static const bool HORIZONTAL = true; +/** Constant for vertical alignment **/ +public static const bool VERTICAL = false; + +/** Transposer object used in layout calculations **/ +protected Transposer transposer; +private void instanceInit() { + transposer = new Transposer(); + transposer.setEnabled(horizontal); +} + +/** + * Constructs a vertically oriented ToolbarLayout with child spacing of 0 pixels, + * matchWidth true, and {@link #ALIGN_TOPLEFT} alignment. + * + * @since 2.0 + */ +public this() { + instanceInit(); + spacing = 0; + matchWidth = true; + minorAlignment = ALIGN_TOPLEFT; + horizontal = false; +} + +/** + * Constructs a ToolbarLayout with a specified orientation. Default values are: child + * spacing 0 pixels, matchWidth false, and {@link #ALIGN_TOPLEFT} + * alignment. + * + * @param isHorizontal whether the children are oriented horizontally + * @since 2.0 + */ +public this(bool isHorizontal) { + instanceInit(); + horizontal = isHorizontal; + transposer.setEnabled(horizontal); + spacing = 0; + matchWidth = false; + minorAlignment = ALIGN_TOPLEFT; +} + +private Dimension calculateChildrenSize(List children, int wHint, int hHint, + bool preferred) { + Dimension childSize; + IFigure child; + int height = 0, width = 0; + for (int i = 0; i < children.size(); i++) { + child = cast(IFigure)children.get(i); + childSize = transposer.t(preferred ? getChildPreferredSize(child, wHint, hHint) + : getChildMinimumSize(child, wHint, hHint)); + height += childSize.height; + width = Math.max(width, childSize.width); + } + return new Dimension(width, height); +} + +/** + * Calculates the minimum size of the container based on the given hints. If this is a + * vertically-oriented Toolbar Layout, then only the widthHint is respected (which means + * that the children can be as tall as they desire). In this case, the minimum width + * is that of the widest child, and the minimum height is the sum of the minimum + * heights of all children, plus the spacing between them. The border and insets of the + * container figure are also accounted for. + * + * @param container the figure whose minimum size has to be calculated + * @param wHint the width hint (the desired width of the container) + * @param hHint the height hint (the desired height of the container) + * @return the minimum size of the container + * @see #getMinimumSize(IFigure, int, int) + * @since 2.1 + */ +protected Dimension calculateMinimumSize(IFigure container, int wHint, int hHint) { + Insets insets = container.getInsets(); + if (isHorizontal()) { + wHint = -1; + if (hHint >= 0) + hHint = Math.max(0, hHint - insets.getHeight()); + } else { + hHint = -1; + if (wHint >= 0) + wHint = Math.max(0, wHint - insets.getWidth()); + } + + List children = container.getChildren(); + Dimension minSize = calculateChildrenSize(children, wHint, hHint, false); + // Do a second pass, if necessary + if (wHint >= 0 && minSize.width > wHint) { + minSize = calculateChildrenSize(children, minSize.width, hHint, false); + } else if (hHint >= 0 && minSize.width > hHint) { + minSize = calculateChildrenSize(children, wHint, minSize.width, false); + } + + minSize.height += Math.max(0, children.size() - 1) * spacing; + return transposer.t(minSize) + .expand(insets.getWidth(), insets.getHeight()) + .union_(getBorderPreferredSize(container)); +} + +/** + * Calculates the preferred size of the container based on the given hints. If this is a + * vertically-oriented Toolbar Layout, then only the widthHint is respected (which means + * that the children can be as tall as they desire). In this case, the preferred width + * is that of the widest child, and the preferred height is the sum of the preferred + * heights of all children, plus the spacing between them. The border and insets of the + * container figure are also accounted for. + * + * @param container the figure whose preferred size has to be calculated + * @param wHint the width hint (the desired width of the container) + * @param hHint the height hint (the desired height of the container) + * @return the preferred size of the container + * @see #getPreferredSize(IFigure, int, int) + * @since 2.0 + */ +protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) { + Insets insets = container.getInsets(); + if (isHorizontal()) { + wHint = -1; + if (hHint >= 0) + hHint = Math.max(0, hHint - insets.getHeight()); + } else { + hHint = -1; + if (wHint >= 0) + wHint = Math.max(0, wHint - insets.getWidth()); + } + + List children = container.getChildren(); + Dimension prefSize = calculateChildrenSize(children, wHint, hHint, true); + // Do a second pass, if necessary + if (wHint >= 0 && prefSize.width > wHint) { + prefSize = calculateChildrenSize(children, prefSize.width, hHint, true); + } else if (hHint >= 0 && prefSize.width > hHint) { + prefSize = calculateChildrenSize(children, wHint, prefSize.width, true); + } + + prefSize.height += Math.max(0, children.size() - 1) * spacing; + return transposer.t(prefSize) + .expand(insets.getWidth(), insets.getHeight()) + .union_(getBorderPreferredSize(container)); +} + +/** + * @param child the figure whose minimum size is to be determined + * @param wHint the width hint + * @param hHint the height hint + * @return the given figure's minimum size + * @since 3.3 + */ +protected Dimension getChildMinimumSize(IFigure child, int wHint, int hHint) { + return child.getMinimumSize(wHint, hHint); +} + +/** + * @param child the figure whose preferred size is to be determined + * @param wHint the width hint + * @param hHint the height hint + * @return given figure's preferred size + * @since 3.3 + */ +protected Dimension getChildPreferredSize(IFigure child, int wHint, int hHint) { + return child.getPreferredSize(wHint, hHint); +} + +/** + * Returns the minor aligment of the layout. Minor minor axis is the axis perpindicular + * to the overall orientation set in the contructor. + * @return the minor aligment + */ +public int getMinorAlignment() { + return minorAlignment; +} + +/** + * @return the spacing between children + */ +public int getSpacing() { + return spacing; +} + +/** + * Returns true if stretch minor axis has been enabled. The default value is + * false. + * @return true if stretch minor axis is enabled + */ +public bool getStretchMinorAxis() { + return matchWidth; +} + +/** + * @return whether the orientation of the layout is horizontal + * @since 2.0 + */ +public bool isHorizontal() { + return horizontal; +} + +/** + * @see dwtx.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure) + */ +protected bool isSensitiveHorizontally(IFigure parent) { + return !isHorizontal(); +} + +/** + * @see dwtx.draw2d.AbstractHintLayout#isSensitiveVertically(IFigure) + */ +protected bool isSensitiveVertically(IFigure parent) { + return isHorizontal(); +} + +/** + * @see dwtx.draw2d.LayoutManager#layout(IFigure) + */ +public void layout(IFigure parent) { + List children = parent.getChildren(); + int numChildren = children.size(); + Rectangle clientArea = transposer.t(parent.getClientArea()); + int x = clientArea.x; + int y = clientArea.y; + int availableHeight = clientArea.height; + + Dimension prefSizes [] = new Dimension[numChildren]; + Dimension minSizes [] = new Dimension[numChildren]; + + // Calculate the width and height hints. If it's a vertical ToolBarLayout, + // then ignore the height hint (set it to -1); otherwise, ignore the + // width hint. These hints will be passed to the children of the parent + // figure when getting their preferred size. + int wHint = -1; + int hHint = -1; + if (isHorizontal()) { + hHint = parent.getClientArea(Rectangle.SINGLETON).height; + } else { + wHint = parent.getClientArea(Rectangle.SINGLETON).width; + } + + /* + * Calculate sum of preferred heights of all children(totalHeight). + * Calculate sum of minimum heights of all children(minHeight). + * Cache Preferred Sizes and Minimum Sizes of all children. + * + * totalHeight is the sum of the preferred heights of all children + * totalMinHeight is the sum of the minimum heights of all children + * prefMinSumHeight is the sum of the difference between all children's + * preferred heights and minimum heights. (This is used as a ratio to + * calculate how much each child will shrink). + */ + IFigure child; + int totalHeight = 0; + int totalMinHeight = 0; + int prefMinSumHeight = 0; + + for (int i = 0; i < numChildren; i++) { + child = cast(IFigure)children.get(i); + + prefSizes[i] = transposer.t(getChildPreferredSize(child, wHint, hHint)); + minSizes[i] = transposer.t(getChildMinimumSize(child, wHint, hHint)); + + totalHeight += prefSizes[i].height; + totalMinHeight += minSizes[i].height; + } + totalHeight += (numChildren - 1) * spacing; + totalMinHeight += (numChildren - 1) * spacing; + prefMinSumHeight = totalHeight - totalMinHeight; + /* + * The total amount that the children must be shrunk is the + * sum of the preferred Heights of the children minus + * Max(the available area and the sum of the minimum heights of the children). + * + * amntShrinkHeight is the combined amount that the children must shrink + * amntShrinkCurrentHeight is the amount each child will shrink respectively + */ + int amntShrinkHeight = totalHeight - Math.max(availableHeight, totalMinHeight); + + if (amntShrinkHeight < 0) { + amntShrinkHeight = 0; + } + + for (int i = 0; i < numChildren; i++) { + int amntShrinkCurrentHeight = 0; + int prefHeight = prefSizes[i].height; + int minHeight = minSizes[i].height; + int prefWidth = prefSizes[i].width; + int minWidth = minSizes[i].width; + Rectangle newBounds = new Rectangle(x, y, prefWidth, prefHeight); + + child = cast(IFigure)children.get(i); + if (prefMinSumHeight !is 0) + amntShrinkCurrentHeight = + (prefHeight - minHeight) * amntShrinkHeight / (prefMinSumHeight); + + int width = Math.min(prefWidth, transposer.t(child.getMaximumSize()).width); + if (matchWidth) + width = transposer.t(child.getMaximumSize()).width; + width = Math.max(minWidth, Math.min(clientArea.width, width)); + newBounds.width = width; + + int adjust = clientArea.width - width; + switch (minorAlignment) { + case ALIGN_TOPLEFT: + adjust = 0; + break; + case ALIGN_CENTER: + adjust /= 2; + break; + case ALIGN_BOTTOMRIGHT: + break; + } + newBounds.x += adjust; + newBounds.height -= amntShrinkCurrentHeight; + child.setBounds(transposer.t(newBounds)); + + amntShrinkHeight -= amntShrinkCurrentHeight; + prefMinSumHeight -= (prefHeight - minHeight); + y += newBounds.height + spacing; + } +} + +/** + * Sets the alignment of the children contained in the layout. Possible values are + * {@link #ALIGN_CENTER}, {@link #ALIGN_BOTTOMRIGHT} and {@link #ALIGN_TOPLEFT}. + * + * @param align the minor alignment + * @since 2.0 + */ +public void setMinorAlignment(int align_) { + minorAlignment = align_; +} + +/** + * Sets the amount of space between children. + * + * @param space the amount of space between children + * @since 2.0 + */ +public void setSpacing(int space) { + spacing = space; +} + +/** + * Sets children's width (if vertically oriented) or height (if horizontally oriented) to + * stretch with their container. + * + * @deprecated use {@link #setStretchMinorAxis(bool)} + * @param match whether to stretch children + * @since 2.0 + */ +public void setMatchWidth(bool match) { + matchWidth = match; +} + +/** + * Causes children that are smaller in the dimension of the minor axis to be stretched to + * fill the minor axis. The minor axis is the opposite of the orientation. + * @param stretch whether to stretch children + * @since 2.0 + */ +public void setStretchMinorAxis(bool stretch) { + matchWidth = stretch; +} + +/** + * Sets the orientation of the children in the ToolbarLayout. + * + * @param flag whether the orientation should be vertical + * @since 2.0 + */ +public void setVertical(bool flag) { + if (horizontal !is flag) return; + invalidate(); + horizontal = !flag; + transposer.setEnabled(horizontal); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/TreeSearch.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/TreeSearch.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.TreeSearch; + +import dwt.dwthelper.utils; +import dwtx.draw2d.IFigure; + +/** + * A helper used in depth-first searches of a figure subgraph. + * @author hudsonr + * @since 2.1 + */ +public interface TreeSearch { + +/** + * Returns true if the given figure is accepted by the search. + * @param figure the current figure in the traversal + * @return true if the figure is accepted + */ +bool accept(IFigure figure); + +/** + * Returns true if the figure and all of its contained figures should be + * pruned from the search. + * @param figure the current figure in the traversal + * @return true if the subgraph should be pruned + */ +bool prune(IFigure figure); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Triangle.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Triangle.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Triangle; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Shape; +import dwtx.draw2d.Orientable; +import dwtx.draw2d.Graphics; + +/** + * A triangular graphical figure. + */ +public final class Triangle + : Shape + , Orientable +{ + +/** The direction this triangle will face. Possible values are + * {@link PositionConstants#NORTH}, {@link PositionConstants#SOUTH}, + * {@link PositionConstants#EAST} and {@link PositionConstants#WEST}. + */ +protected int direction = NORTH; +/** The orientation of this triangle. Possible values are {@link Orientable#VERTICAL} + * and {@link Orientable#HORIZONTAL}. + */ +protected int orientation = VERTICAL; + +/** The points of the triangle. */ +protected PointList triangle; + +public this(){ + triangle = new PointList(3); +} +/** + * @see Shape#fillShape(Graphics) + */ +protected void fillShape(Graphics g) { + g.fillPolygon(triangle); +} + +/** + * @see Shape#outlineShape(Graphics) + */ +protected void outlineShape(Graphics g) { + g.drawPolygon(triangle); +} + +/** + * @see Figure#primTranslate(int, int) + */ +public void primTranslate(int dx, int dy) { + super.primTranslate(dx, dy); + triangle.translate(dx, dy); +} + +/** + * @see Orientable#setDirection(int) + */ +public void setDirection(int value) { + if ((value & (NORTH | SOUTH)) !is 0) + orientation = VERTICAL; + else + orientation = HORIZONTAL; + direction = value; + revalidate(); + repaint(); +} + +/** + * @see Orientable#setOrientation(int) + */ +public void setOrientation(int value) { + if (orientation is VERTICAL && value is HORIZONTAL) { + if (direction is NORTH) setDirection(WEST); + else setDirection(EAST); + } + if (orientation is HORIZONTAL && value is VERTICAL) { + if (direction is WEST) setDirection(NORTH); + else setDirection(SOUTH); + } +} + +/** + * @see IFigure#validate() + */ +public void validate() { + super.validate(); + Rectangle r = new Rectangle(); + r.setBounds(getBounds()); + r.crop(getInsets()); + r.resize(-1, -1); + int size; + switch (direction & (NORTH | SOUTH)) { + case 0: //East or west. + size = Math.min(r.height / 2, r.width); + r.x += (r.width - size) / 2; + break; + default: //North or south + size = Math.min(r.height, r.width / 2); + r.y += (r.height - size) / 2; + break; + } + + size = Math.max(size, 1); //Size cannot be negative + + Point head, p2, p3; + + switch (direction) { + case NORTH: + head = new Point(r.x + r.width / 2, r.y); + p2 = new Point (head.x - size, head.y + size); + p3 = new Point (head.x + size, head.y + size); + break; + case SOUTH: + head = new Point (r.x + r.width / 2, r.y + size); + p2 = new Point (head.x - size, head.y - size); + p3 = new Point (head.x + size, head.y - size); + break; + case WEST: + head = new Point (r.x, r.y + r.height / 2); + p2 = new Point (head.x + size, head.y - size); + p3 = new Point (head.x + size, head.y + size); + break; + default: + head = new Point(r.x + size, r.y + r.height / 2); + p2 = new Point(head.x - size, head.y - size); + p3 = new Point(head.x - size, head.y + size); + + } + triangle.removeAllPoints(); + triangle.addPoint(head); + triangle.addPoint(p2); + triangle.addPoint(p3); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/UpdateListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/UpdateListener.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.UpdateListener; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Rectangle; + +/** + * An interface used to notify listeners that the listened to object is updating. + */ +public interface UpdateListener { + +/** + * Notifies the listener that the listened to object is painting. The damage rectangle + * may be null or empty. This indicates the dirty regions were clipped or not visible. + * But for objects such as the {@link dwtx.draw2d.parts.Thumbnail}, notification + * still needs to occur. The map of dirty regions is passed to allow the listener to + * determine if it needs to update, for instance when a particular figure is painting. + * + * @param damage The area being painted + * @param dirtyRegions a Map of figures to their dirty regions + */ +void notifyPainting(Rectangle damage, Map dirtyRegions); + +/** + * Notifies the listener that the listened to object is validating. + */ +void notifyValidating(); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/UpdateManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/UpdateManager.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,202 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.UpdateManager; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; +import dwtx.dwtxhelper.Collection; + +import dwt.graphics.GC; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.UpdateListener; +import dwtx.draw2d.GraphicsSource; + +/** + * Update managers handle the job of laying out and repainting figures. A desirable + * implementation is to batches work to be done and collapses any redundant work. For + * example, clients may be making multiple changes to figures, which require laying out + * the same container or repainting the same region. + *

+ * The update manager receives requests to validate certain figures, and repaint certain + * areas of figures. An update manager could process every request synchronously, or it + * could batch these requests and process them asynchronously. + *

+ * The update process occurs in two phases. The first phase is laying out invalid figures. + * This phase comes first because it usually introduces additional damage regions. In some + * cases, while validating figures, new invalid figures may be appended to the update + * manager. Of course, damage regions will be reported too as figures are layed out. + *

+ * The second phase is to repaint all damaged areas. The update manager will typically + * batch, clip, and union, all rectangles and perform a single paint of the overall + * damaged area. + * + */ +public abstract class UpdateManager { + +private UpdateListener listeners[]; +private bool disposed; + +/** + * Adds the dirty region defined by the coordinates on the IFigure figure. The + * update manager should repaint the dirty region in a timely fashion. + * + * @param figure the dirty figure + * @param x the x coordinate of the dirty region + * @param y the y coordinate of the dirty region + * @param w the width of the dirty region + * @param h the height of the dirty region + */ +public abstract void addDirtyRegion(IFigure figure, int x, int y, int w, int h); + +/** + * @see #addDirtyRegion(IFigure, int, int, int, int) + */ +public void addDirtyRegion(IFigure figure, Rectangle rect) { + addDirtyRegion(figure, rect.x, rect.y, rect.width, rect.height); +} + +/** + * Causes an update to occur at some time, and the given runnable to be executed + * following the update. + * @since 3.1 + * @param run the runnable + */ +public void runWithUpdate(Runnable run) { } + +/** + * The receiver should call validate() on the IFigure figure in a timely fashion. + * + * @param figure the invalid figure + */ +public abstract void addInvalidFigure(IFigure figure); + +/** + * Adds the given listener to the list of listeners to be notified of painting and + * validation. + * @param listener the listener to add + */ +public void addUpdateListener(UpdateListener listener) { + if (listener is null) + throw new IllegalArgumentException(""); + if (listeners is null) { + listeners = new UpdateListener[1]; + listeners[0] = listener; + } else { + int oldSize = listeners.length; + UpdateListener newListeners[] = new UpdateListener[oldSize + 1]; + SimpleType!(UpdateListener).arraycopy(listeners, 0, newListeners, 0, oldSize); + newListeners[oldSize] = listener; + listeners = newListeners; + } +} + +/** + * Called when the EditPartViewer is being disposed. + */ +public void dispose() { + disposed = true; +} + +/** + * Notifies listeners that painting is about to occur, passing them the damaged rectangle + * and the map of dirty regions. + * @param damage the damaged rectangle + * @param dirtyRegions map of dirty regions to figures + */ +protected void firePainting(Rectangle damage, Map dirtyRegions) { + UpdateListener localListeners[] = listeners; + for (int i = 0; i < localListeners.length; i++) + localListeners[i].notifyPainting(damage, dirtyRegions); +} + +/** + * Notifies listeners that validation is about to occur. + */ +protected void fireValidating() { + UpdateListener localListeners[] = listeners; + for (int i = 0; i < localListeners.length; i++) + localListeners[i].notifyValidating(); +} + +/** + * @return whether this update manager has been disposed. + */ +protected bool isDisposed() { + return disposed; +} + +/** + * Forces an update to occur. Update managers will perform updates automatically, but may + * do so asynchronously. Calling this method forces a synchronous update. + */ +public abstract void performUpdate(); + +void paint(GC gc) { + performUpdate(new Rectangle(gc.getClipping())); +} + +/** + * Performs an update on the given exposed rectangle. + * @param exposed the exposed rectangle + */ +public abstract void performUpdate(Rectangle exposed); + +/** + * Removes one occurrence of the given UpdateListener by identity. + * @param listener the listener to remove + */ +public void removeUpdateListener(UpdateListener listener) { + if (listener is null) + throw new IllegalArgumentException(""); + for (int index = 0; index < listeners.length; index++) + if (listeners[index] is listener) { + int newSize = listeners.length - 1; + UpdateListener newListeners[] = null; + if (newSize !is 0) { + newListeners = new UpdateListener[newSize]; + SimpleType!(UpdateListener).arraycopy(listeners, 0, newListeners, 0, index); + SimpleType!(UpdateListener).arraycopy(listeners, index + 1, newListeners, index, newSize - index); + } else { + newListeners = new UpdateListener[0]; + } + listeners = newListeners; + return; + } +} + +/** + * Sets the GraphicsSource for this update manager. + * @param gs the new GraphicsSource + */ +public abstract void setGraphicsSource(GraphicsSource gs); + +/** + * Sets the root figure. + * @param figure the new root figure + */ +public abstract void setRoot(IFigure figure); + +/** + * Performs a partial update if supported (validation only). Fires notification to + * listeners that validation has been performed. By default this method calls {@link + * #performUpdate()}. Subclasses should override this method to support validation + * without repainting. + * + * @since 3.2 + */ +public void performValidation() { + performUpdate(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/Viewport.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Viewport.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,380 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.Viewport; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Bean; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.Translatable; +import dwtx.draw2d.Figure; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.RangeModel; +import dwtx.draw2d.ViewportLayout; +import dwtx.draw2d.DefaultRangeModel; + +/** + * A Viewport is a flexible window onto a {@link ScrollPane} and represents the visible + * portion of the ScrollPane. + */ +public class Viewport + : Figure + , PropertyChangeListener +{ + +public alias Figure.getClientArea getClientArea; + +/** ID for the view location property */ +public static final String PROPERTY_VIEW_LOCATION = "viewLocation"; //$NON-NLS-1$ +private IFigure view; + +private bool useTranslate = false; +private bool trackWidth = false; +private bool trackHeight = false; +private bool ignoreScroll = false; +private RangeModel + horiztonalRangeModel = null, + verticalRangeModel = null; + +private void instanceInit(){ + setLayoutManager(new ViewportLayout()); + setHorizontalRangeModel(new DefaultRangeModel()); + setVerticalRangeModel(new DefaultRangeModel()); +} + +/** + * Constructs a new Viewport with the default values. + */ +public this() { + instanceInit(); +} + +/** + * Constructs a new Viewport. If setting is true, the viewport will + * use graphics translation to paint. + * @param setting whether to use graphics translation + */ +public this(bool setting) { + instanceInit(); + useTranslate = setting; +} + +/** + * @see IFigure#getClientArea(Rectangle) + */ +public Rectangle getClientArea(Rectangle rect) { + super.getClientArea(rect); + if (useGraphicsTranslate()) + rect.translate(getViewLocation()); + return rect; +} + +/** + * Returns the view, which is the contents of the {@link ScrollPane} associated with this + * Viewport. + * + * @return the contents + * @since 2.0 + */ +public IFigure getContents() { + return view; +} + +/** + * Returns the RangeModel associated with the horizontal motion of this Viewport + * @return the RangeModel + * + * @since 2.0 + */ +public RangeModel getHorizontalRangeModel() { + return horiztonalRangeModel; +} + +/** + * Returns true if the Viewport resizes itself in the vertical direction when + * the available height of its view is decreased, false otherwise. This option is turned + * off by default, and can be activated by calling + * {@link #setContentsTracksHeight(bool)} and passing in true. + * + * @return whether the contents tracks height + * @since 2.0 + */ +public bool getContentsTracksHeight() { + return trackHeight; +} + +/** + * Returns true if the Viewport resizes itself in the horizontal direction + * when the available width of its view is decreased, false otherwise. This option is + * turned off by default, and can be activated by calling + * {@link #setContentsTracksWidth(bool)} and passing in true. + * + * @return whether the contents tracks width + * @since 2.0 + */ +public bool getContentsTracksWidth() { + return trackWidth; +} + +/** + * Returns the range model associated with the vertical motion of the Viewport. + * @return the RangeModel + * + * @since 2.0 + */ +public RangeModel getVerticalRangeModel() { + return verticalRangeModel; +} + +/** + * Returns the current location of this Viewport. + * @return the current location of this Viewport + * + * @since 2.0 + */ +public Point getViewLocation() { + return new Point( + getHorizontalRangeModel().getValue(), + getVerticalRangeModel().getValue()); +} + +private void localRevalidate() { + invalidate(); + if (getLayoutManager() !is null) + getLayoutManager().invalidate(); + getUpdateManager().addInvalidFigure(this); +} + +/** + * @see Figure#paintClientArea(Graphics) + */ +protected void paintClientArea(Graphics g) { + if (useGraphicsTranslate()) { + Point p = getViewLocation(); + try { + g.translate(-p.x, -p.y); + g.pushState(); + super.paintClientArea(g); + g.popState(); + } finally { + g.translate(p.x, p.y); + } + } else + super.paintClientArea(g); +} + +/** + * @see dwtx.draw2d.Figure#isCoordinateSystem() + */ +public bool isCoordinateSystem() { + return useGraphicsTranslate() || super.isCoordinateSystem(); +} + +/** + * Listens for either of the {@link RangeModel RangeModels} to fire a property change + * event and updates the view accordingly. + * @param event the event + */ +public void propertyChange(PropertyChangeEvent event) { + if (null !is cast(RangeModel)(event.getSource()) ) { + if (RangeModel.PROPERTY_VALUE.equals(event.getPropertyName())) { + if (!ignoreScroll) { + localRevalidate(); + if (useGraphicsTranslate()) { + repaint(); + fireMoved(); + } + } + firePropertyChange(PROPERTY_VIEW_LOCATION, event.getOldValue(), + event.getNewValue()); + } + } +} + +/** + * Sets extents of {@link RangeModel RangeModels} to the client area of this Viewport. + * Sets RangeModel minimums to zero. Sets RangeModel maximums to this Viewport's + * height/width. + * + * @since 2.0 + */ +protected void readjustScrollBars() { + if (getContents() is null) + return; + getVerticalRangeModel().setAll(0, getClientArea().height, + getContents().getBounds().height); + getHorizontalRangeModel().setAll(0, getClientArea().width, + getContents().getBounds().width); +} + +/** + * Sets this Viewport to be associated with the passed Figure. + * + * @param figure the new contents + * @since 2.0 + */ +public void setContents(IFigure figure) { + if (view is figure) + return; + if (view !is null) + remove(view); + view = figure; + if (view !is null) + add(figure); +} + +/** + * Toggles the Viewport's ability to resize itself automatically when its view is + * decreased in size in the vertical direction. This is disabled by default. + * + * @param track whether this viewport should track its height + * @since 2.0 + */ +public void setContentsTracksHeight(bool track) { + trackHeight = track; +} + +/** + * Toggles the Viewport's ability to resize itself automatically when its view is + * decreased in size in the horizontal direction. This is disabled by default. + * + * @param track whether this viewport should track its width + * @since 2.0 + */ +public void setContentsTracksWidth(bool track) { + trackWidth = track; +} + +/** + * Sets the horizontal location of the Viewport's view to the passed value. + * + * @param value the new horizontal location + * @since 2.0 + */ +public void setHorizontalLocation(int value) { + setViewLocation(value, getVerticalRangeModel().getValue()); +} + +/** + * Sets the horizontal range model to the passed RangeModel. + * + * @param rangeModel the new horizontal range model + * @since 2.0 + */ +public void setHorizontalRangeModel(RangeModel rangeModel) { + if (horiztonalRangeModel !is null) + horiztonalRangeModel.removePropertyChangeListener(this); + horiztonalRangeModel = rangeModel; + horiztonalRangeModel.addPropertyChangeListener(this); +} + +/** + * If value is true, this viewport will ignore any scrolling that + * occurs until this method is called again with false. + * + * @param value whether this viewport should ignore future scrolls + */ +public void setIgnoreScroll(bool value) { + ignoreScroll = value; +} + +/** + * Sets the vertical location of the Viewport's view to the passed value. + * + * @param value the new vertical location + * @since 2.0 + */ +public void setVerticalLocation(int value) { + setViewLocation(getHorizontalRangeModel().getValue(), value); +} + +/** + * Sets the vertical range model to the passed RangeModel. + * + * @param rangeModel the new vertical RangeModel + * @since 2.0 + */ +public void setVerticalRangeModel(RangeModel rangeModel) { + if (verticalRangeModel !is null) + verticalRangeModel.removePropertyChangeListener(this); + verticalRangeModel = rangeModel; + verticalRangeModel.addPropertyChangeListener(this); +} + +/** + * Sets the location of the Viewport's view to the passed values. + * + * @param x The new x coordinate of the Viewport's view. + * @param y The new y coordinate of the Viewport's view. + * @since 2.0 + */ +public void setViewLocation(int x, int y) { + if (getHorizontalRangeModel().getValue() !is x) + getHorizontalRangeModel().setValue(x); + if (getVerticalRangeModel().getValue() !is y) + getVerticalRangeModel().setValue(y); +} + +/** + * Sets the location of the Viewport's view to the passed Point. + * + * @param p The new location of the Viewport's view. + * @since 2.0 + */ +public void setViewLocation(Point p) { + setViewLocation(p.x, p.y); +} + +/** + * @see IFigure#translateFromParent(Translatable) + */ +public void translateFromParent(Translatable t) { + if (useTranslate) + t.performTranslate( + getHorizontalRangeModel().getValue(), + getVerticalRangeModel().getValue() + ); + super.translateFromParent(t); +} + +/** + * @see IFigure#translateToParent(Translatable) + */ +public void translateToParent(Translatable t) { + if (useTranslate) + t.performTranslate( + -getHorizontalRangeModel().getValue(), + -getVerticalRangeModel().getValue() + ); + super.translateToParent(t); +} + +/** + * Returns true if this viewport uses graphics translation. + * @return whether this viewport uses graphics translation + */ +public bool useGraphicsTranslate() { + return useTranslate; +} + +/** + * @see IFigure#validate() + */ +public void validate() { + super.validate(); + readjustScrollBars(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/ViewportLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ViewportLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.ViewportLayout; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.AbstractHintLayout; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Viewport; + +/** + * Layout for a viewport. A viewport is a flexible window + * onto a figure. + */ +public class ViewportLayout + : AbstractHintLayout +{ + +/** + * Returns the minimum size required by the input viewport figure. Since viewport is + * flexible, the minimum size required would be the just the size of the borders. + * @see AbstractHintLayout#calculateMinimumSize(IFigure, int, int) + */ +protected Dimension calculateMinimumSize(IFigure figure, int wHint, int hHint) { + Viewport viewport = cast(Viewport)figure; + Dimension min = new Dimension(); + Insets insets = viewport.getInsets(); + return min.getExpanded(insets.getWidth(), insets.getHeight()); +} + +/** + * Calculates and returns the preferred size of the figure based on the given hints. The + * given wHint is ignored unless the viewport (parent) is tracking width. The same is true + * for the height hint. + * @param parent the Viewport whose preferred size is to be calculated + * @param wHint the width hint + * @param hHint the height hint + * @return the Preferred size of the given viewport + * @since 2.0 + */ +protected Dimension calculatePreferredSize(IFigure parent, int wHint, int hHint) { + Viewport viewport = cast(Viewport)parent; + Insets insets = viewport.getInsets(); + IFigure contents = viewport.getContents(); + + if (viewport.getContentsTracksWidth() && wHint > -1) + wHint = Math.max(0, wHint - insets.getWidth()); + else + wHint = -1; + if (viewport.getContentsTracksHeight() && hHint > -1) + hHint = Math.max(0, hHint - insets.getHeight()); + else + hHint = -1; + + if (contents is null) { + return new Dimension(insets.getWidth(), insets.getHeight()); + } else { + Dimension minSize = contents.getMinimumSize(wHint, hHint); + if (wHint > -1) + wHint = Math.max(wHint, minSize.width); + if (hHint > -1) + hHint = Math.max(hHint, minSize.height); + return contents + .getPreferredSize(wHint, hHint) + .getExpanded(insets.getWidth(), insets.getHeight()); + } + + //Layout currently does not union border's preferred size. +} + +/** + * @see dwtx.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure) + */ +protected bool isSensitiveHorizontally(IFigure parent) { + return (cast(Viewport)parent).getContentsTracksWidth(); +} + +/** + * @see dwtx.draw2d.AbstractHintLayout#isSensitiveHorizontally(IFigure) + */ +protected bool isSensitiveVertically(IFigure parent) { + return (cast(Viewport)parent).getContentsTracksHeight(); +} + +/** + * @see dwtx.draw2d.LayoutManager#layout(IFigure) + */ +public void layout(IFigure figure) { + Viewport viewport = cast(Viewport)figure; + IFigure contents = viewport.getContents(); + + if (contents is null) return; + Point p = viewport.getClientArea().getLocation(); + + p.translate(viewport.getViewLocation().getNegated()); + + // Calculate the hints + Rectangle hints = viewport.getClientArea(); + int wHint = viewport.getContentsTracksWidth() ? hints.width : -1; + int hHint = viewport.getContentsTracksHeight() ? hints.height : -1; + + Dimension newSize = viewport.getClientArea().getSize(); + Dimension min = contents.getMinimumSize(wHint, hHint); + Dimension pref = contents.getPreferredSize(wHint, hHint); + + if (viewport.getContentsTracksHeight()) + newSize.height = Math.max(newSize.height, min.height); + else + newSize.height = Math.max(newSize.height, pref.height); + + if (viewport.getContentsTracksWidth()) + newSize.width = Math.max(newSize.width, min.width); + else + newSize.width = Math.max(newSize.width, pref.width); + + contents.setBounds(new Rectangle(p, newSize)); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/XYAnchor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/XYAnchor.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.XYAnchor; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.ConnectionAnchorBase; +import dwtx.draw2d.IFigure; + +/** + * Supports an anchor in the XY layout. This anchor exists independently without an owner. + */ +public class XYAnchor + : ConnectionAnchorBase +{ + +private Point location; + +/** + * Constructs an XYAnchor at the Point p. + * + * @param p the point where this anchor will be located + * @since 2.0 + */ +public this(Point p) { + location = new Point(p); +} + +/** + * Returns the location of this anchor relative to the reference point given in as input. + * Since this is XY layout, the location of the point is independent of the reference + * point. + * + * @see ConnectionAnchor#getLocation(Point) + */ +public Point getLocation(Point reference) { + return location; +} + +/** + * Returns null as these anchors inherently do not depend on other figures + * for their location. + * + * @see ConnectionAnchor#getOwner() + * @since 2.0 + */ +public IFigure getOwner() { + return null; +} + +/** + * Returns the point which is used as the reference by this connection anchor. In the case + * of the XYAnchor, this point is the same as its location. + * + * @see ConnectionAnchor#getReferencePoint() + */ +public Point getReferencePoint() { + return location; +} + +/** + * Sets the location of this anchor and notifies all the listeners of the update. + * + * @param p the new location of this anchor + * @see #getLocation(Point) + * @since 2.0 + */ +public void setLocation(Point p) { + location.setLocation(p); + fireAnchorMoved(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/XYLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/XYLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.XYLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; + +import dwtx.draw2d.AbstractLayout; +import dwtx.draw2d.IFigure; + +/** + * This class implements the {@link dwtx.draw2d.LayoutManager} interface using the + * XY Layout algorithm. This lays out the components using the layout constraints as + * defined by each component. + */ +public class XYLayout + : AbstractLayout +{ + +/** The layout contraints */ +protected Map constraints; + +this(){ + constraints = new HashMap(); +} + +/** + * Calculates and returns the preferred size of the input figure. Since in XYLayout the + * location of the child should be preserved, the preferred size would be a region which + * would hold all the children of the input figure. If no constraint is set, that child + * is ignored for calculation. If width and height are not positive, the preferred + * dimensions of the child are taken. + * + * @see AbstractLayout#calculatePreferredSize(IFigure, int, int) + * @since 2.0 + */ +protected Dimension calculatePreferredSize(IFigure f, int wHint, int hHint) { + Rectangle rect = new Rectangle(); + ListIterator children = f.getChildren().listIterator(); + while (children.hasNext()) { + IFigure child = cast(IFigure)children.next(); + Rectangle r = cast(Rectangle)constraints.get(cast(Object)child); + if (r is null) + continue; + + if (r.width is -1 || r.height is -1) { + Dimension preferredSize = child.getPreferredSize(r.width, r.height); + r = r.getCopy(); + if (r.width is -1) + r.width = preferredSize.width; + if (r.height is -1) + r.height = preferredSize.height; + } + rect.union_(r); + } + Dimension d = rect.getSize(); + Insets insets = f.getInsets(); + return (new Dimension(d.width + insets.getWidth(), d.height + insets.getHeight())). + union_(getBorderPreferredSize(f)); +} + +/** + * @see LayoutManager#getConstraint(IFigure) + */ +public Object getConstraint(IFigure figure) { + return constraints.get(cast(Object)figure); +} + +/** + * Returns the origin for the given figure. + * @param parent the figure whose origin is requested + * @return the origin + */ +public Point getOrigin(IFigure parent) { + return parent.getClientArea().getLocation(); +} + +/** + * Implements the algorithm to layout the components of the given container figure. + * Each component is laid out using its own layout constraint specifying its size + * and position. + * + * @see LayoutManager#layout(IFigure) + */ +public void layout(IFigure parent) { + Iterator children = parent.getChildren().iterator(); + Point offset = getOrigin(parent); + IFigure f; + while (children.hasNext()) { + f = cast(IFigure)children.next(); + Rectangle bounds = cast(Rectangle)getConstraint(f); + if (bounds is null) continue; + + if (bounds.width is -1 || bounds.height is -1) { + Dimension preferredSize = f.getPreferredSize(bounds.width, bounds.height); + bounds = bounds.getCopy(); + if (bounds.width is -1) + bounds.width = preferredSize.width; + if (bounds.height is -1) + bounds.height = preferredSize.height; + } + bounds = bounds.getTranslated(offset); + f.setBounds(bounds); + } +} + +/** + * @see LayoutManager#remove(IFigure) + */ +public void remove(IFigure figure) { + super.remove(figure); + constraints.remove(cast(Object)figure); +} + +/** + * Sets the layout constraint of the given figure. The constraints can only be of type + * {@link Rectangle}. + * + * @see LayoutManager#setConstraint(IFigure, Object) + * @since 2.0 + */ +public void setConstraint(IFigure figure, Object newConstraint) { + super.setConstraint(figure, newConstraint); + if (newConstraint !is null) + constraints.put(cast(Object)figure, newConstraint); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/Dimension.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/Dimension.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,471 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.Dimension; + +import dwtx.draw2d.geometry.Point; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Translatable; +static import dwt.graphics.Point; +static import dwt.graphics.Image; +static import dwt.graphics.Rectangle; +import tango.text.convert.Format; + +/** + * Stores an integer width and height. This class provides various methods for + * manipulating this Dimension or creating new derived Objects. + */ +public class Dimension + : Cloneable/+, java.io.Serializable+/, Translatable +{ + +/**A singleton for use in short calculations. Use to avoid newing unnecessary objects.*/ +public static const Dimension SINGLETON; + +static this(){ + SINGLETON = new Dimension(); +} + +/**The width.*/ +public int width; +/**The height. */ +public int height; + +static final long serialVersionUID = 1; + +/** + * Constructs a Dimension of zero width and height. + * + * @since 2.0 + */ +public this() { } + +/** + * Constructs a Dimension with the width and height of the passed Dimension. + * + * @param d the Dimension supplying the initial values + * @since 2.0 + */ +public this(Dimension d) { + width = d.width; + height = d.height; +} + +/** + * Constructs a Dimension where the width and height are the x and y distances of the + * input point from the origin. + * + * @param pt the Point supplying the initial values + * @since 2.0 + */ +public this(dwt.graphics.Point.Point pt) { + width = pt.x; + height = pt.y; +} + +/** + * Constructs a Dimension with the supplied width and height values. + * + * @param w the width + * @param h the height + * @since 2.0 + */ +public this(int w, int h) { + width = w; + height = h; +} + +/** + * Constructs a Dimension with the width and height of the Image supplied as input. + * + * @param image the image supplying the dimensions + * @since 2.0 + */ +public this(dwt.graphics.Image.Image image) { + dwt.graphics.Rectangle.Rectangle r = image.getBounds(); + width = r.width; + height = r.height; +} + +/** + * Returns true if the input Dimension fits into this Dimension. A Dimension + * of the same size is considered to "fit". + * + * @param d the dimension being tested + * @return true if this Dimension contains d + * @since 2.0 + */ +public bool contains(Dimension d) { + return width >= d.width && height >= d.height; +} + +/** + * Returns true if this Dimension properly contains the one specified. + * Proper containment is defined as containment using "<", instead of "<=". + * + * @param d the dimension being tested + * @return true if this Dimension properly contains the one specified + * @since 2.0 + */ +public bool containsProper(Dimension d) { + return width > d.width && height > d.height; +} + +/** + * Copies the width and height values of the input Dimension to this Dimension. + * + * @param d the dimension supplying the values + * @since 2.0 + */ +public void setSize(Dimension d) { + width = d.width; + height = d.height; +} + +/** + * Returns the area of this Dimension. + * + * @return the area + * @since 2.0 + */ +public int getArea() { + return width * height; +} + +/** + * Creates and returns a copy of this Dimension. + * @return a copy of this Dimension + * @since 2.0 + */ +public Dimension getCopy() { + return new Dimension(this); +} + +/** + * Creates and returns a new Dimension representing the difference between this Dimension + * and the one specified. + * + * @param d the dimension being compared + * @return a new dimension representing the difference + * @since 2.0 + */ +public Dimension getDifference(Dimension d) { + return new Dimension(width - d.width, height - d.height); +} + +/** + * Creates and returns a Dimension representing the sum of this Dimension and the one + * specified. + * + * @param d the dimension providing the expansion width and height + * @return a new dimension expanded by d + * @since 2.0 + */ +public Dimension getExpanded(Dimension d) { + return new Dimension(width + d.width, height + d.height); +} + +/** + * Creates and returns a new Dimension representing the sum of this Dimension and the one + * specified. + * + * @param w value by which the width of this is to be expanded + * @param h value by which the height of this is to be expanded + * @return a new Dimension expanded by the given values + * @since 2.0 + */ +public Dimension getExpanded(int w, int h) { + return new Dimension(width + w, height + h); +} + +/** + * Creates and returns a new Dimension representing the intersection of this Dimension and + * the one specified. + * + * @param d the Dimension to intersect with + * @return A new Dimension representing the intersection + * @since 2.0 + */ +public Dimension getIntersected(Dimension d) { + return (new Dimension(this)).intersect(d); +} + +/** + * Creates and returns a new Dimension with negated values. + * + * @return a new Dimension with negated values + * @since 2.0 + */ +public Dimension getNegated() { + return new Dimension(0 - width, 0 - height); +} + +/** + * Returns whether the input Object is equivalent to this Dimension. true if + * the Object is a Dimension and its width and height are equal to this Dimension's width + * and height, false otherwise. + * + * @param o the Object being tested for equality + * @return true if the given object is equal to this dimension + * @since 2.0 + */ +public override int opEquals(Object o) { + if (auto d = cast(Dimension)o ) { + return (d.width is width && d.height is height); + } + return false; +} + +/** + * Returns true if this Dimension's width and height are equal to the given + * width and height. + * + * @param w the width + * @param h the height + * @return true if this dimension's width and height are equal to those given. + * @since 2.0 + */ +public bool equals(int w, int h) { + return width is w && height is h; +} + +/** + * Expands the size of this Dimension by the specified amount. + * + * @param d the Dimension providing the expansion width and height + * @return this for convenience + * @since 2.0 + */ +public Dimension expand(Dimension d) { + width += d.width; + height += d.height; + return this; +} + +/** + * Expands the size of this Dimension by the specified amound. + * + * @param pt the Point supplying the dimensional values + * @return this for convenience + * @since 2.0 + */ +public Dimension expand(Point pt) { + width += pt.x; + height += pt.y; + return this; +} + +/** + * Expands the size of this Dimension by the specified width and height. + * + * @param w Value by which the width should be increased + * @param h Value by which the height should be increased + * @return this for convenience + * @since 2.0 + */ +public Dimension expand(int w, int h) { + width += w; + height += h; + return this; +} + +/** + * Creates a new Dimension with its width and height scaled by the specified value. + * + * @param amount Value by which the width and height are scaled + * @return a new dimension with the scale applied + * @since 2.0 + */ +public Dimension getScaled(double amount) { + return (new Dimension(this)) + .scale(amount); +} + +/** + * Creates a new Dimension with its height and width swapped. Useful in orientation change + * calculations. + * + * @return a new Dimension with its height and width swapped + * @since 2.0 + */ +public Dimension getTransposed() { + return (new Dimension(this)) + .transpose(); +} + +/** + * Creates a new Dimension representing the union of this Dimension with the one + * specified. Union is defined as the max() of the values from each Dimension. + * + * @param d the Dimension to be unioned + * @return a new Dimension + * @since 2.0 + */ +public Dimension getUnioned(Dimension d) { + return (new Dimension(this)).union_(d); +} + +/** + * @see java.lang.Object#toHash() + */ +public override hash_t toHash() { + return (width * height) ^ (width + height); +} + + +/** + * This Dimension is intersected with the one specified. Intersection is performed by + * taking the min() of the values from each dimension. + * + * @param d the Dimension used to perform the min() + * @return this for convenience + * @since 2.0 + */ +public Dimension intersect(Dimension d) { + width = Math.min(d.width, width); + height = Math.min(d.height, height); + return this; +} + +/** + * Returns true if the Dimension has width or height greater than 0. + * + * @return true if this Dimension is empty + * @since 2.0 + */ +public bool isEmpty() { + return (width <= 0) || (height <= 0); +} + +/** + * Negates the width and height of this Dimension. + * + * @return this for convenience + * @since 2.0 + */ +public Dimension negate() { + width = 0 - width; + height = 0 - height; + return this; +} + +/** + * @see dwtx.draw2d.geometry.Translatable#performScale(double) + */ +public void performScale(double factor) { + scale(factor); +} + +/** + * @see dwtx.draw2d.geometry.Translatable#performTranslate(int, int) + */ +public void performTranslate(int dx, int dy) { } + +/** + * Scales the width and height of this Dimension by the amount supplied, and returns this + * for convenience. + * + * @param amount value by which this Dimension's width and height are to be scaled + * @return this for convenience + * @since 2.0 + */ +public Dimension scale(double amount) { + return scale(amount, amount); +} + +/** + * Scales the width of this Dimension by w and scales the height of this Dimension + * by h. Returns this for convenience. + * + * @param w the value by which the width is to be scaled + * @param h the value by which the height is to be scaled + * @return this for convenience + * @since 2.0 + */ +public Dimension scale(double w, double h) { + width = cast(int)(Math.floor(width * w)); + height = cast(int)(Math.floor(height * h)); + return this; +} + +/** + * Reduces the width of this Dimension by w, and reduces the height of this + * Dimension by h. Returns this for convenience. + * + * @param w the value by which the width is to be reduced + * @param h the value by which the height is to be reduced + * @return this for convenience + * @since 2.0 + */ +public Dimension shrink(int w, int h) { + return expand(-w, -h); +} + +/** + * @see Object#toString() + */ + +public override String toString() { + return Format("Dimension({}, {})", width, height); +} + +/** + * Swaps the width and height of this Dimension, and returns this for convenience. Can be + * useful in orientation changes. + * + * @return this for convenience + * @since 2.0 + */ +public Dimension transpose() { + int temp = width; + width = height; + height = temp; + return this; +} + +/** + * Sets the width of this Dimension to the greater of this Dimension's width and + * d.width. Likewise for this Dimension's height. + * + * @param d the Dimension to union with this Dimension + * @return this for convenience + * @since 2.0 + */ +public Dimension union_ (Dimension d) { + width = Math.max(width, d.width); + height = Math.max(height, d.height); + return this; +} + +/** + * Returns double width + * + * @return double width + * @since 3.4 + */ +public double preciseWidth() { + return width; +} + +/** + * Returns double height + * + * @return double height + * @since 3.4 + */ +public double preciseHeight() { + return height; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/Geometry.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/Geometry.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.Geometry; + +import dwt.dwthelper.utils; + +/** + * A Utilities class for geometry operations. + * @author Pratik Shah + * @since 3.1 + */ +public class Geometry +{ + +/** + * Determines whether the two line segments formed by the given coordinates intersect. If + * one of the two line segments starts or ends on the other line, then they are considered + * to be intersecting. + * + * @param ux x coordinate of starting point of line 1 + * @param uy y coordinate of starting point of line 1 + * @param vx x coordinate of ending point of line 1 + * @param vy y coordinate of endpoing point of line 1 + * @param sx x coordinate of the starting point of line 2 + * @param sy y coordinate of the starting point of line 2 + * @param tx x coordinate of the ending point of line 2 + * @param ty y coordinate of the ending point of line 2 + * @return true if the two line segments formed by the given coordinates + * cross + * @since 3.1 + */ +public static bool linesIntersect(int ux, int uy, int vx, int vy, + int sx, int sy, int tx, int ty) { + /* + * Given the segments: u-------v. s-------t. If s->t is inside the triangle u-v-s, + * then check whether the line u->u splits the line s->t. + */ + /* Values are casted to long to avoid integer overflows */ + long usX = cast(long) ux - sx; + long usY = cast(long) uy - sy; + long vsX = cast(long) vx - sx; + long vsY = cast(long) vy - sy; + long stX = cast(long) sx - tx; + long stY = cast(long) sy - ty; + if (productSign(cross(vsX, vsY, stX, stY), cross(stX, stY, usX, usY)) >= 0) { + long vuX = cast(long) vx - ux; + long vuY = cast(long) vy - uy; + long utX = cast(long) ux - tx; + long utY = cast(long) uy - ty; + return productSign(cross(-usX, -usY, vuX, vuY), cross(vuX, vuY, utX, utY)) <= 0; + } + return false; +} + +private static int productSign(long x, long y) { + if (x is 0 || y is 0) { + return 0; + } else if (x < 0 ^ y < 0) { + return -1; + } + return 1; +} + +private static long cross(long x1, long y1, long x2, long y2) { + return x1 * y2 - x2 * y1; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/Insets.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/Insets.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,190 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.Insets; + +import dwt.dwthelper.utils; +import tango.text.convert.Format; + +/** + * Stores four integers for top, left, bottom, and right measurements. + */ +public class Insets + : Cloneable/+, java.io.Serializable+/ +{ + +/** distance from left */ +public int left; +/** distance from top*/ +public int top; +/** distance from bottom*/ +public int bottom; +/** distance from right */ +public int right; + +static final long serialVersionUID = 1; + +/** + * Constructs an Insets with all zeroes. + * @since 2.0 + */ +public this() { } + +/** + * Constructs a new Insets with initial values the same as the provided Insets. + * @param i The insets to copy. + * @since 2.0 + */ +public this(Insets i) { + this(i.top, i.left, i.bottom, i.right); +} + +/** + * Constructs a new Insets with all the sides set to the speicifed value. + * @param i Value applied to all sides of new Insets. + * @since 2.0 + */ +public this(int i) { + this(i, i, i, i); +} + +/** + * Creates a new Insets with the specified top, left, bottom, and right values. + * @param top Value of the top space. + * @param left Value of the left space. + * @param bottom Value of the bottom space. + * @param right Value of the right space. + * @since 2.0 + */ +public this(int top, int left, int bottom, int right) { + this.top = top; + this.left = left; + this.bottom = bottom; + this.right = right; +} + +/** + * Adds the values of the specified Insets to this Insets' values. + * @return this for convenience + * @param insets the Insets being added + * @since 2.0 + */ +public Insets add(Insets insets) { + top += insets.top; + bottom += insets.bottom; + left += insets.left; + right += insets.right; + return this; +} + +/** + * Test for equality. + * The Insets are equal if their top, left, bottom, and + * right values are equivalent. + * + * @param o Object being tested for equality. + * @return true if all values are the same. + * @since 2.0 + */ +public override int opEquals(Object o) { + if (auto i = cast(Insets)o ) { + return i.top is top + && i.bottom is bottom + && i.left is left + && i.right is right; + } + return false; +} + +/** + * Creates an Insets representing the sum of this Insets with the specified Insets. + * @param insets Insets to be added + * @return A new Insets + * @since 2.0 + */ +public Insets getAdded(Insets insets) { + return (new Insets(this)).add(insets); +} + +/** + * Returns the height for this Insets, equal to top + bottom. + * @return The sum of top + bottom + * @see #getWidth() + * @since 2.0 + */ +public int getHeight() { + return top + bottom; +} + +/** + * Creates a new Insets with transposed values. + * Top and Left are transposed. + * Bottom and Right are transposed. + * @return New Insets with the transposed values. + * @since 2.0 + */ +public Insets getTransposed() { + return (new Insets(this)).transpose(); +} + +/** + * Returns the width for this Insets, equal to left + right. + * @return The sum of left + right + * @see #getHeight() + * @since 2.0 + */ +public int getWidth() { + return left + right; +} + +/** + * @see java.lang.Object#toHash() + */ +public override hash_t toHash() { + return top * 7 + left * 2 + bottom * 31 + right * 37; +} + + +/** + * Returns true if all values are 0. + * @return true if all values are 0 + * @since 2.0 + */ +public bool isEmpty() { + return (left is 0 && right is 0 && top is 0 && bottom is 0); +} + +/** + * @return String representation. + * @since 2.0 + */ +public override String toString() { + return Format( "Insets(t={}, l={}, b={}, r={})", top, left, bottom, right ); +} + +/** + * Transposes this object. Top and Left are exchanged. Bottom and Right are exchanged. + * Can be used in orientation changes. + * @return this for convenience + * @since 2.0 + */ +public Insets transpose() { + int temp = top; + top = left; + left = temp; + temp = right; + right = bottom; + bottom = temp; + return this; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/Point.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/Point.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,438 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.Point; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.PositionConstants; +import dwtx.draw2d.geometry.Translatable; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Rectangle; + +static import dwt.graphics.Point; + +import tango.text.convert.Format; + +/** + * Represents a point (x, y) in 2-dimensional space. This class provides various methods + * for manipulating this Point or creating new derived geometrical Objects. + */ +public class Point + : Cloneable, /+java.io.Serializable,+/ Translatable +{ + +static final long serialVersionUID = 1; +/**A singleton for use in short calculations*/ +public static const Point SINGLETON; + +static this(){ + SINGLETON = new Point(); +} + +/**x value*/ +public int x; +/**y value*/ +public int y; + +/** + * Constructs a Point at location (0,0). + * @since 2.0 + */ +public this() { } + +/** + * Constructs a Point at the same location as the given Point. + * @param copy Point from which the initial values are taken. + * @since 2.0 + */ +public this(Point copy) { + x = copy.x; + y = copy.y; +} + +/** + * Constructs a Point at the same location as the given DWT Point. + * @param copy Point from which the initial values are taken. + * @since 2.0 + */ +public this(dwt.graphics.Point.Point copy) { + x = copy.x; + y = copy.y; +} + + +/** + * Constructs a Point at the specified x and y locations. + * + * @param x x value + * @param y y value + * @since 2.0 + */ +public this(int x, int y) { + this.x = x; + this.y = y; +} + +/** + * Constructs a Point at the specified x and y locations. + * @param x x value + * @param y y value + * @since 2.0 + */ +public this(double x, double y) { + this.x = cast(int)x; + this.y = cast(int)y; +} + +/** + * Test for equality. + * @param o Object being tested for equality + * @return true if both x and y values are equal + * @since 2.0 + */ +public override int opEquals(Object o) { + if ( auto p = cast(Point)o ) { + return p.x is x && p.y is y; + } + return false; +} + +/** + * @return a copy of this Point + * @since 2.0 + */ +public Point getCopy() { + return new Point(x, y); +} + +/** + * Calculates the difference in between this Point and the one specified. + * @param pt The Point being subtracted from this Point + * @return A new Dimension representing the difference + * @since 2.0 + */ +public Dimension getDifference(Point pt) { + return new Dimension(this.x - pt.x, this.y - pt.y); +} + +/** + * Calculates the distance from this Point to the one specified. + * @param pt The Point being compared to this + * @return The distance + * @since 2.0 + */ +public double getDistance(Point pt) { + return Math.sqrt(getPreciseDistance2(pt)); +} + +/** + * Calculates the distance squared between this Point and the one specified. If + * the distance squared is larger than the maximum integer value, then + * Integer.MAX_VALUE will be returned. + * @param pt The reference Point + * @return distance2 + * @since 2.0 + */ +public int getDistance2(Point pt) { + long i = pt.x - x; + long j = pt.y - y; + long result = i * i + j * j; + if (result > Integer.MAX_VALUE) + return Integer.MAX_VALUE; + return cast(int)result; +} + +private double getPreciseDistance2(Point pt) { + double i = pt.preciseX() - preciseX(); + double j = pt.preciseY() - preciseY(); + return i * i + j * j; +} + +/** + * Calculates the orthogonal distance to the specified point. The orthogonal distance is + * the sum of the horizontal and vertical differences. + * @param pt The reference Point + * @return the orthoganal distance + */ +public int getDistanceOrthogonal(Point pt) { + return Math.abs(y - pt.y) + Math.abs(x - pt.x); +} + +/** + * Creates a Point with negated x and y values. + * @return A new Point + * @since 2.0 + */ +public Point getNegated() { + return getCopy().negate(); +} + +/** + * Calculates the relative position of the specified Point to this Point. + * @param p The reference Point + * @return NORTH, SOUTH, EAST, or WEST, as defined in {@link PositionConstants} + */ +public int getPosition(Point p) { + int dx = p.x - x; + int dy = p.y - y; + if (Math.abs(dx) > Math.abs(dy)) { + if (dx < 0) + return PositionConstants.WEST; + return PositionConstants.EAST; + } + if (dy < 0) + return PositionConstants.NORTH; + return PositionConstants.SOUTH; +} + +/** + * Creates a new Point from this Point by scaling by the specified amount. + * @param amount scale factor + * @return A new Point + * @since 2.0 + */ +public Point getScaled(double amount) { + return getCopy().scale(amount); +} + +/** + * Creates a new DWT {@link dwt.graphics.Point Point} from this Point. + * @return A new DWT Point + * @since 2.0 + */ +public dwt.graphics.Point.Point getSWTPoint() { + return new dwt.graphics.Point.Point(x, y); +} + +/** + * Creates a new Point which is translated by the values of the input Dimension. + * @param delta Dimension which provides the translation amounts. + * @return A new Point + * @since 2.0 + */ +public Point getTranslated(Dimension delta) { + return getCopy().translate(delta); +} + +/** + * Creates a new Point which is translated by the specified x and y values + * @param x horizontal component + * @param y vertical component + * @return A new Point + * @since 2.0 + */ +public Point getTranslated(int x, int y) { + return getCopy().translate(x, y); +} + +/** + * Creates a new Point which is translated by the values of the provided Point. + * @param pt Point which provides the translation amounts. + * @return A new Point + * @since 2.0 + */ +public Point getTranslated(Point pt) { + return getCopy().translate(pt); +} + +/** + * Creates a new Point with the transposed values of this Point. + * Can be useful in orientation change calculations. + * @return A new Point + * @since 2.0 + */ +public Point getTransposed() { + return getCopy().transpose(); +} + +/** + * @see java.lang.Object#toHash() + */ +public override hash_t toHash() { + return (x * y) ^ (x + y); +} + +/** + * Creates a new Point representing the MAX of two provided Points. + * @param p1 first point + * @param p2 second point + * @return A new Point representing the Max() + */ +public static Point max(Point p1, Point p2) { + return (new Rectangle(p1, p2)) + .getBottomRight() + .translate(-1, -1); +} + +/** + * Creates a new Point representing the MIN of two provided Points. + * @param p1 first point + * @param p2 second point + * @return A new Point representing the Min() + */ +public static Point min(Point p1, Point p2) { + return (new Rectangle(p1, p2)).getTopLeft(); +} + +/** + * Negates the x and y values of this Point. + * @return this for convenience + * @since 2.0 + */ +public Point negate() { + x = -x; + y = -y; + return this; +} + +/** @see Translatable#performScale(double) */ +public void performScale(double factor) { + scale(factor); +} + +/** @see Translatable#performTranslate(int, int) */ +public void performTranslate(int dx, int dy) { + translate(dx, dy); +} + +/** + * Scales this Point by the specified amount. + * @return this for convenience + * @param amount scale factor + * @since 2.0 + */ +public Point scale(double amount) { + x = cast(int)Math.floor(x * amount); + y = cast(int)Math.floor(y * amount); + return this; +} + +/** + * Scales this Point by the specified values. + * @param xAmount horizontal scale factor + * @param yAmount vertical scale factor + * @return this for convenience + * @since 2.0 + */ +public Point scale(double xAmount, double yAmount) { + x = cast(int)Math.floor(x * xAmount); + y = cast(int)Math.floor(y * yAmount); + return this; +} + +/** + * Sets the location of this Point to the provided x and y locations. + * @return this for convenience + * @param x the x location + * @param y the y location + * @since 2.0 + */ +public Point setLocation(int x, int y) { + this.x = x; + this.y = y; + return this; +} + +/** + * Sets the location of this Point to the specified Point. + * @return this for convenience + * @param pt the Location + * @since 2.0 + */ +public Point setLocation(Point pt) { + x = pt.x; + y = pt.y; + return this; +} + +/** + * @return String representation. + * @since 2.0 + */ +public override String toString() { + return Format("Point({}, {})", x, y );//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ +} + +/** + * Shifts the location of this Point by the location of the + * input Point along each of the axes, and returns this for + * convenience. + * + * @param p Point to which the origin is being shifted. + * @return this for convenience + * @since 2.0 + */ +public Point translate(Point p) { + return translate(p.x, p.y); +} + +/** + * Shifts this Point by the values of the Dimension along + * each axis, and returns this for convenience. + * + * @param d Dimension by which the origin is being shifted. + * @return this for convenience + * @since 2.0 + */ +public Point translate(Dimension d) { + return translate(d.width, d.height); +} + +/** + * Shifts this Point by the values supplied along each axes, and + * returns this for convenience. + * + * @param dx Amount by which point is shifted along X axis. + * @param dy Amount by which point is shifted along Y axis. + * @return this for convenience + * @since 2.0 + */ +public Point translate(int dx, int dy) { + x += dx; + y += dy; + return this; +} + +/** + * Transposes this object. X and Y values are exchanged. + * @return this for convenience + * @since 2.0 + */ +public Point transpose() { + int temp = x; + x = y; + y = temp; + return this; +} + +/** + * Returns double x coordinate + * + * @return double x coordinate + * @since 3.4 + */ +public double preciseX() { + return x; +} + +/** + * Returns double y coordinate + * + * @return double y coordinate + * @since 3.4 + */ +public double preciseY() { + return y; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/PointList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/PointList.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,449 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.PointList; + +import dwt.dwthelper.utils; +import tango.text.convert.Format; + +import dwtx.draw2d.geometry.Translatable; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Geometry; + +/** + * Represents a List of Points. This class is used for building an int[]. + * The array is internal, and is constructed and queried by the client using + * {@link Point Points}. DWT uses integer arrays when painting polylines and polygons. + */ +public class PointList + : /+java.io.Serializable,+/ Translatable +{ + +private int[] points; +private Rectangle bounds; +private int size_ = 0; + +static const long serialVersionUID = 1; + +/** + * Constructs an empty PointList. + * + * @since 2.0 + */ +public this() { } + +/** + * Constructs a PointList with the given points. + * @param points int array where two consecutive ints form the coordinates of a point + * @since 3.1 + */ +public this(int points[]) { + this.points = points; + this.size_ = points.length / 2; +} + +/** + * Constructs a PointList with initial capacity size, but no points. + * + * @param size Number of points to hold. + * @since 2.0 + */ +public this(int size_) { + points = new int[size_ * 2]; +} + +/** + * Appends all of the given points to this PointList. + * @param source the source pointlist + */ +public void addAll(PointList source) { + ensureCapacity(size_ + source.size_); + System.arraycopy(source.points, 0, points, size_ * 2, source.size_ * 2); + size_ += source.size_; +} + +/** + * Adds Point p to this PointList. + * @param p the point to be added + * @see #removePoint(int) + * @since 2.0 + */ +public void addPoint(Point p) { + addPoint(p.x, p.y); +} + +/** + * Adds the input point values to this PointList. + * @param x X value of a point to add + * @param y Y value of a point to add + * @since 2.0 + */ +public void addPoint(int x, int y) { + bounds = null; + int index = size_ * 2; + ensureCapacity(size_ + 1); + points[index] = x; + points[index + 1] = y; + size_++; +} + +private void ensureCapacity(int newSize) { + newSize *= 2; + if (points.length < newSize) { + int old[] = points; + points = new int[Math.max(newSize, size_ * 4)]; + System.arraycopy(old, 0, points, 0, size_ * 2); + } +} + +/** + * Returns the smallest Rectangle which contains all Points. + * @return The smallest Rectangle which contains all Points. + * @since 2.0 + */ +public Rectangle getBounds() { + if (bounds !is null) + return bounds; + bounds = new Rectangle(); + if (size_ > 0) { + bounds.setLocation(getPoint(0)); + for (int i = 0; i < size_; i++) + bounds.union_(getPoint(i)); + } + return bounds; +} + +/** + * Creates a copy + * @return PointList A copy of this PointList + */ +public PointList getCopy() { + PointList result = new PointList(size_); + System.arraycopy(points, 0, result.points, 0, size_ * 2); + result.size_ = size_; + result.bounds = null; + return result; +} + +/** + * Returns the first Point in the list. + * @return The first point in the list. + * @throws IndexOutOfBoundsException if the list is empty + * @since 2.0 + */ +public Point getFirstPoint() { + return getPoint(0); +} + +/** + * Returns the last point in the list. + * @throws IndexOutOfBoundsException if the list is empty + * @return The last Point in the list + * @since 2.0 + */ +public Point getLastPoint() { + return getPoint(size_ - 1); +} + +/** + * Returns the midpoint of the list of Points. The midpoint is the median of the List, + * unless there are 2 medians (size is even), then the middle of the medians is returned. + * @return The midpoint + * @throws IndexOutOfBoundsException if the list is empty + */ +public Point getMidpoint() { + if (size() % 2 is 0) + return getPoint(size() / 2 - 1). + getTranslated(getPoint(size() / 2)). + scale(0.5f); + return getPoint(size() / 2); +} + +/** + * Returns the Point in the list at the specified index. + * @param index Index of the desired Point + * @return The requested Point + * @throws IndexOutOfBoundsException If the specified index is out of range + * @since 2.0 + */ +public Point getPoint(int index) { + if (index < 0 || index >= size_) + throw new IndexOutOfBoundsException( Format( + "Index: {}, Size: {}", index, //$NON-NLS-1$ + size_)); //$NON-NLS-1$ + index *= 2; + return new Point(points[index], points[index + 1]); +} + +/** + * Copies the x and y values at given index into a specified Point. + * This method exists to avoid the creation of a new Point. + * @see #getPoint(int) + * @param p The Point which will be set with the <x, y> values + * @param index The index being requested + * @return The parameter p is returned for convenience + * @since 2.0 + */ +public Point getPoint(Point p, int index) { + if (index < 0 || index >= size_) + throw new IndexOutOfBoundsException( Format( + "Index: {}, Size: {}", index, //$NON-NLS-1$ + size_)); //$NON-NLS-1$ + index *= 2; + p.x = points[index]; + p.y = points[index + 1]; + return p; +} + +/** + * Inserts a given point at a specified index. + * @param p Point to be inserted. + * @param index Position where the point is to be inserted. + * @exception IndexOutOfBoundsException if the index is invalid + * @see #setPoint(Point, int) + * @since 2.0 + */ +public void insertPoint(Point p, int index) { + if (bounds !is null && !bounds.contains(p)) + bounds = null; + if (index > size_ || index < 0) + throw new IndexOutOfBoundsException( Format( + "Index: {}, Size: {}", index, //$NON-NLS-1$ + size_)); //$NON-NLS-1$ + index *= 2; + + int length = points.length; + int old[] = points; + points = new int[length + 2]; + System.arraycopy(old, 0, points, 0, index); + System.arraycopy(old, index, points, index + 2, length - index); + + points[index] = p.x; + points[index + 1] = p.y; + size_++; +} + +/** + * Determines whether any of the line segments represented by this PointList intersect + * the given Rectangle. If a segment touches the given rectangle, that's considered + * intersection. + * + * @param r the rectangle + * @return true if the given rectangle intersects any of the line segments + * represented by this PointList + * @since 3.1 + */ +public bool intersects(Rectangle r) { + if (r.isEmpty()) + return false; + for (int i = 0; i < size_ * 2; i += 2) { + if (r.contains(points[i], points[i + 1])) + return true; + } + int diagonal1x1 = r.x, + diagonal1y1 = r.y, + diagonal1x2 = r.x + r.width - 1, + diagonal1y2 = r.y + r.height - 1, + diagonal2x1 = r.x + r.width - 1, + diagonal2y1 = r.y, + diagonal2x2 = r.x, + diagonal2y2 = r.y + r.height - 1; + for (int i = 0; i < (size_ - 1) * 2; i += 2) { + if (Geometry.linesIntersect(diagonal1x1, diagonal1y1, diagonal1x2, + diagonal1y2, points[i], points[i + 1], points[i + 2], points[i + 3]) + || Geometry.linesIntersect(diagonal2x1, diagonal2y1, diagonal2x2, + diagonal2y2, points[i], points[i + 1], points[i + 2], points[i + 3])) + return true; + } + return false; +} + +/** + * @see dwtx.draw2d.geometry.Translatable#performScale(double) + */ +public void performScale(double factor) { + for (int i = 0; i < points.length; i++) + points[i] = cast(int)Math.floor(points[i] * factor); + bounds = null; +} + +/** + * @see dwtx.draw2d.geometry.Translatable#performTranslate(int, int) + */ +public void performTranslate(int dx, int dy) { + for (int i = 0; i < size_ * 2; i += 2) { + points[i] += dx; + points[i + 1] += dy; + } + if (bounds !is null) + bounds.translate(dx, dy); +} + +/** + * Removes all the points stored by this list. Resets all + * the properties based on the point information. + * + * @since 2.0 + */ +public void removeAllPoints() { + bounds = null; + size_ = 0; +} + +/** + * Removes the point at the specified index from the PointList, and + * returns it. + * @since 2.0 + * @see #addPoint(Point) + * @param index Index of the point to be removed. + * @return The point which has been removed + * @throws IndexOutOfBoundsException if the removal index is beyond the list capacity + */ +public Point removePoint(int index) { + bounds = null; + if (index < 0 || index >= size_) + throw new IndexOutOfBoundsException( Format( + "Index: {}, Size: {}", index, //$NON-NLS-1$ + size_)); //$NON-NLS-1$ + + index *= 2; + Point pt = new Point(points[index], points[index + 1]); + if (index !is size_ * 2 - 2) + System.arraycopy(points, index + 2, points, index, size_ * 2 - index - 2); + size_--; + return pt; +} + +/** + * Reverses the order of the points in the list. + * @since 3.2 + */ +public void reverse() { + int temp; + for (int i = 0, j = size_ * 2 - 2; i < size_; i += 2 , j -= 2) { + temp = points[i]; + points[i] = points[j]; + points[j] = temp; + temp = points[i + 1]; + points[i + 1] = points[j + 1]; + points[j + 1] = temp; + } +} + +/** + * Overwrites a point at a given index in the list with the specified Point. + * @param pt Point which is to be stored at the index. + * @param index Index where the given point is to be stored. + * @since 2.0 + */ +public void setPoint(Point pt, int index) { + if (index < 0 || index >= size_) + throw new IndexOutOfBoundsException( Format( + "Index: {}, Size: {}", index, //$NON-NLS-1$ + size_)); //$NON-NLS-1$ + if (bounds !is null && !bounds.contains(pt)) + bounds = null; + points[index * 2] = pt.x; + points[index * 2 + 1] = pt.y; +} + +/** + * Sets the size of this PointList. + * @param newSize the new size + */ +public void setSize(int newSize) { + if (points.length > newSize * 2) { + size_ = newSize; + return; + } + int[] newArray = new int[newSize * 2]; + System.arraycopy(points, 0, newArray, 0, points.length); + points = newArray; + size_ = newSize; +} + +/** + * Returns the number of points in this PointList. + * @return The number of points + * @since 2.0 + */ +public int size() { + return size_; +} + +/** + * Returns the contents of this PointList as an integer array. The returned array is by + * reference. Any changes made to the array will also be changing the original PointList. + * + * @return the integer array of points by reference + * @since 2.0 + */ +public int[] toIntArray() { + if (points.length !is size_ * 2) { + int[] old = points; + points = new int[size_ * 2]; + System.arraycopy(old, 0, points, 0, size_ * 2); + } + return points; +} + +/** + * Moves the origin (0,0) of the coordinate system of all + * the points to the Point pt. This updates the position + * of all the points in this PointList. + * + * @param pt Position by which all the points will be shifted. + * @see #translate(int,int) + * @since 2.0 + */ +public final void translate(Point pt) { + translate(pt.x, pt.y); +} + +/** + * Moves the origin (0,0) of the coordinate system of all + * the points to the Point (x,y). This updates the position + * of all the points in this PointList. + * + * @param x Amount by which all the points will be shifted on the X axis. + * @param y Amount by which all the points will be shifted on the Y axis. + * @see #translate(Point) + * @since 2.0 + */ +public void translate(int x, int y) { + if (x is 0 && y is 0) + return; + if (bounds !is null) + bounds.translate(x, y); + for (int i = 0; i < size_ * 2; i += 2) { + points[i] += x; + points[i + 1] += y; + } +} + +/** + * Transposes all x and y values. Useful for orientation changes. + * @since 3.2 + */ +public void transpose() { + int temp; + if (bounds !is null) + bounds.transpose(); + for (int i = 0; i < size_ * 2; i += 2) { + temp = points[i]; + points[i] = points[i + 1]; + points[i + 1] = temp; + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/PrecisionDimension.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/PrecisionDimension.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2003, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.PrecisionDimension; + +import dwt.dwthelper.utils; +import dwtx.draw2d.geometry.Dimension; + + +/** + * @author Randy Hudson + */ +public class PrecisionDimension : Dimension { + +/** + * The width in double precision. + */ +public double preciseWidth_; +/** + * The height in double precision. + */ +public double preciseHeight_; + +/** + * Constructs a new precision dimension. + */ +public this() { +} + +/** + * Constructs a new precision dimension with the given values. + * @param width the width + * @param height the height + */ +public this(double width, double height) { + preciseWidth_ = width; + preciseHeight_ = height; + updateInts(); +} + +/** + * Constructs a precision representation of the given dimension. + * @param d the reference dimension + */ +public this(Dimension d) { + preciseHeight_ = d.preciseHeight(); + preciseWidth_ = d.preciseWidth(); + updateInts(); +} + +/** + * @see dwtx.draw2d.geometry.Dimension#performScale(double) + */ +public void performScale(double factor) { + preciseHeight_ *= factor; + preciseWidth_ *= factor; + updateInts(); +} + +/** + * Updates the integer fields using the precise versions. + */ +public final void updateInts() { + width = cast(int)Math.floor(preciseWidth_ + 0.000000001); + height = cast(int)Math.floor(preciseHeight_ + 0.000000001); +} + +/** + * @see dwtx.draw2d.geometry.Dimension#preciseWidth() + */ +public double preciseWidth() { + return preciseWidth_; +} + +/** + * @see dwtx.draw2d.geometry.Dimension#preciseHeight() + */ +public double preciseHeight() { + return preciseHeight_; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/PrecisionPoint.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/PrecisionPoint.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.PrecisionPoint; + +import dwt.dwthelper.utils; +import dwtx.draw2d.geometry.Point; + +/** + * @author danlee + */ +public class PrecisionPoint : Point { + +/** Double value for X **/ +public double preciseX_; + +/** Double value for Y **/ +public double preciseY_; + +/** + * Constructor for PrecisionPoint. + */ +public this() { + super(); +} + +/** + * Constructor for PrecisionPoint. + * @param copy Point from which the initial values are taken + */ +public this(Point copy) { + preciseX_ = copy.preciseX(); + preciseY_ = copy.preciseY(); + updateInts(); +} + +/** + * Constructor for PrecisionPoint. + * @param x X value + * @param y Y value + */ +public this(int x, int y) { + super(x, y); + preciseX_ = x; + preciseY_ = y; +} + +/** + * Constructor for PrecisionPoint. + * @param x X value + * @param y Y value + */ +public this(double x, double y) { + preciseX_ = x; + preciseY_ = y; + updateInts(); +} + +/** + * @see dwtx.draw2d.geometry.Point#getCopy() + */ +public Point getCopy() { + return new PrecisionPoint(preciseX_, preciseY_); +} + + +/** + * @see dwtx.draw2d.geometry.Point#performScale(double) + */ +public void performScale(double factor) { + preciseX_ = preciseX_ * factor; + preciseY_ = preciseY_ * factor; + updateInts(); +} + +/** + * @see dwtx.draw2d.geometry.Point#performTranslate(int, int) + */ +public void performTranslate(int dx, int dy) { + preciseX_ += dx; + preciseY_ += dy; + updateInts(); +} + +/** + * @see dwtx.draw2d.geometry.Point#setLocation(Point) + */ +public Point setLocation(Point pt) { + preciseX_ = pt.preciseX(); + preciseY_ = pt.preciseY(); + updateInts(); + return this; +} + +/** + * Updates the integer fields using the precise versions. + */ +public final void updateInts() { + x = cast(int)Math.floor(preciseX_ + 0.000000001); + y = cast(int)Math.floor(preciseY_ + 0.000000001); +} + +/** + * @see dwtx.draw2d.geometry.Point#preciseX() + */ +public double preciseX() { + return preciseX_; +} + +/** + * @see dwtx.draw2d.geometry.Point#preciseY() + */ +public double preciseY() { + return preciseY_; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/PrecisionRectangle.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/PrecisionRectangle.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,396 @@ +/******************************************************************************* + * Copyright (c) 2003, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.PrecisionRectangle; + +import dwt.dwthelper.utils; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.PrecisionPoint; + +/** + * A Rectangle implementation using floating point values which are truncated into the inherited + * integer fields. The use of floating point prevents rounding errors from accumulating. + * @author hudsonr + * Created on Apr 9, 2003 + */ +public final class PrecisionRectangle : Rectangle { + +/** Double value for height */ +public double preciseHeight_; + +/** Double value for width */ +public double preciseWidth_; + +/** Double value for X */ +public double preciseX_; + +/** Double value for Y */ +public double preciseY_; + +/** + * Constructs a new PrecisionRectangle with all values 0. + */ +public this() { } + +/** + * Constructs a new PrecisionRectangle from the given integer Rectangle. + * @param rect the base rectangle + */ +public this(Rectangle rect) { + preciseX_ = rect.preciseX(); + preciseY_ = rect.preciseY(); + preciseWidth_ = rect.preciseWidth(); + preciseHeight_ = rect.preciseHeight(); + updateInts(); +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#getCopy() + */ +public Rectangle getCopy() { + return getPreciseCopy(); +} + +/** + * Returns a precise copy of this. + * @return a precise copy + */ +public PrecisionRectangle getPreciseCopy() { + PrecisionRectangle result = new PrecisionRectangle(); + result.preciseX_ = preciseX_; + result.preciseY_ = preciseY_; + result.preciseWidth_ = preciseWidth_; + result.preciseHeight_ = preciseHeight_; + result.updateInts(); + return result; +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#crop(dwtx.draw2d.geometry.Insets) + */ +public Rectangle crop(Insets insets) { + if (insets is null) + return this; + setX(preciseX_ + insets.left); + setY(preciseY_ + insets.top); + setWidth(preciseWidth_ - (insets.getWidth())); + setHeight(preciseHeight_ - (insets.getHeight())); + + return this; +} + +/** + * @see Rectangle#equals(Object) + */ +public override int opEquals(Object o) { + if ( auto pr = cast(PrecisionRectangle)o ) { + return super.opEquals(o) + && Math.abs(pr.preciseX_ - preciseX_) < 0.000000001 + && Math.abs(pr.preciseY_ - preciseY_) < 0.000000001 + && Math.abs(pr.preciseWidth_ - preciseWidth_) < 0.000000001 + && Math.abs(pr.preciseHeight_ - preciseHeight_) < 0.00000001; + } + + return super.opEquals(o); +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#performScale(double) + */ +public void performScale(double factor) { + preciseX_ *= factor; + preciseY_ *= factor; + preciseWidth_ *= factor; + preciseHeight_ *= factor; + updateInts(); +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#performTranslate(int, int) + */ +public void performTranslate(int dx, int dy) { + preciseX_ += dx; + preciseY_ += dy; + x += dx; + y += dy; +} + +/** + * Returns the bottom coordinte in double precision. + * @return the precise bottom + */ +public double preciseBottom() { + return preciseHeight_ + preciseY_; +} + +/** + * Returns the right side in double precision. + * @return the precise right + */ +public double preciseRight() { + return preciseWidth_ + preciseX_; +} + + +/** + * @see dwtx.draw2d.geometry.Rectangle#resize(dwtx.draw2d.geometry.Dimension) + */ +public Rectangle resize(Dimension sizeDelta) { + preciseWidth_ += sizeDelta.preciseWidth(); + preciseHeight_ += sizeDelta.preciseHeight(); + updateInts(); + return this; +} + +/** + * Sets the height. + * @param value the new height + */ +public void setHeight(double value) { + preciseHeight_ = value; + height = cast(int)Math.floor(preciseHeight_ + 0.000000001); +} + +/** + * Sets the width. + * @param value the new width + */ +public void setWidth(double value) { + preciseWidth_ = value; + width = cast(int)Math.floor(preciseWidth_ + 0.000000001); +} + +/** + * Sets the x value. + * @param value the new x value + */ +public void setX(double value) { + preciseX_ = value; + x = cast(int)Math.floor(preciseX_ + 0.000000001); +} + +/** + * Sets the y value. + * @param value the new y value + */ +public void setY(double value) { + preciseY_ = value; + y = cast(int)Math.floor(preciseY_ + 0.000000001); +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#translate(dwtx.draw2d.geometry.Point) + */ +public Rectangle translate(Point p) { + preciseX_ += p.preciseX(); + preciseY_ += p.preciseY(); + updateInts(); + return this; +} + +/** + * Unions the given PrecisionRectangle with this rectangle and returns this + * for convenience. + * @since 3.0 + * @param other the rectangle being unioned + * @return this for convenience + * @deprecated + * Use {@link #union(Rectangle)} instead + */ +public PrecisionRectangle union_(PrecisionRectangle other) { + double newright = Math.max(preciseRight(), other.preciseRight()); + double newbottom = Math.max(preciseBottom(), other.preciseBottom()); + preciseX_ = Math.min(preciseX_, other.preciseX_); + preciseY_ = Math.min(preciseY_, other.preciseY_); + preciseWidth_ = newright - preciseX_; + preciseHeight_ = newbottom - preciseY_; + updateInts(); + + return this; +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#union(dwtx.draw2d.geometry.Rectangle) + */ +public Rectangle union_(Rectangle other) { + double newright = Math.max(preciseRight(), other.preciseX() + other.preciseWidth()); + double newbottom = Math.max(preciseBottom(), other.preciseY() + other.preciseHeight()); + preciseX_ = Math.min(preciseX_, other.preciseX()); + preciseY_ = Math.min(preciseY_, other.preciseY()); + preciseWidth_ = newright - preciseX_; + preciseHeight_ = newbottom - preciseY_; + updateInts(); + + return this; +} + +/** + * Updates the integer values based on the current precise values. The integer values ar + * the floor of the double values. This is called automatically when calling api which is + * overridden in this class. + * @since 3.0 + */ +public void updateInts() { + x = cast(int)Math.floor(preciseX_ + 0.000000001); + y = cast(int)Math.floor(preciseY_ + 0.000000001); + width = cast(int)Math.floor(preciseWidth_ + preciseX_ + 0.000000001) - x; + height = cast(int)Math.floor(preciseHeight_ + preciseY_ + 0.000000001) - y; +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#union(dwtx.draw2d.geometry.Point) + */ +public void union_(Point p) { + if (p.preciseX() < preciseX_) { + preciseWidth_ += (preciseX_ - p.preciseX()); + preciseX_ = p.preciseX(); + } else { + double right = preciseX_ + preciseWidth_; + if (p.preciseX() > right) { + preciseWidth_ = p.preciseX() - preciseX_; + } + } + if (p.preciseY() < preciseY_) { + preciseHeight_ += (preciseY - p.preciseY()); + preciseY_ = p.preciseY(); + } else { + double bottom = preciseY_ + preciseHeight_; + if (p.preciseY() > bottom) { + preciseHeight_ = p.preciseY() - preciseY_; + } + } + updateInts(); +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#transpose() + */ +public Rectangle transpose() { + double temp = preciseX_; + preciseX_ = preciseY_; + preciseY_ = temp; + temp = preciseWidth_; + preciseWidth_ = preciseHeight_; + preciseHeight_ = temp; + super.transpose(); + return this; +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#setLocation(dwtx.draw2d.geometry.Point) + */ +public Rectangle setLocation(Point loc) { + preciseX_ = loc.preciseX(); + preciseY_ = loc.preciseY(); + updateInts(); + return this; +} + +/** + * Returns the precise geometric centre of the rectangle + * + * @return PrecisionPoint geometric center of the rectangle + * @since 3.4 + */ +public Point getCenter() { + return new PrecisionPoint(preciseX_ + preciseWidth_ / 2.0, preciseY_ + preciseHeight_ / 2.0); +} + +/** + * Shrinks the sides of this Rectangle by the horizontal and vertical values + * provided as input, and returns this Rectangle for convenience. The center of + * this Rectangle is kept constant. + * + * @param h Horizontal reduction amount + * @param v Vertical reduction amount + * @return this for convenience + * @since 3.4 + */ +public Rectangle shrink(double h, double v) { + preciseX_ += h; + preciseWidth_ -= (h + h); + preciseY_ += v; + preciseHeight_ -= (v + v); + updateInts(); + return this; +} + +/** + * Expands the horizontal and vertical sides of this Rectangle with the values + * provided as input, and returns this for convenience. The location of its + * center is kept constant. + * + * @param h Horizontal increment + * @param v Vertical increment + * @return this for convenience + * @since 3.4 + */ +public Rectangle expand(double h, double v) { + return shrink(-h, -v); +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#shrink(int, int) + */ +public Rectangle shrink(int h, int v) { + return shrink(cast(double)h, cast(double)v); +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#contains(dwtx.draw2d.geometry.Point) + */ +public bool contains(Point p) { + return preciseX_ <= p.preciseX() && p.preciseX() <= preciseX_ + preciseWidth_ + && preciseY_ <= p.preciseY() && p.preciseY() <= preciseY_ + preciseHeight_; +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#preciseX() + */ +public double preciseX() { + return preciseX_; +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#preciseY() + */ +public double preciseY() { + return preciseY_; +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#preciseWidth() + */ +public double preciseWidth() { + return preciseWidth_; +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#preciseHeight() + */ +public double preciseHeight() { + return preciseHeight_; +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#setSize(dwtx.draw2d.geometry.Dimension) + */ +public Rectangle setSize(Dimension d) { + preciseWidth_ = d.preciseWidth(); + preciseHeight_ = d.preciseHeight(); + return super.setSize(d); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/Ray.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/Ray.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,190 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.Ray; + +import dwt.dwthelper.utils; +import tango.text.convert.Format; + +import dwtx.draw2d.geometry.Point; + + +/** + * Represents a 2-dimensional directional Vector, or Ray. {@link java.util.Vector} is + * commonly imported, so the name Ray was chosen. + */ +public final class Ray { + +/** the X value */ +public int x; +/** the Y value*/ +public int y; + +/** + * Constructs a Ray <0, 0> with no direction and magnitude. + * @since 2.0 + */ +public this() { } + +/** + * Constructs a Ray pointed in the specified direction. + * + * @param x X value. + * @param y Y value. + * @since 2.0 + */ +public this(int x, int y) { + this.x = x; + this.y = y; +} + +/** + * Constructs a Ray pointed in the direction specified by a Point. + * @param p the Point + * @since 2.0 + */ +public this(Point p) { + x = p.x; y = p.y; +} + +/** + * Constructs a Ray representing the direction and magnitude between to provided Points. + * @param start Strarting Point + * @param end End Point + * @since 2.0 + */ +public this(Point start, Point end) { + x = end.x - start.x; + y = end.y - start.y; +} + +/** + * Constructs a Ray representing the difference between two provided Rays. + * @param start The start Ray + * @param end The end Ray + * @since 2.0 + */ +public this(Ray start, Ray end) { + x = end.x - start.x; + y = end.y - start.y; +} + +/** + * Calculates the magnitude of the cross product of this Ray with another. + * Represents the amount by which two Rays are directionally different. + * Parallel Rays return a value of 0. + * @param r Ray being compared + * @return The assimilarity + * @see #similarity(Ray) + * @since 2.0 + */ +public int assimilarity(Ray r) { + return Math.abs(x * r.y - y * r.x); +} + +/** + * Calculates the dot product of this Ray with another. + * @param r the Ray used to perform the dot product + * @return The dot product + * @since 2.0 + */ +public int dotProduct(Ray r) { + return x * r.x + y * r.y; +} + +/** + * @see java.lang.Object#equals(Object) + */ +public override int opEquals(Object obj) { + if (obj is this) + return true; + if ( auto r = cast(Ray)obj ) { + return x is r.x && y is r.y; + } + return false; +} + +/** + * Creates a new Ray which is the sum of this Ray with another. + * @param r Ray to be added with this Ray + * @return a new Ray + * @since 2.0 + */ +public Ray getAdded(Ray r) { + return new Ray(r.x + x, r.y + y); +} + +/** + * Creates a new Ray which represents the average of this Ray with another. + * @param r Ray to calculate the average. + * @return a new Ray + * @since 2.0 + */ +public Ray getAveraged(Ray r) { + return new Ray ((x + r.x) / 2, (y + r.y) / 2); +} + +/** + * Creates a new Ray which represents this Ray scaled by the amount provided. + * @param s Value providing the amount to scale. + * @return a new Ray + * @since 2.0 + */ +public Ray getScaled(int s) { + return new Ray(x * s, y * s); +} + +/** + * @see java.lang.Object#toHash() + */ +public override hash_t toHash() { + return (x * y) ^ (x + y); +} + +/** + * Returns true if this Ray has a non-zero horizontal comonent. + * @return true if this Ray has a non-zero horizontal comonent + * @since 2.0 + */ +public bool isHorizontal() { + return x !is 0; +} + +/** + * Returns the length of this Ray. + * @return Length of this Ray + * @since 2.0 + */ +public double length() { + return Math.sqrt(cast(real)dotProduct(this)); +} + +/** + * Calculates the similarity of this Ray with another. + * Similarity is defined as the absolute value of the dotProduct() + * @param r Ray being tested for similarity + * @return the Similarity + * @see #assimilarity(Ray) + * @since 2.0 + */ +public int similarity(Ray r) { + return Math.abs(dotProduct(r)); +} + +/** + * @return a String representation + */ +public String toString() { + return Format("({}, {})", x, y );//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/Rectangle.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/Rectangle.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,977 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.Rectangle; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Translatable; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.PositionConstants; +import tango.text.convert.Format; + +static import dwt.graphics.Rectangle; + +/** + * Represents a Rectangle(x, y, width, height). This class provides various methods + * for manipulating this Rectangle or creating new derived geometrical Objects. + */ +public class Rectangle + : Cloneable, /+java.io.Serializable, +/Translatable +{ +/** the X value */ +public int x; +/** the Y value */ +public int y; +/** the width*/ +public int width; +/** the height */ +public int height; + +/**A singleton for use in short calculations. Use to avoid newing unnecessary objects.*/ +public static const Rectangle SINGLETON; + +static this(){ + SINGLETON = new Rectangle(); +} + +static final long serialVersionUID = 1; + +/** + * Constructs a Rectangle at the origin with zero width and height. + * + * @since 2.0 + */ +public this() { } + +/** + * Constructs a Rectangle given a location and size. + * + * @param p the location + * @param size the size + * @since 2.0 + */ +public this(Point p, Dimension size) { + this(p.x, p.y, size.width, size.height); +} + +/** + * Constructs a copy of the provided Rectangle. + * + * @param rect Rectangle supplying the initial values + * @since 2.0 + */ +public this(Rectangle rect) { + this(rect.x, rect.y, rect.width, rect.height); +} + +/** + * Constructs a copy of the provided DWT {@link dwt.graphics.Rectangle}. + * + * @param rect The DWT Rectangle being copied + * @since 2.0 + */ +public this(dwt.graphics.Rectangle.Rectangle rect) { + this(rect.x, rect.y, rect.width, rect.height); +} + +/** + * Constructs a Rectangle with the provided values. + * + * @param x X location + * @param y Y location + * @param width Width of the rectangle + * @param height Height of the rectangle + * @since 2.0 + */ +public this(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; +} + +/** + * Constructs the smallest Rectangle that contains the specified Points. + * + * @param p1 Upper left hand corner + * @param p2 Lower right hand corner + * @since 2.0 + */ +public this(Point p1, Point p2) { + this.x = Math.min(p1.x, p2.x); + this.y = Math.min(p1.y, p2.y); + this.width = Math.abs(p2.x - p1.x) + 1; + this.height = Math.abs(p2.y - p1.y) + 1; +} + +/** + * Returns the y-coordinate of the bottom of this Rectangle. + * + * @return The Y coordinate of the bottom + * @since 2.0 + */ +public int bottom() { + return y + height; +} + +/** + * Returns whether the given point is within the boundaries of this Rectangle. + * The boundaries are inclusive of the top and left edges, but exclusive of the + * bottom and right edges. + * + * @param pt Point being tested for containment + * @return true if the Point is within this Rectangle + * @since 2.0 + */ +public bool contains(Point pt) { + return contains(pt.x, pt.y); +} + +/** + * Returns true if the given rectangle is contained within the + * boundaries of this Rectangle. + * + * @param rect the Rectangle to test + * @return true if the Rectangle is within this Rectangle + */ +public bool contains(Rectangle rect) { + return x <= rect.x + && y <= rect.y + && right() >= rect.right() + && bottom() >= rect.bottom(); +} + +/** + * Returns whether the given coordinates are within the boundaries of this + * Rectangle. The boundaries are inclusive of the top and left edges, but + * exclusive of the bottom and right edges. + * + * @param x X value + * @param y Y value + * @return true if the coordinates are within this Rectangle + * @since 2.0 + */ +public bool contains(int x, int y) { + return y >= this.y + && y < this.y + this.height + && x >= this.x + && x < this.x + this.width; +} + +/** + * Crops this rectangle by the amount specified in insets. + * + * @param insets Insets to be removed from the Rectangle + * @return this for convenience + * @since 2.0 + */ +public Rectangle crop(Insets insets) { + if (insets is null) + return this; + x += insets.left; + y += insets.top; + width -= (insets.getWidth()); + height -= (insets.getHeight()); + return this; +} + +/** + * Returns whether the input object is equal to this Rectangle or not. + * Rectangles are equivalent if their x, y, height, and width values are the + * same. + * + * @param o Object being tested for equality + * @return Returns the result of the equality test + * @since 2.0 + */ +public override int opEquals(Object o) { + if (this is o) return true; + if (auto r = cast(Rectangle)o ) { + return (x is r.x) + && (y is r.y) + && (width is r.width) + && (height is r.height); + } + return false; +} + +/** + * Expands the horizontal and vertical sides of this Rectangle with the values + * provided as input, and returns this for convenience. The location of its + * center is kept constant. + * + * @param h Horizontal increment + * @param v Vertical increment + * @return this for convenience + * @since 2.0 + */ +public Rectangle expand(int h, int v) { + return shrink(-h, -v); +} + +/** + * Expands the horizontal and vertical sides of this Rectangle by the width and + * height of the given Insets, and returns this for convenience. + * + * @param insets contains the amounts to expand on each side + * @return this for convenience + * @since 2.0 + */ +public Rectangle expand(Insets insets) { + x -= insets.left; + y -= insets.top; + height += insets.getHeight(); + width += insets.getWidth(); + return this; +} + +/** + * Returns a new Point representing the middle point of the bottom side of this + * Rectangle. + * + * @return Point at the bottom of the Rectangle + * @since 2.0 + */ +public Point getBottom() { + return new Point(x + width / 2, bottom()); +} + +/** + * Returns a new Point representing the bottom left point of this Rectangle. + * + * @return Point at the bottom left of the rectangle + * @since 2.0 + */ +public Point getBottomLeft() { + return new Point(x, y + height); +} + +/** + * Returns a new Point representing the bottom right point of this Rectangle. + * + * @return Point at the bottom right of the rectangle + * @since 2.0 + */ +public Point getBottomRight() { + return new Point(x + width, y + height); +} + +/** + * Returns a new point representing the center of this Rectangle. + * + * @return Point at the center of the rectangle + */ +public Point getCenter() { + return new Point(x + width / 2, y + height / 2); +} + +/** + * Returns a new Rectangle which has the exact same parameters as this + * Rectangle. + * + * @return Copy of this Rectangle + * @since 2.0 + */ +public Rectangle getCopy() { +// try { +// return cast(Rectangle)clone(); +// } catch (CloneNotSupportedException exc) { + return new Rectangle(this); +// } +} + +/** + * Returns a new Rectangle with the specified insets cropped. + * + * @param insets Insets being cropped from the Rectangle + * @return Cropped new Rectangle + */ +public Rectangle getCropped(Insets insets) { + Rectangle r = new Rectangle(this); + r.crop(insets); + return r; +} + +/** + * Returns a new incremented Rectangle, where the sides are expanded by the + * horizonatal and vertical values provided. The center of the Rectangle is + * maintained constant. + * + * @param h Horizontal increment + * @param v Vertical inrement + * @return A new expanded Rectangle + * @since 2.0 + */ +public Rectangle getExpanded(int h, int v) { + return (new Rectangle(this)).expand(h, v); +} + +/** + * Creates and returns a new Rectangle with the bounds of this + * Rectangle, expanded by the given Insets. + * + * @param insets The insets used to expand this rectangle + * @return A new expanded Rectangle + * @since 2.0 + */ +public Rectangle getExpanded(Insets insets) { + return (new Rectangle(this)).expand(insets); +} + + +/** + * Returns a new Rectangle which has the intersection of this Rectangle and the + * rectangle provided as input. Returns an empty Rectangle if there is no + * interection. + * + * @param rect Rectangle provided to test for intersection + * @return A new Rectangle representing the intersection + * @since 2.0 + */ +public Rectangle getIntersection(Rectangle rect) { + int x1 = Math.max(x, rect.x); + int x2 = Math.min(x + width, rect.x + rect.width); + int y1 = Math.max(y, rect.y); + int y2 = Math.min(y + height, rect.y + rect.height); + if (((x2 - x1) < 0) || ((y2 - y1) < 0)) + return new Rectangle(0, 0, 0, 0); // No intersection + else + return new Rectangle(x1, y1, x2 - x1, y2 - y1); +} + +/** + * Returns a new Point representing the middle point of the left hand side of + * this Rectangle. + * + * @return Point at the left of the Rectangle + */ +public Point getLeft() { + return new Point(x, y + height / 2); +} + +/** + * Returns the upper left hand corner of the rectangle. + * + * @return Location of the rectangle + * @see #setLocation(Point) + */ +public Point getLocation() { + return new Point(x, y); +} + +/** + *

Returns an integer which represents the position of the given point with respect to + * this rectangle. Possible return values are bitwise ORs of the constants WEST, EAST, + * NORTH, and SOUTH as found in {@link dwtx.draw2d.PositionConstants}. + * + *

Returns PositionConstant.NONE if the given point is inside this Rectangle. + * + * @param pt The Point whose position has to be determined + * @return An int which is a PositionConstant + * @see dwtx.draw2d.PositionConstants + * @since 2.0 + */ +public int getPosition(Point pt) { + int result = PositionConstants.NONE; + + if (contains(pt)) + return result; + + if (pt.x < x) + result = PositionConstants.WEST; + else if (pt.x >= (x + width)) + result = PositionConstants.EAST; + + if (pt.y < y) + result = result | PositionConstants.NORTH; + else if (pt.y >= (y + height)) + result = result | PositionConstants.SOUTH; + + return result; +} + +/** + * Returns a new Rectangle which is equivalent to this Rectangle with its + * dimensions modified by the passed width w and height h. + + * @param w Amount by which width is to be resized + * @param h Amount by which height is to be resized + * @return a new rectangle with its width and height modified + */ +public Rectangle getResized(int w, int h) { + return (new Rectangle(this)).resize(w, h); +} + +/** + * Returns a new Rectangle which is equivalent to this Rectangle with its + * dimensions modified by the passed Dimension d. + * + * @param d Dimensions by which the rectangle's size should be modified + * @return The new rectangle with the modified dimensions + * @since 2.0 + */ +public Rectangle getResized(Dimension d) { + return (new Rectangle(this)).resize(d); +} + +/** + * Returns a new Point which represents the middle point of the right hand side + * of this Rectangle. + * + * @return Point at the right of the Rectangle + * @since 2.0 + */ +public Point getRight() { + return new Point(right(), y + height / 2); +} + +/** + * Retuns the dimensions of this Rectangle. + * + * @return Size of this Rectangle as a Dimension + * @since 2.0 + */ +public Dimension getSize() { + return new Dimension(width, height); +} + +/** + * Returns a new Point which represents the middle point of the top side of this + * Rectangle. + * + * @return Point at the top of the Rectangle + * @since 2.0 + */ +public Point getTop() { + return new Point(x + width / 2, y); +} + +/** + * Returns a new Point which represents the top left hand corner of this + * Rectangle. + * + * @return Point at the top left of the rectangle + * @since 2.0 + */ +public Point getTopLeft() { + return new Point(x, y); +} + +/** + * Returns a new Point which represents the top right hand corner of this + * Rectangle. + * + * @return Point at the top right of the rectangle + * @since 2.0 + */ +public Point getTopRight() { + return new Point(x + width, y); +} + +/** + * Returns a new Rectangle which is shifted along each axis by the passed + * values. + * + * @param dx Displacement along X axis + * @param dy Displacement along Y axis + * @return The new translated rectangle + * @since 2.0 + */ +public Rectangle getTranslated(int dx, int dy) { + return (new Rectangle(this)).translate(dx, dy); +} + +/** + * Returns a new Rectangle which is shifted by the position of the given Point. + * + * @param pt Point providing the amount of shift along each axis + * @return The new translated Rectangle + * @since 2.0 + */ +public Rectangle getTranslated(Point pt) { + return (new Rectangle(this)).translate(pt); +} + +/** + * Returns a new rectangle whose width and height have been interchanged, as well + * as its x and y values. This can be useful in orientation changes. + * + * @return The transposed rectangle + * @since 2.0 + */ +public Rectangle getTransposed() { + Rectangle r = new Rectangle(this); + r.transpose(); + return r; +} + +/** + * Returns a new Rectangle which contains both this Rectangle and the Rectangle + * supplied as input. + * + * @param rect Rectangle for calculating union_ + * @return A new unioned Rectangle + * @since 2.0 + */ +public Rectangle getUnion(Rectangle rect) { + if (rect is null || rect.isEmpty()) + return new Rectangle(this); + Rectangle union_ = new Rectangle( + Math.min(x, rect.x), + Math.min(y, rect.y), 0, 0); + union_.width = Math.max(x + width, rect.x + rect.width) - union_.x; + union_.height = Math.max(y + height, rect.y + rect.height) - union_.y; + return union_; +} + +/** + * @see java.lang.Object#toHash() + */ +public override hash_t toHash() { + return (x + height + 1) * (y + width + 1) ^ x ^ y; +} + +/** + * Sets the size of this Rectangle to the intersection region with the Rectangle + * supplied as input, and returns this for convenience. The location and + * dimensions are set to zero if there is no intersection with the input + * Rectangle. + * + * @param rect Rectangle for the calculating intersection. + * @return this for convenience + * @since 2.0 + */ +public Rectangle intersect(Rectangle rect) { + int x1 = Math.max(x, rect.x); + int x2 = Math.min(x + width, rect.x + rect.width); + int y1 = Math.max(y, rect.y); + int y2 = Math.min(y + height, rect.y + rect.height); + if (((x2 - x1) < 0) || ((y2 - y1) < 0)) + x = y = width = height = 0; // No intersection + else { + x = x1; + y = y1; + width = x2 - x1; + height = y2 - y1; + } + return this; +} + +/** + * Returns true if the input Rectangle intersects this Rectangle. + * + * @param rect Rectangle for the intersetion test + * @return true if the input Rectangle intersects this Rectangle + * @since 2.0 + */ +public bool intersects(Rectangle rect) { + return rect.x < x + width + && rect.y < y + height + && rect.x + rect.width > x + && rect.y + rect.height > y; +} + +/** + * Returns true if this Rectangle's width or height is less than or + * equal to 0. + * + * @return true if this Rectangle is empty + * @since 2.0 + */ +public bool isEmpty() { + return width <= 0 || height <= 0; +} + +/** + * @see Translatable#performScale(double) + */ +public void performScale(double factor) { + scale(factor); +} + +/** + * @see Translatable#performTranslate(int, int) + */ +public void performTranslate(int dx, int dy) { + translate(dx, dy); +} + +/** + * Resizes this Rectangle by the Dimension provided as input and returns this + * for convenience. This Rectange's width will become this.width + sizeDelta.width. + * Likewise for height. + * + * @param sizeDelta Resize data as a Dimension + * @return this for convenience + * @since 2.0 + */ +public Rectangle resize(Dimension sizeDelta) { + width += sizeDelta.width; + height += sizeDelta.height; + return this; +} + +/** + * Resizes this Rectangle by the values supplied as input and returns this for + * convenience. This Rectangle's width will become this.width + dw. This + * Rectangle's height will become this.height + dh. + * + * @param dw Amount by which width is to be resized + * @param dh Amount by which height is to be resized + * @return this for convenience + * @since 2.0 + */ +public Rectangle resize(int dw, int dh) { + width += dw; + height += dh; + return this; +} + +/** + * Returns the x-coordinate of the right side of this Rectangle. + * + * @return The X coordinate of the right side + * @since 2.0 + */ +public int right() { + return x + width; +} + +/** + * Scales the location and size of this Rectangle by the given scale and returns + * this for convenience. + * + * @param scaleFactor The factor by which this rectangle will be scaled + * @return this for convenience + * @since 2.0 + */ +public final Rectangle scale(double scaleFactor) { + return scale(scaleFactor, scaleFactor); +} + +/** + * Scales the location and size of this Rectangle by the given scales and returns + * this for convenience. + * + * @param scaleX the factor by which the X dimension has to be scaled + * @param scaleY the factor by which the Y dimension has to be scaled + * @return this for convenience + * @since 2.0 + */ +public Rectangle scale(double scaleX, double scaleY) { + int oldX = x; + int oldY = y; + x = cast(int)(Math.floor(x * scaleX)); + y = cast(int)(Math.floor(y * scaleY)); + width = cast(int)(Math.ceil((oldX + width) * scaleX)) - x; + height = cast(int)(Math.ceil((oldY + height) * scaleY)) - y; + return this; +} + +/** + * Sets the parameters of this Rectangle from the Rectangle passed in and + * returns this for convenience. + * + * @return this for convenience + * @param rect Rectangle providing the bounding values + * @since 2.0 + */ +public Rectangle setBounds(Rectangle rect) { + x = rect.x; + y = rect.y; + width = rect.width; + height = rect.height; + return this; +} + +/** + * Sets the location of this Rectangle to the point given as input and returns + * this for convenience. + * + * @return this for convenience + * @param p New position of this Rectangle + * @since 2.0 + */ +public Rectangle setLocation(Point p) { + x = p.x; + y = p.y; + return this; +} + +/** + * Sets the location of this Rectangle to the coordinates given as input and + * returns this for convenience. + * + * @param x1 The new X coordinate + * @param y1 The new Y coordinate + * @return this for convenience + * @since 2.0 + */ +public Rectangle setLocation(int x1, int y1) { + x = x1; + y = y1; + return this; +} + +/** + * Sets the width and height of this Rectangle to the width and height of the + * given Dimension and returns this for convenience. + * + * @param d The new Dimension + * @return this for convenience + * @since 2.0 + */ +public Rectangle setSize(Dimension d) { + width = d.width; + height = d.height; + return this; +} + +/** + * Sets the width of this Rectangle to w and the height of this + * Rectangle to h and returns this for convenience. + * + * @return this for convenience + * @param w The new width + * @param h The new height + * @since 2.0 + */ +public Rectangle setSize(int w, int h) { + width = w; + height = h; + return this; +} + +/** + * Shrinks the sides of this Rectangle by the horizontal and vertical values + * provided as input, and returns this Rectangle for convenience. The center of + * this Rectangle is kept constant. + * + * @param h Horizontal reduction amount + * @param v Vertical reduction amount + * @return this for convenience + * @since 2.0 + */ +public Rectangle shrink(int h, int v) { + x += h; + width -= (h + h); + y += v; + height -= (v + v); + return this; +} + +/** + * Returns the description of this Rectangle. + * + * @return String containing the description + * @since 2.0 + */ +public override String toString() { + return Format("Rectangle({}, {}, {}, {})", x, y, width, height ); +} + +/** + * Returns true if the input Rectangle touches this Rectangle. + * + * @param rect Rectangle being checked for contact + * @return true if rect touches this Rectangle + * @since 2.0 + */ +public bool touches(Rectangle rect) { + return rect.x <= x + width + && rect.y <= y + height + && rect.x + rect.width >= x + && rect.y + rect.height >= y; +} + +/** + * Moves this Rectangle horizontally by the x value of the given Point and + * vertically by the y value of the given Point, then returns this Rectangle for + * convenience. + * + * @param p Point which provides translation information + * @return this for convenience + */ +public Rectangle translate(Point p) { + x += p.x; + y += p.y; + return this; +} + +/** + * Moves this Rectangle horizontally by dx and vertically by dy, then returns + * this Rectangle for convenience. + * + * @param dx Shift along X axis + * @param dy Shift along Y axis + * @return this for convenience + * @since 2.0 + */ +public Rectangle translate(int dx, int dy) { + x += dx; + y += dy; + return this; +} + +/** + * Switches the x and y values, as well as the width and height of this Rectangle. + * Useful for orientation changes. + * + * @return this for convenience + * @since 2.0 + */ +public Rectangle transpose() { + int temp = x; + x = y; + y = temp; + temp = width; + width = height; + height = temp; + return this; +} + +/** + * Unions this Rectangle's width and height with the specified Dimension. + * + * @param d Dimension being unioned + * @return this for convenience + * @since 2.0 + */ +public Rectangle union_(Dimension d) { + width = Math.max(width, d.width); + height = Math.max(height, d.height); + return this; +} + +/** + * Updates this Rectangle's bounds to the minimum size which can hold both this + * Rectangle and the coordinate (x,y). + * + * @return this for convenience + * @param x1 X coordinate + * @param y1 Y coordinate + * @since 2.0 + */ +public Rectangle union_(int x1, int y1) { + if (x1 < x) { + width += (x - x1); + x = x1; + } else { + int right = x + width; + if (x1 >= right) { + right = x1 + 1; + width = right - x; + } + } + if (y1 < y) { + height += (y - y1); + y = y1; + } else { + int bottom = y + height; + if (y1 >= bottom) { + bottom = y1 + 1; + height = bottom - y; + } + } + return this; +} + +/** + * Updates this Rectangle's bounds to the minimum size which can hold both this + * Rectangle and the given Point. + * + * @param p Point to be unioned with this Rectangle + * @since 2.0 + */ +public void union_(Point p) { + union_(p.x, p.y); +} + +/** + * Updates this Rectangle's dimensions to the minimum size which can hold both + * this Rectangle and the given Rectangle. + * + * @return this for convenience + * @param rect Rectangle to be unioned with this Rectangle + * @since 2.0 + */ +public Rectangle union_(Rectangle rect) { + if (rect is null) + return this; + return union_(rect.x, rect.y, rect.width, rect.height); +} + +/** + * Updates this Rectangle's dimensions to the minimum size which can hold both + * this Rectangle and the rectangle (x, y, w, h). + * + * @param x X coordiante of desired union_. + * @param y Y coordiante of desired union_. + * @param w Width of desired union_. + * @param h Height of desired union_. + * @return this for convenience + * @since 2.0 + */ +public Rectangle union_(int x, int y, int w, int h) { + int right = Math.max(this.x + width, x + w); + int bottom = Math.max(this.y + height, y + h); + this.x = Math.min(this.x, x); + this.y = Math.min(this.y, y); + this.width = right - this.x; + this.height = bottom - this.y; + return this; +} + +/** + * Returns double x coordinate + * + * @return double x coordinate + * @since 3.4 + */ +public double preciseX() { + return x; +} + +/** + * Returns double y coordinate + * + * @return double y coordinate + * @since 3.4 + */ +public double preciseY() { + return y; +} + +/** + * Returns double width + * + * @return double width + * @since 3.4 + */ +public double preciseWidth() { + return width; +} + +/** + * Returns double height + * + * @return double height + * @since 3.4 + */ +public double preciseHeight() { + return height; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/Transform.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/Transform.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.Transform; + +import dwt.dwthelper.utils; +import dwtx.draw2d.geometry.Point; + +/** + * Provides support for transformations of scaling, translation and rotation. + */ +public class Transform { + +private double + scaleX = 1.0, + scaleY = 1.0, + dx, dy, + cos = 1.0, + sin; + +/** + * Sets the value for the amount of scaling to be done along both axes. + * + * @param scale Scale factor + * @since 2.0 + */ +public void setScale(double scale) { + scaleX = scaleY = scale; +} + +/** + * Sets the value for the amount of scaling to be done along X and Y axes + * individually. + * + * @param x Amount of scaling on X axis + * @param y Amount of scaling on Y axis + * @since 2.0 + */ +public void setScale(double x, double y) { + scaleX = x; + scaleY = y; +} + +/** + * Sets the rotation angle. + * + * @param angle Angle of rotation + * @since 2.0 + */ +public void setRotation(double angle) { + cos = Math.cos(angle); + sin = Math.sin(angle); +} + +/** + * Sets the translation amounts for both axes. + * + * @param x Amount of shift on X axis + * @param y Amount of shift on Y axis + * @since 2.0 + */ +public void setTranslation(double x, double y) { + dx = x; + dy = y; +} + +/** + * Returns a new transformed Point of the input Point based on the transformation + * values set. + * + * @param p Point being transformed + * @return The transformed Point + * @since 2.0 + */ +public Point getTransformed(Point p) { + double x = p.x; + double y = p.y; + double temp; + x *= scaleX; + y *= scaleY; + + temp = x * cos - y * sin; + y = x * sin + y * cos; + x = temp; + return new Point(Math.round(x + dx), Math.round(y + dy)); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/Translatable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/Translatable.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.Translatable; + +import dwt.dwthelper.utils; + +/** + * A translatable object can be translated (or moved) vertically and/or horizontally. + */ +public interface Translatable { + +/** + * Translates this object horizontally by dx and vertically by + * dy. + * + * @param dx The amount to translate horizontally + * @param dy The amount to translate vertically + * @since 2.0 + */ +void performTranslate(int dx, int dy); + +/** + * Scales this object by the scale factor. + * + * @param factor The scale factor + * @since 2.0 + */ +void performScale(double factor); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/geometry/Transposer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/geometry/Transposer.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.geometry.Transposer; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; + +/** + * Conditionally transposes geometrical objects based on an "enabled" flag. When + * enabled, the method t(Object) will transpose the passed geometrical object. + * Otherwise, the object will be returned without modification + */ +public class Transposer { + +private bool enabled = false; + +/** + * Disables transposing of inputs. + * + * @since 2.0 + */ +public void disable() { + enabled = false; +} + +/** + * Enables transposing of inputs. + * + * @since 2.0 + */ +public void enable() { + enabled = true; +} + +/** + * Returns true if this Transposer is enabled. + * + * @return true if this Transposer is enabled + * @since 2.0 + */ +public bool isEnabled() { + return enabled; +} + +/** + * Sets the enabled state of this Transposer. + * + * @param e New enabled value + * @since 2.0 + */ +public void setEnabled(bool e) { + enabled = e; +} + +/** + * Returns a new transposed Dimension of the input Dimension. + * + * @param d Input dimension being transposed. + * @return The new transposed dimension. + * @since 2.0 + */ +public Dimension t(Dimension d) { + if (isEnabled()) + return d.getTransposed(); + return d; +} + +/** + * Returns a new transposed Insets of the input Insets. + * + * @param i Insets to be transposed. + * @return The new transposed Insets. + * @since 2.0 + */ +public Insets t(Insets i) { + if (isEnabled()) + return i.getTransposed(); + return i; +} + +/** + * Returns a new transposed Point of the input Point. + * + * @param p Point to be transposed. + * @return The new transposed Point. + * @since 2.0 + */ +public Point t(Point p) { + if (isEnabled()) + return p.getTransposed(); + return p; +} + +/** + * Returns a new transposed Rectangle of the input Rectangle. + * + * @param r Rectangle to be transposed. + * @return The new trasnposed Rectangle. + * @since 2.0 + */ +public Rectangle t(Rectangle r) { + if (isEnabled()) + return r.getTransposed(); + return r; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/BreakCycles.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/BreakCycles.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,280 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.BreakCycles; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Edge; + +/** + * This visitor eliminates cycles in the graph using a "greedy" heuristic. Nodes which + * are sources and sinks are marked and placed in a source and sink list, leaving only + * nodes involved in cycles. A remaining node with the highest (outgoing-incoming) edges + * score is then chosen greedily as if it were a source. The process is repeated until all + * nodes have been marked and placed in a list. The lists are then concatenated, and any + * edges which go backwards in this list will be inverted during the layout procedure. + * + * @author Daniel Lee + * @since 2.1.2 + */ +class BreakCycles : GraphVisitor { + +// Used in identifying cycles and in cycle removal. +// Flag field indicates "presence". If true, the node has been removed from the list. +NodeList graphNodes; + +public this(){ + graphNodes = new NodeList(); +} + +private bool allNodesFlagged() { + for (int i = 0; i < graphNodes.size(); i++) { + if (graphNodes.getNode(i).flag is false) + return false; + } + return true; +} + +private void breakCycles(DirectedGraph g) { + initializeDegrees(g); + greedyCycleRemove(g); + invertEdges(g); +} + +/* + * Returns true if g contains cycles, false otherwise + */ +private bool containsCycles(DirectedGraph g) { + List noLefts = new ArrayList(); + //Identify all initial nodes for removal + for (int i = 0; i < graphNodes.size(); i++) { + Node node = graphNodes.getNode(i); + if (getIncomingCount(node) is 0) + sortedInsert(noLefts, node); + } + + while (noLefts.size() > 0) { + Node node = cast(Node)noLefts.remove(noLefts.size() - 1); + node.flag = true; + for (int i = 0; i < node.outgoing.size(); i++) { + Node right = node.outgoing.getEdge(i).target; + setIncomingCount(right, getIncomingCount(right) - 1); + if (getIncomingCount(right) is 0) + sortedInsert(noLefts, right); + } + } + + if (allNodesFlagged()) + return false; + return true; +} + +/* + * Returns the node in graphNodes with the largest + * (outgoing edge count - incoming edge count) value + */ +private Node findNodeWithMaxDegree() { + int max = Integer.MIN_VALUE; + Node maxNode = null; + + for (int i = 0; i < graphNodes.size(); i++) { + Node node = graphNodes.getNode(i); + if (getDegree(node) >= max && node.flag is false) { + max = getDegree(node); + maxNode = node; + } + } + return maxNode; +} + +private int getDegree(Node n) { + return n.workingInts[3]; +} + +private int getIncomingCount(Node n) { + return n.workingInts[0]; +} + +private int getInDegree(Node n) { + return n.workingInts[1]; +} + +private int getOrderIndex(Node n) { + return n.workingInts[0]; +} + +private int getOutDegree(Node n) { + return n.workingInts[2]; +} + +private void greedyCycleRemove(DirectedGraph g) { + NodeList sL = new NodeList(); + NodeList sR = new NodeList(); + + do { + // Add all sinks and isolated nodes to sR + bool hasSink; + do { + hasSink = false; + for (int i = 0; i < graphNodes.size(); i++) { + Node node = graphNodes.getNode(i); + if (getOutDegree(node) is 0 && node.flag is false) { + hasSink = true; + node.flag = true; + updateIncoming(node); + sR.add(node); + break; + } + } + } while (hasSink); + + // Add all sources to sL + bool hasSource; + do { + hasSource = false; + for (int i = 0; i < graphNodes.size(); i++) { + Node node = graphNodes.getNode(i); + if (getInDegree(node) is 0 && node.flag is false) { + hasSource = true; + node.flag = true; + updateOutgoing(node); + sL.add(node); + break; + } + } + } while (hasSource); + + // When all sinks and sources are removed, choose a node with the + // maximum degree (outDegree - inDegree) and add it to sL + Node max = findNodeWithMaxDegree(); + if (max !is null) { + sL.add(max); + max.flag = true; + updateIncoming(max); + updateOutgoing(max); + } + } while (!allNodesFlagged()); + + // Assign order indexes + int orderIndex = 0; + for (int i = 0; i < sL.size(); i++) { + setOrderIndex(sL.getNode(i), orderIndex++); + } + for (int i = sR.size() - 1; i >= 0; i--) { + setOrderIndex(sR.getNode(i), orderIndex++); + } +} + +private void initializeDegrees(DirectedGraph g) { + graphNodes.resetFlags(); + for (int i = 0; i < g.nodes.size(); i++) { + Node n = graphNodes.getNode(i); + setInDegree(n, n.incoming.size()); + setOutDegree(n, n.outgoing.size()); + setDegree(n, n.outgoing.size() - n.incoming.size()); + } +} + +private void invertEdges(DirectedGraph g) { + for (int i = 0; i < g.edges.size(); i++) { + Edge e = g.edges.getEdge(i); + if (getOrderIndex(e.source) > getOrderIndex(e.target)) { + e.invert(); + e.isFeedback_ = true; + } + } +} + +private void setDegree(Node n, int deg) { + n.workingInts[3] = deg; +} + +private void setIncomingCount(Node n, int count) { + n.workingInts[0] = count; +} + +private void setInDegree(Node n, int deg) { + n.workingInts[1] = deg; +} + +private void setOutDegree(Node n, int deg) { + n.workingInts[2] = deg; +} + +private void setOrderIndex(Node n, int index) { + n.workingInts[0] = index; +} + +private void sortedInsert(List list, Node node) { + int insert = 0; + while (insert < list.size() + && (cast(Node)list.get(insert)).sortValue > node.sortValue) + insert++; + list.add(insert, node); +} + +/* + * Called after removal of n. Updates the degree values of n's incoming nodes. + */ +private void updateIncoming(Node n) { + for (int i = 0; i < n.incoming.size(); i++) { + Node in_ = n.incoming.getEdge(i).source; + if (in_.flag is false) { + setOutDegree(in_, getOutDegree(in_) - 1); + setDegree(in_, getOutDegree(in_) - getInDegree(in_)); + } + } +} + +/* + * Called after removal of n. Updates the degree values of n's outgoing nodes. + */ +private void updateOutgoing(Node n) { + for (int i = 0; i < n.outgoing.size(); i++) { + Node out_ = n.outgoing.getEdge(i).target; + if (out_.flag is false) { + setInDegree(out_, getInDegree(out_) - 1); + setDegree(out_, getOutDegree(out_) - getInDegree(out_)); + } + } +} + +public void revisit(DirectedGraph g) { + for (int i = 0; i < g.edges.size(); i++) { + Edge e = g.edges.getEdge(i); + if (e.isFeedback()) + e.invert(); + } +} + +/** + * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph) + */ +public void visit(DirectedGraph g) { + // put all nodes in list, initialize index + graphNodes.resetFlags(); + for (int i = 0; i < g.nodes.size(); i++) { + Node n = g.nodes.getNode(i); + setIncomingCount(n, n.incoming.size()); + graphNodes.add(n); + } + if (containsCycles(g)) { + breakCycles(g); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/Cell.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/Cell.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.graph.Cell; + +import dwt.dwthelper.utils; + +public class Cell { + +public int index; + +public int rank; + +public this(int rank, int index){ + this.rank = rank; + this.index = index; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/CollapsedEdges.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/CollapsedEdges.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.CollapsedEdges; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.Edge; + +/** + * Contains the information from all edges going from a given cluster to some other + * cluster. An edge with minimal slack as chosen to maintain the link between clusters. + * The weight and any slack more than the minimal edge's slack is tracked for all other + * edges. + * @since 3.1 + */ +class CollapsedEdges { + + /** + * The total weight of the collapsed edges. + */ + int collapsedWeight; + int collapsedCount; + + /** + * The total amount of weighted difference in the collapsed edges slack and the + * tightest edge's slack. + */ + int overage; + int unOverage; + Edge tightestEdge; + + this(Edge edge) { + tightestEdge = edge; + collapsedWeight = edge.weight; + collapsedCount++; + } + + public int getWeightedPull() { + return tightestEdge.getSlack() * collapsedWeight + overage; + } + + public bool isTight() { + return tightestEdge.getSlack() is 0; + } + + /** + * Compares the given edge to the current tightest edge. If the given edge is tighter + * than the current, the current tightest is returned. Otherwise, the edge itself is + * returned. The returned edge would be the one to remove from the graph. + * @param candidate another edge + * @return the edge which is not the tightest edge + * @since 3.1 + */ + Edge processEdge(Edge candidate) { + collapsedCount++; + if (candidate.getSlack() < tightestEdge.getSlack()) { + overage += collapsedWeight * (tightestEdge.getSlack() - candidate.getSlack()); + Edge temp = tightestEdge; + tightestEdge = candidate; + collapsedWeight += candidate.weight; + return temp; + } else { + int over = candidate.getSlack() - tightestEdge.getSlack(); + unOverage += over; + overage += candidate.weight * over; + collapsedWeight += candidate.weight; + return candidate; + } + } +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/CompoundBreakCycles.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/CompoundBreakCycles.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,481 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.CompoundBreakCycles; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.Subgraph; +import dwtx.draw2d.graph.Edge; + + +/** + * This visitor eliminates cycles in the graph via a modified implementation of the + * greedy cycle removal algorithm for directed graphs. The algorithm has been modified to + * handle the presence of Subgraphs and compound cycles which may result. This algorithm + * determines a set of edges which can be inverted and result in a graph without compound + * cycles. + * + * @author Daniel Lee + * @author Randy Hudson + * @since 2.1.2 + */ +class CompoundBreakCycles : GraphVisitor { + +/* + * Caches all nodes in the graph. Used in identifying cycles and in cycle removal. + * Flag field indicates "presence". If true, the node has been removed from the list. + */ +private NodeList graphNodes; +private NodeList sL; + +public this(){ + sL = new NodeList(); +} + +private bool allFlagged(NodeList nodes) { + for (int i = 0; i < nodes.size(); i++) { + if (nodes.getNode(i).flag is false) + return false; + } + return true; +} + +private int buildNestingTreeIndices(NodeList nodes, int base) { + for (int i = 0; i < nodes.size(); i++) { + Node node = cast(Node)nodes.get(i); + if (auto s = cast(Subgraph) node ) { + s.nestingTreeMin = base; + base = buildNestingTreeIndices(s.members, base); + } + node.nestingIndex = base++; + } + return base++; +} + +private bool canBeRemoved(Node n) { + return !n.flag && getChildCount(n) is 0; +} + +private bool changeInDegree(Node n, int delta) { + return (n.workingInts[1] += delta) is 0; +} + +private bool changeOutDegree(Node n, int delta) { + return (n.workingInts[2] += delta) is 0; +} + +/* + * Execution of the modified greedy cycle removal algorithm. + */ +private void cycleRemove(NodeList children) { + NodeList sR = new NodeList(); + do { + findSinks(children, sR); + findSources(children); + + // all sinks and sources added, find node with highest + // outDegree - inDegree + Node max = findNodeWithMaxDegree(children); + if (max !is null) { + for (int i = 0; i < children.size(); i++) { + Node child = cast(Node)children.get(i); + if (child.flag) + continue; + if (child is max) + restoreSinks(max, sR); + else + restoreSources(child); + } + remove(max); + } + } while (!allFlagged(children)); + while (!sR.isEmpty()) + sL.add(sR.remove(sR.size() - 1)); +} + +private void findInitialSinks(NodeList children, NodeList sinks) { + for (int i = 0; i < children.size(); i++) { + Node node = children.getNode(i); + if (node.flag) + continue; + if (isSink(node) && canBeRemoved(node)) { + sinks.add(node); + node.flag = true; + } + if (null !is cast(Subgraph)node ) + findInitialSinks((cast(Subgraph)node).members, sinks); + } +} + +private void findInitialSources(NodeList children, NodeList sources) { + for (int i = 0; i < children.size(); i++) { + Node node = children.getNode(i); + if (isSource(node) && canBeRemoved(node)) { + sources.add(node); + node.flag = true; + } + if (null !is cast(Subgraph)node ) + findInitialSources((cast(Subgraph)node).members, sources); + } +} + +private Node findNodeWithMaxDegree(NodeList nodes) { + int max = Integer.MIN_VALUE; + Node maxNode = null; + + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.getNode(i); + if (node.flag) + continue; + int degree = getNestedOutDegree(node) - getNestedInDegree(node); + if (degree >= max && node.flag is false) { + max = degree; + maxNode = node; + } + } + return maxNode; +} + +/* + * Finds all sinks in graphNodes and adds them to the passed NodeList + */ +private void findSinks(NodeList children, NodeList rightList) { +// NodeList rightList = new NodeList(); + NodeList sinks = new NodeList(); + findInitialSinks(children, sinks); + while (!sinks.isEmpty()) { + Node sink = sinks.getNode(sinks.size() - 1); + rightList.add(sink); + sinks.remove(sink); + removeSink(sink, sinks); + + // Check to see if the removal has made the parent node a sink + if (sink.getParent() !is null) { + Node parent = sink.getParent(); + setChildCount(parent, getChildCount(parent) - 1); + if (isSink(parent) && canBeRemoved(parent)) { + sinks.add(parent); + parent.flag = true; + } + } + } +} + +/* + * Finds all sources in graphNodes and adds them to the sL NodeList. + */ +private void findSources(NodeList children) { + NodeList sources = new NodeList(); + findInitialSources(children, sources); + while (!sources.isEmpty()) { + Node source = sources.getNode(sources.size() - 1); + sL.add(source); + sources.remove(source); + removeSource(source, sources); + + // Check to see if the removal has made the parent node a source + if (source.getParent() !is null) { + Node parent = source.getParent(); + setChildCount(parent, getChildCount(parent) - 1); + if (isSource(parent) && canBeRemoved(parent)) { + sources.add(parent); + parent.flag = true; + } + } + } +} + +private int getChildCount(Node n) { + return n.workingInts[3]; +} + +private int getInDegree(Node n) { + return n.workingInts[1]; +} + +private int getNestedInDegree(Node n) { + int result = getInDegree(n); + if ( auto s = cast(Subgraph)n ) { + for (int i = 0; i < s.members.size(); i++) + if (!s.members.getNode(i).flag) + result += getInDegree(s.members.getNode(i)); + } + return result; +} + +private int getNestedOutDegree(Node n) { + int result = getOutDegree(n); + if ( auto s = cast(Subgraph)n ) { + for (int i = 0; i < s.members.size(); i++) + if (!s.members.getNode(i).flag) + result += getOutDegree(s.members.getNode(i)); + } + return result; +} + +private int getOrderIndex(Node n) { + return n.workingInts[0]; +} + +private int getOutDegree(Node n) { + return n.workingInts[2]; +} + +private void initializeDegrees(DirectedGraph g) { + g.nodes.resetFlags(); + g.edges.resetFlags(false); + for (int i = 0; i < g.nodes.size(); i++) { + Node n = g.nodes.getNode(i); + setInDegree(n, n.incoming.size()); + setOutDegree(n, n.outgoing.size()); + if ( auto s = cast(Subgraph)n ) + setChildCount(n, s.members.size()); + else + setChildCount(n, 0); + } +} + +private void invertEdges(DirectedGraph g) { + // Assign order indices + int orderIndex = 0; + for (int i = 0; i < sL.size(); i++) { + setOrderIndex(sL.getNode(i), orderIndex++); + } + // Invert edges that are causing a cycle + for (int i = 0; i < g.edges.size(); i++) { + Edge e = g.edges.getEdge(i); + if (getOrderIndex(e.source) > getOrderIndex(e.target) + && !e.source.isNested(e.target) + && !e.target.isNested(e.source)) { + e.invert(); + e.isFeedback_ = true; + } + } +} + +/** + * Removes all edges connecting the given subgraph to other nodes outside of it. + * @param s + * @param n + */ +private void isolateSubgraph(Subgraph subgraph, Node member) { + Edge edge = null; + for (int i = 0; i < member.incoming.size(); i++) { + edge = member.incoming.getEdge(i); + if (!subgraph.isNested(edge.source) && !edge.flag) + removeEdge(edge); + } + for (int i = 0; i < member.outgoing.size(); i++) { + edge = member.outgoing.getEdge(i); + if (!subgraph.isNested(edge.target) && !edge.flag) + removeEdge(edge); + } + if ( auto s = cast(Subgraph)member ) { + NodeList members = s.members; + for (int i = 0; i < members.size(); i++) + isolateSubgraph(subgraph, members.getNode(i)); + } +} + +private bool isSink(Node n) { + return getOutDegree(n) is 0 + && (n.getParent() is null + || isSink(n.getParent())); +} + +private bool isSource(Node n) { + return getInDegree(n) is 0 + && (n.getParent() is null + || isSource(n.getParent())); +} + +private void remove(Node n) { + n.flag = true; + if (n.getParent() !is null) + setChildCount(n.getParent(), getChildCount(n.getParent()) - 1); + removeSink(n, null); + removeSource(n, null); + sL.add(n); + if ( auto s = cast(Subgraph)n ) { + isolateSubgraph(s, s); + cycleRemove(s.members); + } +} + +private bool removeEdge(Edge e) { + if (e.flag) + return false; + e.flag = true; + changeOutDegree(e.source, -1); + changeInDegree(e.target, -1); + return true; +} + +/** + * Removes all edges between a parent and any of its children or descendants. + */ +private void removeParentChildEdges(DirectedGraph g) { + for (int i = 0; i < g.edges.size(); i++) { + Edge e = g.edges.getEdge(i); + if (e.source.isNested(e.target) || e.target.isNested(e.source)) + removeEdge(e); + } +} + +private void removeSink(Node sink, NodeList allSinks) { + for (int i = 0; i < sink.incoming.size(); i++) { + Edge e = sink.incoming.getEdge(i); + if (!e.flag) { + removeEdge(e); + Node source = e.source; + if (allSinks !is null && isSink(source) && canBeRemoved(source)) { + allSinks.add(source); + source.flag = true; + } + } + } +} + +private void removeSource(Node n, NodeList allSources) { + for (int i = 0; i < n.outgoing.size(); i++) { + Edge e = n.outgoing.getEdge(i); + if (!e.flag) { + e.flag = true; + changeInDegree(e.target, -1); + changeOutDegree(e.source, -1); + + Node target = e.target; + if (allSources !is null && isSource(target) && canBeRemoved(target)) { + allSources.add(target); + target.flag = true; + } + } + } +} + +/** + * Restores an edge if it has been removed, and both of its nodes are not removed. + * @param e the edge + * @return true if the edge was restored + */ +private bool restoreEdge(Edge e) { + if (!e.flag || e.source.flag || e.target.flag) + return false; + e.flag = false; + changeOutDegree(e.source, 1); + changeInDegree(e.target, 1); + return true; +} + +/** + * Brings back all nodes nested in the given node. + * @param node the node to restore + * @param sr current sinks + */ +private void restoreSinks(Node node, NodeList sR) { + if (node.flag && sR.contains(node)) { + node.flag = false; + if (node.getParent() !is null) + setChildCount(node.getParent(), getChildCount(node.getParent()) + 1); + sR.remove(node); + for (int i = 0; i < node.incoming.size(); i++) { + Edge e = node.incoming.getEdge(i); + restoreEdge(e); + } + for (int i = 0; i < node.outgoing.size(); i++) { + Edge e = node.outgoing.getEdge(i); + restoreEdge(e); + } + } + if ( auto s = cast(Subgraph)node ) { + for (int i = 0; i < s.members.size(); i++) { + Node member = s.members.getNode(i); + restoreSinks(member, sR); + } + } +} + +/** + * Brings back all nodes nested in the given node. + * @param node the node to restore + * @param sr current sinks + */ +private void restoreSources(Node node) { + if (node.flag && sL.contains(node)) { + node.flag = false; + if (node.getParent() !is null) + setChildCount(node.getParent(), getChildCount(node.getParent()) + 1); + sL.remove(node); + for (int i = 0; i < node.incoming.size(); i++) { + Edge e = node.incoming.getEdge(i); + restoreEdge(e); + } + for (int i = 0; i < node.outgoing.size(); i++) { + Edge e = node.outgoing.getEdge(i); + restoreEdge(e); + } + } + if ( auto s = cast(Subgraph)node ) { + for (int i = 0; i < s.members.size(); i++) { + Node member = s.members.getNode(i); + restoreSources(member); + } + } +} + +public void revisit(DirectedGraph g) { + for (int i = 0; i < g.edges.size(); i++) { + Edge e = g.edges.getEdge(i); + if (e.isFeedback()) + e.invert(); + } +} + +private void setChildCount(Node n, int count) { + n.workingInts[3] = count; +} + +private void setInDegree(Node n, int deg) { + n.workingInts[1] = deg; +} + +private void setOrderIndex(Node n, int index) { + n.workingInts[0] = index; +} + +private void setOutDegree(Node n, int deg) { + n.workingInts[2] = deg; +} + +/** + * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph) + */ +public void visit(DirectedGraph g) { + initializeDegrees(g); + graphNodes = g.nodes; + + NodeList roots = new NodeList(); + for (int i = 0; i < graphNodes.size(); i++) { + if (graphNodes.getNode(i).getParent() is null) + roots.add(graphNodes.getNode(i)); + } + buildNestingTreeIndices(roots, 0); + removeParentChildEdges(g); + cycleRemove(roots); + invertEdges(g); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/CompoundDirectedGraph.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/CompoundDirectedGraph.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.CompoundDirectedGraph; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.EdgeList; + +/** + * A DirectedGraph whose Nodes may be compound {@link Subgraph}s, which may + * contain other nodes. Any node in the graph may be parented by one subgraph. Since + * subgraphs are nodes, the source or target end of an {@link Edge} may be a subgraph. + * For additional restrictions, refer to the JavaDoc for the layout algorithm being used. + *

+ * A CompoundDirectedGraph is passed to a graph layout, which will position all of the + * nodes, subgraphs, and edges in that graph. This class serves as the data structure for + * a layout algorithm. + * + * @author Randy Hudson + * @since 2.1.2 + */ +public class CompoundDirectedGraph : DirectedGraph { + +/** + * For internal use only. + */ +public NodeList subgraphs; + +/** + * For internal use only. + */ +public EdgeList containment; + +public this(){ + subgraphs = new NodeList(); + containment = new EdgeList(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/CompoundDirectedGraphLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/CompoundDirectedGraphLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2003, 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 + *******************************************************************************/ +module dwtx.draw2d.graph.CompoundDirectedGraphLayout; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.DirectedGraphLayout; +import dwtx.draw2d.graph.TransposeMetrics; +import dwtx.draw2d.graph.CompoundBreakCycles; +import dwtx.draw2d.graph.RouteEdges; +import dwtx.draw2d.graph.ConvertCompoundGraph; +import dwtx.draw2d.graph.InitialRankSolver; +import dwtx.draw2d.graph.TightSpanningTreeSolver; +import dwtx.draw2d.graph.RankAssignmentSolver; +import dwtx.draw2d.graph.CompoundPopulateRanks; +import dwtx.draw2d.graph.CompoundVerticalPlacement; +import dwtx.draw2d.graph.MinCross; +import dwtx.draw2d.graph.CompoundRankSorter; +import dwtx.draw2d.graph.SortSubgraphs; +import dwtx.draw2d.graph.CompoundHorizontalPlacement; + +/** + * Performs a graph layout on a CompoundDirectedGraph. The input format is + * the same as for {@link DirectedGraphLayout}. All nodes, including subgraphs and their + * children, should be added to the {@link DirectedGraph#nodes} field. + *

+ * The requirements for this algorithm are the same as those of + * DirectedGraphLayout, with the following exceptions: + *

    + *
  • There is an implied edge between a subgraph and each of its member nodes. These + * edges form the containment graph T. Thus, the compound directed graph + * CG is said to be connected iff Union(G, T) is connected, + * where G represents the given nodes (including subgraphs) and edges. + * + *
  • This algorithm will remove any compound cycles found in the input graph + * G by inverting edges according to a heuristic until no more cycles are + * found. A compound cycle is defined as: a cycle comprised of edges from G, + * T, and T-1, in the form + * (c*e+p*e+)*, where + * T-1 is the backwards graph of T, c element of T, e + * element of G, and p element of T-1. + *
+ * + * @author Randy Hudson + * @since 2.1.2 + */ +public final class CompoundDirectedGraphLayout : DirectedGraphLayout { + +void init() { + steps.add(new TransposeMetrics()); + steps.add(new CompoundBreakCycles()); + steps.add(new RouteEdges()); + steps.add(new ConvertCompoundGraph()); + steps.add(new InitialRankSolver()); + steps.add(new TightSpanningTreeSolver()); + steps.add(new RankAssignmentSolver()); + steps.add(new CompoundPopulateRanks()); + steps.add(new CompoundVerticalPlacement()); + steps.add(new MinCross(new CompoundRankSorter())); + steps.add(new SortSubgraphs()); + steps.add(new CompoundHorizontalPlacement()); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/CompoundHorizontalPlacement.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/CompoundHorizontalPlacement.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2003, 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 + *******************************************************************************/ +module dwtx.draw2d.graph.CompoundHorizontalPlacement; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.HorizontalPlacement; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.RankList; +import dwtx.draw2d.graph.Rank; +import dwtx.draw2d.graph.Subgraph; +import dwtx.draw2d.graph.SubgraphBoundary; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.CompoundDirectedGraph; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.GraphUtilities; + +/** + * Calculates the X-coordinates for nodes in a compound directed graph. + * @author Randy Hudson + * @since 2.1.2 + */ +class CompoundHorizontalPlacement : HorizontalPlacement { + +class LeftRight { + //$TODO Delete and use NodePair class, equivalent + Object left, right; + this(Object l, Object r) { + left = l; right = r; + } + public override int opEquals(Object obj) { + LeftRight entry = cast(LeftRight)obj; + return entry.left.opEquals(left) && entry.right.opEquals(right); + } + public override hash_t toHash() { + return left.toHash() ^ right.toHash(); + } +} + +Set entries; + +public this(){ + entries = new HashSet(); +} + +/** + * @see org.eclipse.graph.HorizontalPlacement#applyGPrime() + */ +void applyGPrime() { + super.applyGPrime(); + NodeList subgraphs = (cast(CompoundDirectedGraph)graph).subgraphs; + for (int i = 0; i < subgraphs.size(); i++) { + Subgraph s = cast(Subgraph)subgraphs.get(i); + s.x = s.left.x; + s.width = s.right.x + s.right.width - s.x; + } +} + +/** + * @see HorizontalPlacement#buildRankSeparators(RankList) + */ +void buildRankSeparators(RankList ranks) { + CompoundDirectedGraph g = cast(CompoundDirectedGraph)graph; + + Rank rank; + for (int row = 0; row < g.ranks.size(); row++) { + rank = g.ranks.getRank(row); + Node n = null, prev = null; + for (int j = 0; j < rank.size(); j++) { + n = rank.getNode(j); + if (prev is null) { + Node left = addSeparatorsLeft(n, null); + if (left !is null) { + Edge e = new Edge(graphLeft, getPrime(left), 0, 0); + prime.edges.add(e); + e.delta = graph.getPadding(n).left + graph.getMargin().left; + } + + } else { + Subgraph s = GraphUtilities.getCommonAncestor(prev, n); + Node left = addSeparatorsRight(prev, s); + Node right = addSeparatorsLeft(n, s); + createEdge(left, right); + } + prev = n; + } + if (n !is null) + addSeparatorsRight(n, null); + } +} + +void createEdge(Node left, Node right) { + LeftRight entry = new LeftRight(left, right); + if (entries.contains(entry)) + return; + entries.add(entry); + int separation = left.width + + graph.getPadding(left).right + + graph.getPadding(right).left; + prime.edges.add(new Edge( + getPrime(left), getPrime(right), separation, 0 + )); +} + +Node addSeparatorsLeft(Node n, Subgraph graph) { + Subgraph parent = n.getParent(); + while (parent !is graph && parent !is null) { + createEdge(getLeft(parent), n); + n = parent.left; + parent = parent.getParent(); + } + return n; +} + +Node addSeparatorsRight(Node n, Subgraph graph) { + Subgraph parent = n.getParent(); + while (parent !is graph && parent !is null) { + createEdge(n, getRight(parent)); + n = parent.right; + parent = parent.getParent(); + } + return n; +} + +Node getLeft(Subgraph s) { + if (s.left is null) { + s.left = new SubgraphBoundary(s, graph.getPadding(s), 1); + s.left.rank = (s.head.rank + s.tail.rank) / 2; + + Node head = getPrime(s.head); + Node tail = getPrime(s.tail); + Node left = getPrime(s.left); + Node right = getPrime(getRight(s)); + prime.edges.add(new Edge(left, right, s.width, 0)); + prime.edges.add(new Edge(left, head, 0, 1)); + prime.edges.add(new Edge(head, right, 0, 1)); + prime.edges.add(new Edge(left, tail, 0, 1)); + prime.edges.add(new Edge(tail, right, 0, 1)); + } + return s.left; +} + +Node getRight(Subgraph s) { + if (s.right is null) { + s.right = new SubgraphBoundary(s, graph.getPadding(s), 3); + s.right.rank = (s.head.rank + s.tail.rank) / 2; + } + return s.right; +} + +Node getPrime(Node n) { + Node nPrime = get(n); + if (nPrime is null) { + nPrime = new Node(n); + prime.nodes.add(nPrime); + map(n, nPrime); + } + return nPrime; +} + +public void visit(DirectedGraph g) { + super.visit(g); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/CompoundPopulateRanks.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/CompoundPopulateRanks.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.CompoundPopulateRanks; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.PopulateRanks; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.Subgraph; +import dwtx.draw2d.graph.CompoundDirectedGraph; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.Node; + +/** + * Places nodes into ranks for a compound directed graph. If a subgraph spans a rank + * without any nodes which belong to that rank, a bridge node is inserted to prevent nodes + * from violating the subgraph boundary. + * @author Randy Hudson + * @since 2.1.2 + */ +class CompoundPopulateRanks : PopulateRanks { + +public void visit(DirectedGraph g) { + CompoundDirectedGraph graph = cast(CompoundDirectedGraph)g; + + /** + * Remove long containment edges at this point so they don't affect MinCross. + */ + Iterator containment = graph.containment.iterator(); + while (containment.hasNext()) { + Edge e = cast(Edge)containment.next(); + if (e.getSlack() > 0) { + graph.removeEdge(e); + containment.remove(); + } + } + + super.visit(g); + NodeList subgraphs = graph.subgraphs; + for (int i = 0; i < subgraphs.size(); i++) { + Subgraph subgraph = cast(Subgraph)subgraphs.get(i); + bridgeSubgraph(subgraph, graph); + } +} + +/** + * @param subgraph + */ +private void bridgeSubgraph(Subgraph subgraph, CompoundDirectedGraph g) { + int offset = subgraph.head.rank; + bool occupied[] = new bool[subgraph.tail.rank - subgraph.head.rank + 1]; + Node bridge[] = new Node[occupied.length]; + + for (int i = 0; i < subgraph.members.size(); i++) { + Node n = cast(Node)subgraph.members.get(i); + if (auto s = cast(Subgraph)n ) { + for (int r = s.head.rank; r <= s.tail.rank; r++) + occupied[r - offset] = true; + } else + occupied[n.rank - offset] = true; + } + + for (int i = 0; i < bridge.length; i++) { + if (!occupied[i]) { + Node br = bridge[i] = new Node(stringcast("bridge"), subgraph); //$NON-NLS-1$ + br.rank = i + offset; + br.height = br.width = 0; + br.nestingIndex = subgraph.nestingIndex; + g.ranks.getRank(br.rank).add(br); + g.nodes.add(br); + } + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/CompoundRankSorter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/CompoundRankSorter.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,238 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.CompoundRankSorter; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.RankSorter; +import dwtx.draw2d.graph.Subgraph; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.Rank; +import dwtx.draw2d.graph.NestingTree; +import dwtx.draw2d.graph.CompoundDirectedGraph; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.LocalOptimizer; + +/** + * Sorts nodes in a compound directed graph. + * @author Randy Hudson + * @since 2.1.2 + */ +class CompoundRankSorter : RankSorter { + +static class RowEntry { + double contribution; + int count; + void reset() { + count = 0; + contribution = 0; + } +} + +static class RowKey { + int rank; + Subgraph s; + this() { } + this(Subgraph s, int rank) { + this.s = s; + this.rank = rank; + } + + public override int opEquals(Object obj) { + RowKey rp = cast(RowKey)obj; + return rp.s is s && rp.rank is rank; + } + + public override hash_t toHash() { + return s.toHash() ^ (rank * 31); + } +} + +bool init_; +RowKey key; + +Map map; + +public this(){ + key = new RowKey(); + map = new HashMap(); +} + +void addRowEntry(Subgraph s, int row) { + key.s = s; + key.rank = row; + if (!map.containsKey(key)) + map.put(new RowKey(s, row), new RowEntry()); +} + +protected void assignIncomingSortValues() { + super.assignIncomingSortValues(); + pullTogetherSubgraphs(); +} + +protected void assignOutgoingSortValues() { + super.assignOutgoingSortValues(); + pullTogetherSubgraphs(); +} + +void optimize(DirectedGraph g) { + CompoundDirectedGraph graph = cast(CompoundDirectedGraph)g; + Iterator containment = graph.containment.iterator(); + while (containment.hasNext()) + graph.removeEdge(cast(Edge)containment.next()); + graph.containment.clear(); + (new LocalOptimizer()) + .visit(graph); +} + +private void pullTogetherSubgraphs() { + if (true) + return; + for (int j = 0; j < rank.count(); j++) { + Node n = rank.getNode(j); + Subgraph s = n.getParent(); + while (s !is null) { + getRowEntry(s, currentRow).reset(); + s = s.getParent(); + } + } + for (int j = 0; j < rank.count(); j++) { + Node n = rank.getNode(j); + Subgraph s = n.getParent(); + while (s !is null) { + RowEntry entry = getRowEntry(s, currentRow); + entry.count++; + entry.contribution += n.sortValue; + s = s.getParent(); + } + } + + double weight = 0.5;// * (1.0 - progress) * 3; + + for (int j = 0; j < rank.count(); j++) { + Node n = rank.getNode(j); + Subgraph s = n.getParent(); + if (s !is null) { + RowEntry entry = getRowEntry(s, currentRow); + n.sortValue = + n.sortValue * (1.0 - weight) + weight * entry.contribution / entry.count; + } + } +} + +double evaluateNodeOutgoing() { + double result = super.evaluateNodeOutgoing(); +// result += Math.random() * rankSize * (1.0 - progress) / 3.0; + if (progress > 0.2) { + Subgraph s = node.getParent(); + double connectivity = mergeConnectivity(s, node.rank + 1, result, progress); + result = connectivity; + } + return result; +} + +double evaluateNodeIncoming() { + double result = super.evaluateNodeIncoming(); +// result += Math.random() * rankSize * (1.0 - progress) / 3.0; + if (progress > 0.2) { + Subgraph s = node.getParent(); + double connectivity = mergeConnectivity(s, node.rank - 1, result, progress); + result = connectivity; + } + return result; +} + +double mergeConnectivity(Subgraph s, int row, double result, double scaleFactor) { + while (s !is null && getRowEntry(s, row) is null) + s = s.getParent(); + if (s !is null) { + RowEntry entry = getRowEntry(s, row); + double connectivity = entry.contribution / entry.count; + result = connectivity * 0.3 + (0.7) * result; + s = s.getParent(); + } + return result; +} + +RowEntry getRowEntry(Subgraph s, int row) { + key.s = s; + key.rank = row; + return cast(RowEntry)map.get(key); +} + +void copyConstraints(NestingTree tree) { + if (tree.subgraph !is null) + tree.sortValue = tree.subgraph.rowOrder; + for (int i = 0; i < tree.contents.size(); i++) { + Object child = tree.contents.get(i); + if (auto n = cast(Node)child ) { + n.sortValue = n.rowOrder; + } else { + copyConstraints(cast(NestingTree)child); + } + } +} + +public void init(DirectedGraph g) { + super.init(g); + init_ = true; + + for (int row = 0; row < g.ranks.size(); row++) { + Rank rank = g.ranks.getRank(row); + + NestingTree tree = NestingTree.buildNestingTreeForRank(rank); + copyConstraints(tree); + tree.recursiveSort(true); + rank.clear(); + tree.repopulateRank(rank); + + for (int j = 0; j < rank.count(); j++) { + Node n = rank.getNode(j); + Subgraph s = n.getParent(); + while (s !is null) { + addRowEntry(s, row); + s = s.getParent(); + } + } + } +} + +protected void postSort() { + super.postSort(); + if (init_) + updateRank(rank); +} + +void updateRank(Rank rank) { + for (int j = 0; j < rank.count(); j++) { + Node n = rank.getNode(j); + Subgraph s = n.getParent(); + while (s !is null) { + getRowEntry(s, currentRow).reset(); + s = s.getParent(); + } + } + for (int j = 0; j < rank.count(); j++) { + Node n = rank.getNode(j); + Subgraph s = n.getParent(); + while (s !is null) { + RowEntry entry = getRowEntry(s, currentRow); + entry.count++; + entry.contribution += n.index; + s = s.getParent(); + } + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/CompoundVerticalPlacement.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/CompoundVerticalPlacement.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.CompoundVerticalPlacement; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.VerticalPlacement; +import dwtx.draw2d.graph.CompoundDirectedGraph; +import dwtx.draw2d.graph.Subgraph; + +/** + * calculates the height and y-coordinates for nodes and subgraphs in a compound directed + * graph. + * @author Randy Hudson + * @since 2.1.2 + */ +class CompoundVerticalPlacement : VerticalPlacement { + +/** + * @see GraphVisitor#visit(DirectedGraph) + * Extended to set subgraph values. + */ +void visit(DirectedGraph dg) { + CompoundDirectedGraph g = cast(CompoundDirectedGraph)dg; + super.visit(g); + for (int i = 0; i < g.subgraphs.size(); i++) { + Subgraph s = cast(Subgraph)g.subgraphs.get(i); + s.y = s.head.y; + s.height = s.tail.height + s.tail.y - s.y; + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/ConvertCompoundGraph.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/ConvertCompoundGraph.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.ConvertCompoundGraph; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Subgraph; +import dwtx.draw2d.graph.SubgraphBoundary; +import dwtx.draw2d.graph.CompoundDirectedGraph; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.Edge; + +/** + * Converts a compound directed graph into a simple directed graph. + * @author Randy Hudson + * @since 2.1.2 + */ +class ConvertCompoundGraph : GraphVisitor { + +private void addContainmentEdges(CompoundDirectedGraph graph) { + //For all nested nodes, connect to head and/or tail of containing subgraph if present + for (int i = 0; i < graph.nodes.size(); i++) { + Node node = graph.nodes.getNode(i); + Subgraph parent = node.getParent(); + if (parent is null) + continue; + if (auto sub = cast(Subgraph)node ) { + connectHead(graph, sub.head, parent); + connectTail(graph, sub.tail, parent); + } else { + connectHead(graph, node, parent); + connectTail(graph, node, parent); + } + } +} + +int buildNestingTreeIndices(NodeList nodes, int base) { + for (int i = 0; i < nodes.size(); i++) { + Node node = cast(Node)nodes.get(i); + if (auto s = cast(Subgraph)node ) { + s.nestingTreeMin = base; + base = buildNestingTreeIndices(s.members, base); + } + node.nestingIndex = base++; + } + return base++; +} + +private void connectHead(CompoundDirectedGraph graph, Node node, Subgraph parent) { + bool connectHead = true; + for (int j = 0; connectHead && j < node.incoming.size(); j++) { + Node ancestor = node.incoming.getEdge(j).source; + if (parent.isNested(ancestor)) + connectHead = false; + } + if (connectHead) { + Edge e = new Edge(parent.head, node); + e.weight = 0; + graph.edges.add(e); + graph.containment.add(e); + } +} + +private void connectTail(CompoundDirectedGraph graph, Node node, Subgraph parent) { + bool connectTail = true; + for (int j = 0; connectTail && j < node.outgoing.size(); j++) { + Node ancestor = node.outgoing.getEdge(j).target; + if (parent.isNested(ancestor)) + connectTail = false; + } + if (connectTail) { + Edge e = new Edge(node, parent.tail); + e.weight = 0; + graph.edges.add(e); + graph.containment.add(e); + } +} + +private void convertSubgraphEndpoints(CompoundDirectedGraph graph) { + for (int i = 0; i < graph.edges.size(); i++) { + Edge edge = cast(Edge)graph.edges.get(i); + if (auto s = cast(Subgraph)edge.source ) { + Node newSource; + if (s.isNested(edge.target)) + newSource = s.head; + else + newSource = s.tail; + //s.outgoing.remove(edge); + edge.source = newSource; + newSource.outgoing.add(edge); + } + if (auto s = cast(Subgraph)edge.target ) { + Node newTarget; + if (s.isNested(edge.source)) + newTarget = s.tail; + else + newTarget = s.head; + + //s.incoming.remove(edge); + edge.target = newTarget; + newTarget.incoming.add(edge); + } + } +} + +private void replaceSubgraphsWithBoundaries(CompoundDirectedGraph graph) { + for (int i = 0; i < graph.subgraphs.size(); i++) { + Subgraph s = cast(Subgraph)graph.subgraphs.get(i); + graph.nodes.add(s.head); + graph.nodes.add(s.tail); + graph.nodes.remove(s); + } +} + +void revisit(DirectedGraph g) { + for (int i = 0; i < g.edges.size(); i++) { + Edge e = g.edges.getEdge(i); + if (null !is cast(SubgraphBoundary)e.source ) { + e.source.outgoing.remove(e); + e.source = e.source.getParent(); + } + if (null !is cast(SubgraphBoundary)e.target ) { + e.target.incoming.remove(e); + e.target = e.target.getParent(); + } + } +} + +/** + * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph) + */ +public void visit(DirectedGraph dg) { + CompoundDirectedGraph graph = cast(CompoundDirectedGraph)dg; + + NodeList roots = new NodeList(); + //Find all subgraphs and root subgraphs + for (int i = 0; i < graph.nodes.size(); i++) { + Object node = graph.nodes.get(i); + if (auto s = cast(Subgraph)node ) { + Insets padding = dg.getPadding(s); + s.head = new SubgraphBoundary(s, padding, 0); + s.tail = new SubgraphBoundary(s, padding, 2); + Edge headToTail = new Edge(s.head, s.tail); + headToTail.weight = 10; + graph.edges.add(headToTail); + graph.containment.add(headToTail); + + graph.subgraphs.add(s); + if (s.getParent() is null) + roots.add(s); + if (s.members.size() is 2) //The 2 being the head and tail only + graph.edges.add(new Edge(s.head, s.tail)); + } + } + + buildNestingTreeIndices(roots, 0); + convertSubgraphEndpoints(graph); + addContainmentEdges(graph); + replaceSubgraphsWithBoundaries(graph); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/DirectedGraph.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/DirectedGraph.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,215 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.DirectedGraph; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.PositionConstants; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.graph.EdgeList; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.RankList; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.Rank; + +/** + * A graph consisting of nodes and directed edges. A DirectedGraph serves as the input to + * a graph layout algorithm. The algorithm will place the graph's nodes and edges + * according to certain goals, such as short, non-crossing edges, and readability. + * + * @author hudsonr + * @since 2.1.2 + */ +public class DirectedGraph { + +private int direction = PositionConstants.SOUTH; + +/** + * The default padding to be used for nodes which don't specify any padding. Padding is + * the amount of empty space to be left around a node. The default value is undefined. + */ +private Insets defaultPadding; + +/** + * All of the edges in the graph. + */ +public EdgeList edges; + +/** + * All of the nodes in the graph. + */ +public NodeList nodes; + +/** + * For internal use only. The list of rows which makeup the final graph layout. + * @deprecated + */ +public RankList ranks; + +Node forestRoot; +Insets margin; +int[] rankLocations; +int[][] cellLocations; +int tensorStrength; +int tensorSize; +Dimension size; + +public this(){ + defaultPadding = new Insets(16); + edges = new EdgeList(); + nodes = new NodeList(); + ranks = new RankList(); + margin = new Insets(); + size = new Dimension(); +} + +/** + * Returns the default padding for nodes. + * @return the default padding + * @since 3.2 + */ +public Insets getDefaultPadding() { + return defaultPadding; +} + +/** + * Returns the direction in which the graph will be layed out. + * @return the layout direction + * @since 3.2 + */ +public int getDirection() { + return direction; +} + +/** + * Sets the outer margin for the entire graph. The margin is the space in which nodes + * should not be placed. + * @return the graph's margin + * @since 3.2 + */ +public Insets getMargin() { + return margin; +} + +/** + * Returns the effective padding for the given node. If the node has a specified padding, + * it will be used, otherwise, the graph's defaultPadding is returned. The + * returned value must not be modified. + * @param node the node + * @return the effective padding for that node + */ +public Insets getPadding(Node node) { + Insets pad = node.getPadding(); + if (pad is null) + return defaultPadding; + return pad; +} + +int[] getCellLocations(int rank) { + return cellLocations[rank]; +} + +int[] getRankLocations() { + return rankLocations; +} +// +//public Cell getCell(Point pt) { +// int rank = 0; +// while (rank < rankLocations.length - 1 && rankLocations[rank] < pt.y) +// rank++; +// int cells[] = cellLocations[rank]; +// int cell = 0; +// while (cell < cells.length - 1 && cells[cell] < pt.x) +// cell++; +// return new Cell(rank, cell, ranks.getRank(rank).getNode(index)); +//} + +public Node getNode(int rank, int index) { + if (ranks.size() <= rank) + return null; + Rank r = ranks.getRank(rank); + if (r.size() <= index) + return null; + return r.getNode(index); +} + +/** + * Removes the given edge from the graph. + * @param edge the edge to be removed + */ +public void removeEdge(Edge edge) { + edges.remove(edge); + edge.source.outgoing.remove(edge); + edge.target.incoming.remove(edge); + if (edge.vNodes !is null) + for (int j = 0; j < edge.vNodes.size(); j++) + removeNode(edge.vNodes.getNode(j)); +} + +/** + * Removes the given node from the graph. Does not remove the node's edges. + * @param node the node to remove + */ +public void removeNode(Node node) { + nodes.remove(node); + if (ranks !is null) + ranks.getRank(node.rank).remove(node); +} + +/** + * Sets the default padding for all nodes in the graph. Padding is the empty space left + * around the outside of each node. The default padding is used for all nodes + * which do not specify a specific amount of padding (i.e., their padding is + * null). + * @param insets the padding + */ +public void setDefaultPadding(Insets insets) { + defaultPadding = insets; +} + +/** + * Sets the layout direction for the graph. Edges will be layed out in the specified + * direction (unless the graph contains cycles). Supported values are: + *
    + *
  • {@link dwtx.draw2d.PositionConstants#EAST} + *
  • {@link dwtx.draw2d.PositionConstants#SOUTH} + *
+ *

The default direction is south. + * @param direction the layout direction + * @since 3.2 + */ +public void setDirection(int direction) { + this.direction = direction; +} + +//public void setGraphTensor(int length, int strength) { +// tensorStrength = strength; +// tensorSize = length; +//} + +/** + * Sets the graphs margin. + * @param insets the graph's margin + * @since 3.2 + */ +public void setMargin(Insets insets) { + this.margin = insets; +} + +public Dimension getLayoutSize() { + return size; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/DirectedGraphLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/DirectedGraphLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2003, 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 + *******************************************************************************/ +module dwtx.draw2d.graph.DirectedGraphLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.TransposeMetrics; +import dwtx.draw2d.graph.BreakCycles; +import dwtx.draw2d.graph.RouteEdges; +import dwtx.draw2d.graph.InitialRankSolver; +import dwtx.draw2d.graph.TightSpanningTreeSolver; +import dwtx.draw2d.graph.RankAssignmentSolver; +import dwtx.draw2d.graph.PopulateRanks; +import dwtx.draw2d.graph.VerticalPlacement; +import dwtx.draw2d.graph.MinCross; +import dwtx.draw2d.graph.LocalOptimizer; +import dwtx.draw2d.graph.HorizontalPlacement; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.GraphVisitor; + +/** + * Performs a graph layout of a DirectedGraph. The directed graph must meet + * the following conditions: + *

    + *
  • The graph must be connected. + *
  • All edge's must be added to the graph's {@link DirectedGraph#edges edges} list + * exactly once. + *
  • All nodes must be added to the graph's {@link DirectedGraph#nodes nodes} list + * exactly once. + *
+ * + * This algorithm will: + *
    + *
  • break cycles by inverting a set of feedback edges. Feedback edges will have the + * flag {@link Edge#isFeedback} set to true. The following statements are + * true with respect to the inverted edge. When the algorithm completes, it will invert + * the edges again, but will leave the feedback flags set. + *
  • for each node n, assign n to a "rank" R(n), such that: for each edge (m, + * n) in n.incoming, R(m)<=R(n)-(m,n).delta. The total weighted edge lengths shall be + * no greater than is necessary to meet this requirement for all edges in the graph. + *
  • attempt to order the nodes in their ranks as to minimize crossings. + *
  • assign y coordinates to each node based on its rank. The spacing + * between ranks is the sum of the bottom padding of the previous rank, and the top + * padding of the next rank. + *
  • assign x coordinates such that the graph is easily readable. The exact + * behavior is undefined, but will favor edge's with higher {@link Edge#weight}s. The + * minimum x value assigned to a node or bendpoint will be 0. + *
  • assign bendpoints to all edge's which span more than 1 rank. + *
+ *

For each NODE: + *

    + *
  • The x coordinate will be assigned a value >= 0 + *
  • The y coordinate will be assigned a value >= 0 + *
  • The rank will be assigned a value >=0 + *
  • The height will be set to the height of the tallest node on the same row + *
+ *

For each EDGE: + *

    + *
  • If an edge spans more than 1 row, it will have a list of {@link + * dwtx.draw2d.graph.Edge#vNodes virtual} nodes. The virtual nodes will be + * assigned an x coordinate indicating the routing path for that edge. + *
  • If an edge is a feedback edge, it's isFeedback flag will be set, + * and if it has virtual nodes, they will be in reverse order (bottom-up). + *
+ *

This class is not guaranteed to produce the same results for each invocation. + * @author Randy Hudson + * @since 2.1.2 + */ +public class DirectedGraphLayout { + +List steps; + +/** + * @since 3.1 + */ +public this() { + steps = new ArrayList(); + init(); +} + +void init() { + steps.add(new TransposeMetrics()); + steps.add(new BreakCycles()); + steps.add(new RouteEdges()); + steps.add(new InitialRankSolver()); + steps.add(new TightSpanningTreeSolver()); + steps.add(new RankAssignmentSolver()); + steps.add(new PopulateRanks()); + steps.add(new VerticalPlacement()); + steps.add(new MinCross()); + steps.add(new LocalOptimizer()); + steps.add(new HorizontalPlacement()); +} + +/** + * Lays out the given graph + * @param graph the graph to layout + */ +public void visit(DirectedGraph graph) { + if (graph.nodes.isEmpty()) + return; + for (int i = 0; i < steps.size(); i++) { + GraphVisitor visitor = cast(GraphVisitor)steps.get(i); + visitor.visit(graph); + } + for (int i = steps.size() - 1; i >= 0; i--) { + GraphVisitor visitor = cast(GraphVisitor)steps.get(i); + visitor.revisit(graph); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/Edge.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/Edge.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,386 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.Edge; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.Node; + +/** + * A directed Edge joining a source and target Node. Edges indicate the dependencies + * between nodes. An Edge provides the information needed to perform a graph layout, and + * it stores the result of the layout in its various field. Therefore, it functions + * both as input and output. The input data consists of: + *

    + *
  • {@link #source} - the source Node + *
  • {@link #target} - the target Node + *
  • {@link #delta} - the minimum number of rows the edge should span + *
  • {@link #weight} - a hint indicating this edge's importance + *
  • {@link #width} - the edge's width + *
  • {@link #padding} - the amount of space to leave on either side of the edge + *
  • [{@link #offsetSource}] - the edge's attachment point at the source node + *
  • [{@link #offsetTarget}] - the edge's attachment point at the target node + *
+ *

The output of a layout consists of bending longer edges, and potentially inverting + * edges to remove cycles in the graph. The output consists of: + *

    + *
  • {@link #vNodes} - the virtual nodes (if any) which make up the bendpoints + *
  • {@link #isFeedback} - true if the edge points backwards + *
+ * + * @author hudsonr + * @since 2.1.2 + */ +public class Edge { + +int cut; + +/** + * An arbitrary data field for use by clients. + */ +public Object data; + +/** + * The minimum rank separation between the source and target nodes. The default value is + * 1. + * @deprecated use accessors instead + */ +public int delta = 1; + +/** + * The ending point. + * @deprecated use {@link #getPoints()} + */ +public Point end; + +bool flag; + +/** + * @deprecated INTERNAL field, use accessor method + * Indicates an edge was inverted during the layout + */ +public bool isFeedback_ = false; + +/** + * The edge's attachment point at the source node. The default value is -1, which + * indicates that the edge should use the node's default {@link Node#getOffsetOutgoing() + * outgoing} attachment point. + * @deprecated use accessors instead + */ +public int offsetSource = -1; + +/** + * The edge's attachment point at the target node. The default value is -1, which + * indicates that the edge should use the node's default {@link Node#getOffsetIncoming() + * incoming} attachment point. + * @deprecated use accessors instead + */ +public int offsetTarget = -1; + +/** + * The minimum amount of space to leave on both the left and right sides of the edge. + * @deprecated use accessors instead + */ +public int padding = 10; + +private PointList points; + +/** + * The source Node. + */ +public Node source; +/** + * The starting point. + * @deprecated use {@link #getPoints()} + */ +public Point start; + +/** + * The target Node. + */ +public Node target; + +bool tree; + +/** + * The virtual nodes used to bend edges which go across one or more ranks. Each virtual + * node is just a regular node which occupies some small amount of space on a row. It's + * width is equivalent to the edge's width. Clients can use each virtual node's location + * (x, y, width, and height) as the way to position an edge which spans multiple rows. + */ +public NodeList vNodes; +/** + * A hint indicating how straight and short the edge should be relative to + * other edges in the graph. The default value is 1. + */ +public int weight = 1; + +/** + * @deprecated use accessors instead + */ +public int width = 1; + +/** + * Constructs a new edge with the given source and target nodes. All other fields will + * have their default values. + * @param source the source Node + * @param target the target Node + */ +public this(Node source, Node target) { + this(null, source, target); +} + +/** + * Constructs a new edge with the given source, target, delta, and weight. + * @param source the source Node + * @param target the target Node + * @param delta the minimum edge span + * @param weight the weight hint + */ +public this(Node source, Node target, int delta, int weight) { + this(source, target); + this.delta = delta; + this.weight = weight; +} + +/** + * Constructs a new edge with the given data object, source, and target node. + * @param data an arbitrary data object + * @param source the source node + * @param target the target node + */ +public this(Object data, Node source, Node target) { + this.data = data; + this.source = source; + this.target = target; + source.outgoing.add(this); + target.incoming.add(this); +} + +/** + * Returns the delta value. The delta is the minimum rank separation for the edge's source + * and target nodes. + * @return the delta. + * @since 3.2 + */ +public int getDelta() { + return delta; +} + +/** + * For internal use only. Returns the index of the {@link Node} (or {@link VirtualNode}) + * on this edge at the given rank. If this edge doesn't have a node at the given rank, -1 + * is returned. + * @param rank the rank + * @return the edges index at the given rank + */ +int getIndexForRank(int rank) { + if (source.rank is rank) + return source.index; + if (target.rank is rank) + return target.index; + if (vNodes !is null) + return vNodes.getNode(rank - source.rank - 1).index; + return -1; +} + +/** + * For internal use only. Returns the target node's row minus the source node's row. + * @return the distance from the source to target ranks + */ +public int getLength() { + return (target.rank - source.rank); +} + +public int getPadding() { + return padding; +} + +/** + * Returns the path connecting the edge's source and target. + * @return a point list + * @since 3.2 + */ +public PointList getPoints() { + return points; +} + +int getSlack() { + return (target.rank - source.rank) - delta; +} + +/** + * Returns the effective source offset for this edge. The effective source offset is + * either the {@link #offsetSource} field, or the source node's default outgoing offset if + * that field's value is -1. + * @return the source offset + */ +public int getSourceOffset() { + if (offsetSource !is -1) + return offsetSource; + return source.getOffsetOutgoing(); +} + +/** + * Returns the effective target offset for this edge. The effective target offset is + * either the {@link #offsetTarget} field, or the target node's default incoming offset if + * that field's value is -1. + * @return the target offset + */ +public int getTargetOffset() { + if (offsetTarget !is -1) + return offsetTarget; + return target.getOffsetIncoming(); +} + +public int getWidth() { + return width; +} + +/** + * Swaps the source and target nodes. If any positional data has been calculated, it is + * inverted as well to reflect the new direction. + * @since 2.1.2 + */ +public void invert() { + source.outgoing.remove(this); + target.incoming.remove(this); + + Node oldTarget = target; + target = source; + source = oldTarget; + + int temp = offsetSource; + offsetSource = offsetTarget; + offsetTarget = temp; + + target.incoming.add(this); + source.outgoing.add(this); + + if (points !is null) + points.reverse(); + + if (vNodes !is null) { + NodeList newVNodes = new NodeList(); + for (int j = vNodes.size() - 1; j >= 0; j--) { + newVNodes.add(vNodes.getNode(j)); + } + vNodes = newVNodes; + } + + if (start !is null) { + Point pt = start; + start = end; + end = pt; + } +} + +/** + * Returns true if the edge was a feedback edge. The layout algorithm may + * invert one or more edges to remove all cycles from the input. The set of edges that are + * inverted are referred to as the "feedback" set. + * @return true if the edge is feedback + * @since 3.2 + */ +public bool isFeedback() { + return isFeedback_; +} + +/** + * For internal use only. Returns the node opposite the given node on this edge. + * @param end one end + * @return the other end + */ +public Node opposite(Node end) { + if (source is end) + return target; + return source; +} + +/** + * Sets the delta value. + * @param delta the new delta value + * @since 3.2 + */ +public void setDelta(int delta) { + this.delta = delta; +} + +/** + * Sets the padding for this edge. + * @param padding the padding + * @since 3.2 + */ +public void setPadding(int padding) { + this.padding = padding; +} + +void setPoints(PointList points) { + this.points = points; + start = points.getFirstPoint(); + end = points.getLastPoint(); +} + +/** + * Sets the source node and adds this edge to the new source's outgoing edges. If the + * source node is previously set, removes this edge from the old source's outgoing edges. + * @param node the new source + * @since 3.2 + */ +public void setSource(Node node) { + if (source is node) + return; + if (source !is null) + source.outgoing.remove(this); + source = node; + if (source !is null) + source.outgoing.add(this); +} + +public void setSourceOffset(int offset) { + this.offsetSource = offset; +} + +/** + * Sets the target node and adds this edge to the new target's incoming edges. If the + * target node is previously set, removes this edge from the old target's incoming edges. + * @param node the new target + * @since 3.2 + */ +public void setTarget(Node node) { + if (target is node) + return; + if (target !is null) + target.incoming.remove(this); + target = node; + if (target !is null) + target.incoming.add(this); +} + +public void setTargetOffset(int offset) { + this.offsetTarget = offset; +} + +/** + * Sets the width of the edge. + * @param width the new width + * @since 3.2 + */ +public void setWidth(int width) { + this.width = width; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/EdgeList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/EdgeList.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.EdgeList; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.Edge; +/** + * A list of Edges. + * @author hudsonr + * @since 2.1.2 + */ +public class EdgeList : ArrayList { + +/** + * Returns the edge for the given index. + * @param index the index of the requested edge + * @return the edge at the given index + */ +public Edge getEdge(int index) { + return cast(Edge)super.get(index); +} + +/** + * For intrenal use only. + * @param i and index + * @return a value + */ +public int getSourceIndex(int i) { + return getEdge(i).source.index; +} + +/** + * For internal use only. + * @param i an index + * @return a value + */ +public int getTargetIndex(int i) { + return getEdge(i).target.index; +} + +/** + * For internal use only. + * @return the minimum slack for this edge list + */ +public int getSlack() { + int slack = Integer.MAX_VALUE; + for (int i = 0; i < this.size(); i++) + slack = Math.min(slack, getEdge(i).getSlack()); + return slack; +} + +/** + * For internal use only. + * @return the total weight of all edges + */ +public int getWeight() { + int w = 0; + for (int i = 0; i < this.size(); i++) + w += getEdge(i).weight; + return w; +} + +/** + * For internal use only + * @return true if completely flagged + */ +public bool isCompletelyFlagged() { + for (int i = 0; i < size(); i++) { + if (!getEdge(i).flag) + return false; + } + return true; +} + +/** + * For internal use only. Resets all flags. + * @param resetTree internal + */ +public void resetFlags(bool resetTree) { + for (int i = 0; i < size(); i++) { + getEdge(i).flag = false; + if (resetTree) + getEdge(i).tree = false; + } +} + +/** + * For internal use only. + * @param value value + */ +public void setFlags(bool value) { + for (int i = 0; i < size(); i++) + getEdge(i).flag = value; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/GraphUtilities.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/GraphUtilities.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.GraphUtilities; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.Subgraph; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.Rank; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.EdgeList; + +/** + * Some utility methods for graphs. + * @author Eric Bordeau + * @since 2.1.2 + */ +class GraphUtilities { + +static Subgraph getCommonAncestor(Node left, Node right) { + Subgraph parent; + if (auto p = cast(Subgraph)right ) + parent = p; + else + parent = right.getParent(); + while (parent !is null) { + if (parent.isNested(left)) + return parent; + parent = parent.getParent(); + } + return null; +} + +/** + * Returns true if the given graph contains at least one cycle. + * @param graph the graph to test + * @return whether the graph is cyclic + */ +public static bool isCyclic(DirectedGraph graph) { + return isCyclic(new NodeList(graph.nodes)); +} + +/** + * Recursively removes leaf nodes from the list until there are no nodes remaining (acyclic) + * or there are no leaf nodes but the list is not empty (cyclic), then returns the result. + * @param nodes the list of nodes to test + * @return whether the graph is cyclic + */ +public static bool isCyclic(NodeList nodes) { + if (nodes.isEmpty()) + return false; + int size = nodes.size(); + // remove all the leaf nodes from the graph + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.getNode(i); + if (node.outgoing is null || node.outgoing.isEmpty()) { // this is a leaf node + nodes.remove(node); + for (int j = 0; j < node.incoming.size(); j++) { + Edge e = node.incoming.getEdge(j); + e.source.outgoing.remove(e); + } + } + } + // if no nodes were removed, that means there are no leaf nodes and the graph is cyclic + if (nodes.size() is size) + return true; + // leaf nodes were removed, so recursively call this method with the new list + return isCyclic(nodes); +} + +/** + * Counts the number of edge crossings in a DirectedGraph + * @param graph the graph whose crossed edges are counted + * @return the number of edge crossings in the graph + */ +public static int numberOfCrossingsInGraph(DirectedGraph graph) { + int crossings = 0; + for (int i = 0; i < graph.ranks.size(); i++) { + Rank rank = graph.ranks.getRank(i); + crossings += numberOfCrossingsInRank(rank); + } + return crossings; +} + +/** + * Counts the number of edge crossings in a Rank + * @param rank the rank whose crossed edges are counted + * @return the number of edge crossings in the rank + */ +public static int numberOfCrossingsInRank(Rank rank) { + int crossings = 0; + for (int i = 0; i < rank.size() - 1; i++) { + Node currentNode = rank.getNode(i); + Node nextNode; + for (int j = i + 1; j < rank.size(); j++) { + nextNode = rank.getNode(j); + EdgeList currentOutgoing = currentNode.outgoing; + EdgeList nextOutgoing = nextNode.outgoing; + for (int k = 0; k < currentOutgoing.size(); k++) { + Edge currentEdge = currentOutgoing.getEdge(k); + for (int l = 0; l < nextOutgoing.size(); l++) { + if (nextOutgoing.getEdge(l).getIndexForRank(currentNode.rank + 1) + < currentEdge.getIndexForRank(currentNode.rank + 1)) + crossings++; + } + } + } + } + return crossings; +} + +private static NodeList search(Node node, NodeList list) { + if (node.flag) + return list; + node.flag = true; + list.add(node); + for (int i = 0; i < node.outgoing.size(); i++) + search(node.outgoing.getEdge(i).target, list); + return list; +} + +/** + * Returns true if adding an edge between the 2 given nodes will introduce a + * cycle in the containing graph. + * @param source the potential source node + * @param target the potential target node + * @return whether an edge between the 2 given nodes will introduce a cycle + */ +public static bool willCauseCycle(Node source, Node target) { + NodeList nodes = search(target, new NodeList()); + nodes.resetFlags(); + return nodes.contains(source); +} + +static bool isConstrained(Node left, Node right) { + Subgraph common = left.getParent(); + while (common !is null && !common.isNested(right)) { + left = left.getParent(); + common = left.getParent(); + } + while (right.getParent() !is common) + right = right.getParent(); + return (left.rowOrder !is -1 && right.rowOrder !is -1) + && left.rowOrder !is right.rowOrder; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/GraphVisitor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/GraphVisitor.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.GraphVisitor; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.DirectedGraph; + +/** + * Performs some action on a Graph. + * @author Randy Hudson + * @since 2.1.2 + */ +abstract class GraphVisitor { + +/** + * Act on the given directed graph. + * @param g the graph + */ +void visit(DirectedGraph g) { } + +/** + * Called in reverse order of visit. + * @since 3.1 + * @param g the graph to act upon + */ +void revisit(DirectedGraph g) { } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/HorizontalPlacement.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/HorizontalPlacement.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,428 @@ +/******************************************************************************* + * Copyright (c) 2003, 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 + *******************************************************************************/ +module dwtx.draw2d.graph.HorizontalPlacement; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.SpanningTreeVisitor; +import dwtx.draw2d.graph.NodeCluster; +import dwtx.draw2d.graph.NodePair; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.RankList; +import dwtx.draw2d.graph.CollapsedEdges; +import dwtx.draw2d.graph.Rank; +import dwtx.draw2d.graph.EdgeList; +import dwtx.draw2d.graph.InitialRankSolver; +import dwtx.draw2d.graph.TightSpanningTreeSolver; +import dwtx.draw2d.graph.RankAssignmentSolver; + +/** + * Assigns the X and width values for nodes in a directed graph. + * @author Randy Hudson + * @since 2.1.2 + */ +class HorizontalPlacement : SpanningTreeVisitor { + +class ClusterSet { + int freedom = Integer.MAX_VALUE; + bool isRight; + public List members; + int pullWeight = 0; + int rawPull = 0; + + public this(){ + members = new ArrayList(); + } + + bool addCluster (NodeCluster seed) { + members.add(seed); + seed.isSetMember = true; + + rawPull += seed.weightedTotal; + pullWeight += seed.weightedDivisor; + if (isRight) { + freedom = Math.min(freedom, seed.rightNonzero); + if (freedom is 0 || rawPull <= 0) + return true; + addIncomingClusters(seed); + if (addOutgoingClusters(seed)) + return true; + } else { + freedom = Math.min(freedom, seed.leftNonzero); + if (freedom is 0 || rawPull >= 0) + return true; + addOutgoingClusters(seed); + if (addIncomingClusters(seed)) + return true; + } + return false; + } + + bool addIncomingClusters(NodeCluster seed) { + for (int i = 0; i < seed.leftCount; i++) { + NodeCluster neighbor = seed.leftNeighbors[i]; + if (neighbor.isSetMember) + continue; + CollapsedEdges edges = seed.leftLinks[i]; + if (!edges.isTight()) + continue; + if ((!isRight || neighbor.getPull() > 0) && addCluster (neighbor)) + return true; + } + return false; + } + + bool addOutgoingClusters(NodeCluster seed) { + for (int i = 0; i < seed.rightCount; i++) { + NodeCluster neighbor = seed.rightNeighbors[i]; + if (neighbor.isSetMember) + continue; + CollapsedEdges edges = seed.rightLinks[i]; + if (!edges.isTight()) + continue; + if ((isRight || neighbor.getPull() < 0) && addCluster (neighbor)) + return true; + } + return false; + } + + bool build(NodeCluster seed) { + isRight = seed.weightedTotal > 0; + if (!addCluster(seed)) { + int delta = rawPull / pullWeight; + if (delta < 0) + delta = Math.max(delta, -freedom); + else + delta = Math.min(delta, freedom); + if (delta !is 0) { + for (int i = 0; i < members.size(); i++) { + NodeCluster c = cast(NodeCluster)members.get(i); + c.adjustRank(delta, dirtyClusters); + } + refreshDirtyClusters(); + reset(); + return true; + } + } + reset(); + return false; + } + + void reset() { + rawPull = pullWeight = 0; + for (int i = 0; i < members.size(); i++) + (cast(NodeCluster)members.get(i)).isSetMember = false; + members.clear(); + freedom = Integer.MAX_VALUE; + } +} + +static int step; +private List allClusters; +private Map clusterMap; +ClusterSet clusterset; +Collection dirtyClusters; +DirectedGraph graph; +Map map_; +DirectedGraph prime; +Node graphRight; +Node graphLeft; + +public this(){ + clusterMap = new HashMap(); + clusterset = new ClusterSet(); + dirtyClusters = new HashSet(); + map_ = new HashMap(); +} + + +/** + * Inset the corresponding parts for the given 2 nodes along an edge E. The weight value + * is a value by which to scale the edges specified weighting factor. + * @param u the source + * @param v the target + * @param e the edge along which u and v exist + * @param weight a scaling for the weight + */ +void addEdge(Node u, Node v, Edge e, int weight) { + Node ne = new Node(new NodePair(u, v)); + prime.nodes.add(ne); + + ne.y = (u.y + u.height + v.y) / 2; + Node uPrime = get(u); + Node vPrime = get(v); + + int uOffset = e.getSourceOffset(); + + int vOffset = e.getTargetOffset(); + + Edge eu = new Edge(ne, uPrime, 0, e.weight * weight); + Edge ev = new Edge(ne, vPrime, 0, e.weight * weight); + + int dw = uOffset - vOffset; + if (dw < 0) + eu.delta = -dw; + else + ev.delta = dw; + + prime.edges.add(eu); + prime.edges.add(ev); +} + +/** + * Adds all of the incoming edges to the graph. + * @param n the original node + * @param nPrime its corresponding node in the auxilary graph + */ +void addEdges(Node n) { + for (int i = 0; i < n.incoming.size(); i++) { + Edge e = n.incoming.getEdge(i); + addEdge(e.source, n, e, 1); + } +} + +void applyGPrime() { + Node node; + for (int n = 0; n < prime.nodes.size(); n++) { + node = prime.nodes.getNode(n); + if (null !is cast(Node)node.data ) + (cast(Node)node.data).x = node.rank; + } +} + +private void balanceClusters() { + findAllClusters(); + + step = 0; + bool somethingMoved = false; + + for (int i = 0; i < allClusters.size();) { + + NodeCluster c = cast(NodeCluster)allClusters.get(i); + int delta = c.getPull(); + if (delta < 0) { + if (c.leftFreedom > 0) { + c.adjustRank(Math.max(delta, -c.leftFreedom), dirtyClusters); + refreshDirtyClusters(); + moveClusterForward(i, c); + somethingMoved = true; + step++; + } else if (clusterset.build(c)) { + step++; + moveClusterForward(i, c); + somethingMoved = true; + } + } else if (delta > 0) { + if (c.rightFreedom > 0) { + c.adjustRank(Math.min(delta, c.rightFreedom), dirtyClusters); + refreshDirtyClusters(); + moveClusterForward(i, c); + somethingMoved = true; + step++; + } else if (clusterset.build(c)) { + step++; + moveClusterForward(i, c); + somethingMoved = true; + } + } + i++; + if (i is allClusters.size() && somethingMoved) { + i = 0; + somethingMoved = false; + } + } +} + +//bool balanceClusterSets() { +// for (int i = 0; i < allClusters.size(); i++) { +// NodeCluster c = (NodeCluster)allClusters.get(i); +// if (c.weightedTotal < 0 && c.leftFreedom is 0) { +// if (clusterset.build(c)) { +// moveClusterForward(i, c); +// return true; +// } +// } else if (c.weightedTotal > 0 && c.rightFreedom is 0) { +// if (clusterset.build(c)) { +// moveClusterForward(i, c); +// return true; +// } +// } +// } +// return false; +//} + +void buildGPrime() { + RankList ranks = graph.ranks; + buildRankSeparators(ranks); + + Rank rank; + Node n; + for (int r = 1; r < ranks.size(); r++) { + rank = ranks.getRank(r); + for (int i = 0; i < rank.count(); i++) { + n = rank.getNode(i); + addEdges(n); + } + } +} + +void buildRankSeparators(RankList ranks) { + Rank rank; + Node n, nPrime, prevNPrime; + Edge e; + for (int r = 0; r < ranks.size(); r++) { + rank = ranks.getRank(r); + prevNPrime = null; + for (int i = 0; i < rank.count(); i++) { + n = rank.getNode(i); + nPrime = new Node(n); + if (i is 0) { + e = new Edge(graphLeft, nPrime, 0, 0); + prime.edges.add(e); + e.delta = graph.getPadding(n).left + graph.getMargin().left; + } else { + e = new Edge(prevNPrime, nPrime); + e.weight = 0; + prime.edges.add(e); + rowSeparation(e); + } + prevNPrime = nPrime; + prime.nodes.add(nPrime); + map(n, nPrime); + if (i is rank.count() - 1) { + e = new Edge(nPrime, graphRight, 0, 0); + e.delta = n.width + graph.getPadding(n).right + graph.getMargin().right; + prime.edges.add(e); + } + } + } +} + +private void calculateCellLocations() { + graph.cellLocations = new int[][](graph.ranks.size() + 1); + for (int row = 0; row < graph.ranks.size(); row++) { + Rank rank = graph.ranks.getRank(row); + int locations[] = graph.cellLocations[row] = new int[rank.size() + 1]; + int cell; + Node node = null; + for (cell = 0; cell < rank.size(); cell++) { + node = rank.getNode(cell); + locations[cell] = node.x - graph.getPadding(node).left; + } + locations[cell] = node.x + node.width + graph.getPadding(node).right; + } +} + +private void findAllClusters() { + Node root = prime.nodes.getNode(0); + NodeCluster cluster = new NodeCluster(); + allClusters = new ArrayList(); + allClusters.add(cluster); + growCluster(root, cluster); + + for (int i = 0; i < prime.edges.size(); i++) { + Edge e = prime.edges.getEdge(i); + NodeCluster sourceCluster = cast(NodeCluster)clusterMap.get(e.source); + NodeCluster targetCluster = cast(NodeCluster)clusterMap.get(e.target); + + //Ignore cluster internal edges + if (targetCluster is sourceCluster) + continue; + + CollapsedEdges link = sourceCluster.getRightNeighbor(targetCluster); + if (link is null) { + link = new CollapsedEdges(e); + sourceCluster.addRightNeighbor(targetCluster, link); + targetCluster.addLeftNeighbor(sourceCluster, link); + } else { + prime.removeEdge(link.processEdge(e)); + i--; + } + } + for (int i = 0; i < allClusters.size(); i++) + (cast(NodeCluster)allClusters.get(i)).initValues(); +} + +Node get(Node key) { + return cast(Node)map_.get(key); +} + +void growCluster(Node root, NodeCluster cluster) { + cluster.add(root); + clusterMap.put(root, cluster); + EdgeList treeChildren = getSpanningTreeChildren(root); + for (int i = 0; i < treeChildren.size(); i++) { + Edge e = treeChildren.getEdge(i); + if (e.cut !is 0) + growCluster(getTreeTail(e), cluster); + else { + NodeCluster newCluster = new NodeCluster(); + allClusters.add(newCluster); + growCluster(getTreeTail(e), newCluster); + } + } +} + +void map(Node key, Node value) { + map_.put(key, value); +} + +private void moveClusterForward(int i, NodeCluster c) { + if (i is 0) + return; + int swapIndex = i / 2; + Object temp = allClusters.get(swapIndex); + allClusters.set(swapIndex, c); + allClusters.set(i, temp); +} + +void refreshDirtyClusters() { + for (Iterator iter = dirtyClusters.iterator(); iter.hasNext();) + (cast(NodeCluster)iter.next()).refreshValues(); + dirtyClusters.clear(); +} + +void rowSeparation(Edge e) { + Node source = cast(Node)e.source.data; + Node target = cast(Node)e.target.data; + e.delta = source.width + + graph.getPadding(source).right + + graph.getPadding(target).left; +} + +public void visit(DirectedGraph g) { + graph = g; + prime = new DirectedGraph(); + prime.nodes.add(graphLeft = new Node(cast(Object)null)); + prime.nodes.add(graphRight = new Node(cast(Object)null)); + if (g.tensorStrength !is 0) + prime.edges.add(new Edge(graphLeft, graphRight, g.tensorSize, g.tensorStrength)); + buildGPrime(); + (new InitialRankSolver()) + .visit(prime); + (new TightSpanningTreeSolver()) + .visit(prime); + + RankAssignmentSolver solver = new RankAssignmentSolver(); + solver.visit(prime); + graph.size.width = graphRight.rank; + balanceClusters(); + + prime.nodes.adjustRank(-graphLeft.rank); + applyGPrime(); + calculateCellLocations(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/InitialRankSolver.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/InitialRankSolver.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.InitialRankSolver; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.EdgeList; +import dwtx.draw2d.graph.Edge; + +/** + * Assigns a valid rank assignment to all nodes based on their edges. The assignment is + * not optimal in that it does not provide the minimum global length of edge lengths. + * @author Randy Hudson + * @since 2.1.2 + */ +class InitialRankSolver : GraphVisitor { + +protected DirectedGraph graph; +protected EdgeList candidates; +protected NodeList members; + +public this(){ + candidates = new EdgeList(); + members = new NodeList(); +} + +public void visit(DirectedGraph graph) { + this.graph = graph; + graph.edges.resetFlags(false); + graph.nodes.resetFlags(); + solve(); +} + +protected void solve() { + if (graph.nodes.size() is 0) + return; + NodeList unranked = new NodeList(graph.nodes); + NodeList rankMe = new NodeList(); + Node node; + int i; + while (!unranked.isEmpty()) { + rankMe.clear(); + for (i = 0; i < unranked.size();) { + node = unranked.getNode(i); + if (node.incoming.isCompletelyFlagged()) { + rankMe.add(node); + unranked.remove(i); + } else + i++; + } + if (rankMe.size() is 0) + throw new RuntimeException("Cycle detected in graph"); //$NON-NLS-1$ + for (i = 0; i < rankMe.size(); i++) { + node = rankMe.getNode(i); + assignMinimumRank(node); + node.outgoing.setFlags(true); + } + } + + connectForest(); +} + +private void connectForest() { + List forest = new ArrayList(); + Stack stack = new Stack(); + NodeList tree; + graph.nodes.resetFlags(); + for (int i = 0; i < graph.nodes.size(); i++) { + Node neighbor, n = graph.nodes.getNode(i); + if (n.flag) + continue; + tree = new NodeList(); + stack.push(n); + while (!stack.isEmpty()) { + n = cast(Node) stack.pop(); + n.flag = true; + tree.add(n); + for (int s = 0; s < n.incoming.size(); s++) { + neighbor = n.incoming.getEdge(s).source; + if (!neighbor.flag) + stack.push(neighbor); + } + for (int s = 0; s < n.outgoing.size(); s++) { + neighbor = n.outgoing.getEdge(s).target; + if (!neighbor.flag) + stack.push(neighbor); + } + } + forest.add(tree); + } + + if (forest.size() > 1) { + //connect the forest + graph.forestRoot = new Node(stringcast("the forest root")); //$NON-NLS-1$ + graph.nodes.add(graph.forestRoot); + for (int i = 0; i < forest.size(); i++) { + tree = cast(NodeList) forest.get(i); + graph.edges.add(new Edge(graph.forestRoot, tree.getNode(0), 0, 0)); + } + } +} + +private void assignMinimumRank(Node node) { + int rank = 0; + Edge e; + for (int i1 = 0; i1 < node.incoming.size(); i1++) { + e = node.incoming.getEdge(i1); + rank = Math.max(rank, e.delta + e.source.rank); + } + node.rank = rank; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/InvertEdges.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/InvertEdges.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.InvertEdges; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.Edge; + +/** + * Inverts any edges which are marked as backwards or "feedback" edges. + * + * @author Daniel Lee + * @since 2.1.2 + */ +class InvertEdges : GraphVisitor { + +/** + * + * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph) + */ +public void visit(DirectedGraph g) { + for (int i = 0; i < g.edges.size(); i++) { + Edge e = g.edges.getEdge(i); + if (e.isFeedback) + e.invert(); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/LocalOptimizer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/LocalOptimizer.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.LocalOptimizer; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.GraphUtilities; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Rank; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.EdgeList; +import dwtx.draw2d.graph.Edge; + +/** + * This graph visitor examines all adjacent pairs of nodes and determines if + * swapping the two nodes provides improved graph aesthetics. + * @author Daniel Lee + * @since 2.1.2 + */ +class LocalOptimizer : GraphVisitor { + +bool shouldSwap(Node current, Node next) { + if (GraphUtilities.isConstrained(current, next)) + return false; + int crossCount = 0; + int invertedCrossCount = 0; + + EdgeList currentEdges = current.incoming; + EdgeList nextEdges = next.incoming; + int rank = current.rank - 1; + int iCurrent, iNext; + + for (int i = 0; i < currentEdges.size(); i++) { + Edge currentEdge = currentEdges.getEdge(i); + iCurrent = currentEdge.getIndexForRank(rank); + for (int j = 0; j < nextEdges.size(); j++) { + iNext = nextEdges.getEdge(j).getIndexForRank(rank); + if (iNext < iCurrent) + crossCount++; + else if (iNext > iCurrent) + invertedCrossCount++; + else { + //edges go to the same location + int offsetDiff = nextEdges.getEdge(j).getSourceOffset() + - currentEdge.getSourceOffset(); + if (offsetDiff < 0) + crossCount++; + else if (offsetDiff > 0) + invertedCrossCount++; + } + } + } + + currentEdges = current.outgoing; + nextEdges = next.outgoing; + rank = current.rank + 1; + + for (int i = 0; i < currentEdges.size(); i++) { + Edge currentEdge = currentEdges.getEdge(i); + iCurrent = currentEdge.getIndexForRank(rank); + for (int j = 0; j < nextEdges.size(); j++) { + iNext = nextEdges.getEdge(j).getIndexForRank(rank); + if (iNext < iCurrent) + crossCount++; + else if (iNext > iCurrent) + invertedCrossCount++; + else { + //edges go to the same location + int offsetDiff = nextEdges.getEdge(j).getTargetOffset() + - currentEdge.getTargetOffset(); + if (offsetDiff < 0) + crossCount++; + else if (offsetDiff > 0) + invertedCrossCount++; + } + } + } + if (invertedCrossCount < crossCount) + return true; + return false; +} + +private void swapNodes(Node current, Node next, Rank rank) { + int index = rank.indexOf(current); + rank.set(index + 1, current); + rank.set(index, next); + index = current.index; + current.index = next.index; + next.index = index; +} + +/** + * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph) + */ +public void visit(DirectedGraph g) { + bool flag; + do { + flag = false; + for (int r = 0; r < g.ranks.size(); r++) { + Rank rank = g.ranks.getRank(r); + for (int n = 0; n < rank.count() - 1; n++) { + Node currentNode = rank.getNode(n); + Node nextNode = rank.getNode(n + 1); + if (shouldSwap(currentNode, nextNode)) { + swapNodes(currentNode, nextNode, rank); + flag = true; + n = Math.max(0, n - 2); + } + } + } + } while (flag); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/MinCross.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/MinCross.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.MinCross; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.RankSorter; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.Rank; + +/** + * Sweeps up and down the ranks rearranging them so as to reduce edge crossings. + * @author Randy Hudson + * @since 2.1.2 + */ +class MinCross : GraphVisitor { + +static const int MAX = 45; + +private DirectedGraph g; +private RankSorter sorter; + +public this() { + sorter = new RankSorter(); +} + +/** + * @since 3.1 + */ +public this(RankSorter sorter) { + this(); + setRankSorter(sorter); +} + +public void setRankSorter(RankSorter sorter) { + this.sorter = sorter; +} + +void solve() { + Rank rank; + for (int loop = 0; loop < MAX; loop++) { + for (int row = 1; row < g.ranks.size(); row++) { + rank = g.ranks.getRank(row); + sorter.sortRankIncoming(g, rank, row, cast(double)loop / MAX); + } + if (loop is MAX - 1) + continue; + for (int row = g.ranks.size() - 2; row >= 0; row--) { + rank = g.ranks.getRank(row); + sorter.sortRankOutgoing(g, rank, row, cast(double)loop / MAX); + } + } +} + +/** + * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph) + */ +public void visit(DirectedGraph g) { + sorter.init(g); + this.g = g; + solve(); + sorter.optimize(g); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/NestingTree.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/NestingTree.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.NestingTree; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Rank; +import dwtx.draw2d.graph.Subgraph; + +class NestingTree { + +List contents; +bool isLeaf = true; +int size; +double sortValue; +Node subgraph; + +public this(){ + contents = new ArrayList(); +} +private static void addToNestingTree(Map map, Node child) { + Subgraph subgraph = child.getParent(); + NestingTree parent = cast(NestingTree)map.get(subgraph); + if (parent is null) { + parent = new NestingTree(); + parent.subgraph = subgraph; + map.put(subgraph, parent); + if (subgraph !is null) + addToNestingTree(map, parent); + } + parent.contents.add(child); +} + +private static void addToNestingTree(Map map, NestingTree branch) { + Subgraph subgraph = branch.subgraph.getParent(); + NestingTree parent = cast(NestingTree)map.get(subgraph); + if (parent is null) { + parent = new NestingTree(); + parent.subgraph = subgraph; + map.put(subgraph, parent); + if (subgraph !is null) + addToNestingTree(map, parent); + } + parent.contents.add(branch); +} + +static NestingTree buildNestingTreeForRank(Rank rank) { + Map nestingMap = new HashMap(); + + for (int j = 0; j < rank.count(); j++) { + Node node = rank.getNode(j); + addToNestingTree(nestingMap, node); + } + + return cast(NestingTree)nestingMap.get(null); +} + +void calculateSortValues() { + int total = 0; + for (int i = 0; i < contents.size(); i++) { + Object o = contents.get(i); + if ( auto e = cast(NestingTree)o ) { + isLeaf = false; + e.calculateSortValues(); + total += cast(int)(e.sortValue * e.size); + size += e.size; + } else { + Node n = cast(Node)o; + n.sortValue = n.index; + total += n.index; + size++; + } + } + sortValue = cast(double)total / size; +} + +void getSortValueFromSubgraph() { + if (subgraph !is null) + sortValue = subgraph.sortValue; + for (int i = 0; i < contents.size(); i++) { + Object o = contents.get(i); + if (auto nt = cast(NestingTree)o ) + nt.getSortValueFromSubgraph(); + } +} + +void recursiveSort(bool sortLeaves) { + if (isLeaf && !sortLeaves) + return; + bool change = false; + //Use modified bubble sort for almost-sorted lists. + do { + change = false; + for (int i = 0; i < contents.size() - 1; i++) + change |= swap(i); + if (!change) + break; + change = false; + for (int i = contents.size() - 2; i >= 0; i--) + change |= swap(i); + } while (change); + for (int i = 0; i < contents.size(); i++) { + Object o = contents.get(i); + if (auto nt = cast(NestingTree)o ) + nt.recursiveSort(sortLeaves); + } +} + +void repopulateRank(Rank r) { + for (int i = 0; i < contents.size(); i++) { + Object o = contents.get(i); + if (null !is cast(Node)o ) + r.add(o); + else + (cast(NestingTree)o).repopulateRank(r); + } +} + + +bool swap(int index) { + Object left = contents.get(index); + Object right = contents.get(index + 1); + double iL = (null !is cast(Node)left ) + ? (cast(Node)left).sortValue + : (cast(NestingTree)left).sortValue; + double iR = (null !is cast(Node)right ) + ? (cast(Node)right).sortValue + : (cast(NestingTree)right).sortValue; + if (iL <= iR) + return false; + contents.set(index, right); + contents.set(index + 1, left); + return true; +} + +public String toString() { + if( subgraph ) + return "Nesting:" ~ subgraph.toString; //$NON-NLS-1$ + return "Nesting: null"; //$NON-NLS-1$ +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/Node.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/Node.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,332 @@ +/******************************************************************************* + * Copyright (c) 2003, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.graph.Node; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import tango.text.convert.Format; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.graph.EdgeList; +import dwtx.draw2d.graph.Subgraph; +import dwtx.draw2d.graph.Edge; + +/** + * A node in a DirectedGraph. A node has 0 or more incoming and outgoing {@link Edge}s. A + * node is given a width and height by the client. When a layout places the node in the + * graph, it will determine the node's x and y location. It may also modify the node's + * height. + * + * A node represents both the input and the output for a layout + * algorithm. The following fields are used as input to a graph layout: + *
    + *
  • {@link #width} - the node's width. + *
  • {@link #height} - the node's height. + *
  • {@link #outgoing} - the node's outgoing edges. + *
  • {@link #incoming} - the node's incoming edges. + *
  • padding - the amount of space to be left around the outside of the node. + *
  • {@link #incomingOffset} - the default attachment point for incoming edges. + *
  • {@link #outgoingOffset} - the default attachment point for outgoing edges. + *
  • parent - the parent subgraph containing this node. + *
+ *

+ * The following fields are calculated by a graph layout and comprise the output: + *

    + *
  • {@link #x} - the node's x location + *
  • {@link #y} - the node's y location + *
  • {@link #height} - the node's height may be stretched to match the height of other + * nodes + *
+ * + * @author Randy Hudson + * @since 2.1.2 + */ +public class Node { + +Node left, right; + +Object[] workingData; +int[] workingInts; + +/** + * Clients may use this field to mark the Node with an arbitrary data object. + */ +public Object data; + +//used by various graph visitors +bool flag; + +/** + * The height of this node. This value should be set prior to laying out the directed + * graph. Depending on the layout rules, a node's height may be expanded to match the + * height of other nodes around it. + */ +public int height = 40; + +/** + * @deprecated use {@link #setRowConstraint(int)} and {@link #getRowConstraint()} + */ +public int rowOrder = -1; + +/** + * The edges for which this node is the target. + */ +public EdgeList incoming; + +/** + * The default attachment point for incoming edges. -1 indicates that the + * node's horizontal center should be used. + */ +public int incomingOffset = -1; + +// A non-decreasing number given to consecutive nodes in a Rank. +int index; + +//Used in Compound graphs to quickly determine whether a node is inside a subgraph. +int nestingIndex = -1; + +/** + * The edges for which this node is the source. + */ +public EdgeList outgoing; + +Insets padding; +private Subgraph parent; +int rank; + +/** + * @deprecated for internal use only + */ +public double sortValue; + +/** + * The node's outgoing offset attachment point. + */ +public int outgoingOffset = -1; + +/** + * The node's width. The default value is 50. + */ +public int width = 50; + +/** + * The node's x coordinate. + */ +public int x; +/** + * The node's y coordinate. + */ +public int y; + +/** + * Constructs a new node. + */ +public this() { } + +/** + * Constructs a node with the given data object + * @param data an arbitrary data object + */ +public this(Object data) { + this(data, null); +} + +/** + * Constructs a node inside the given subgraph. + * @param parent the parent subgraph + */ +public this(Subgraph parent) { + this(null, parent); +} + +/** + * Constructs a node with the given data object and parent subgraph. This node is added to + * the set of members for the parent subgraph + * @param data an arbitrary data object + * @param parent the parent subgraph or null + */ +public this(Object data, Subgraph parent) { + this.data = data; + this.parent = parent; + incoming = new EdgeList(); + outgoing = new EdgeList(); + workingData = new Object[3]; + workingInts = new int[4]; + if (parent !is null) + parent.addMember(this); +} + +/** + * Returns the incoming attachment point. This is the distance from the left edge to the + * default incoming attachment point for edges. Each incoming edge may have it's own + * attachment setting which takes priority over this default one. + * @return the incoming offset + */ +public int getOffsetIncoming() { + if (incomingOffset is -1) + return width / 2; + return incomingOffset; +} + +/** + * Returns the outgoing attachment point. This is the distance from the left edge to the + * default outgoing attachment point for edges. Each outgoing edge may have it's own + * attachment setting which takes priority over this default one. + * @return the outgoing offset + */ +public int getOffsetOutgoing() { + if (outgoingOffset is -1) + return width / 2; + return outgoingOffset; +} + +/** + * Returns the padding for this node or null if the default padding for the + * graph should be used. + * @return the padding or null + */ +public Insets getPadding() { + return padding; +} + +/** + * Returns the parent Subgraph or null if there is no parent. Subgraphs are + * only for use in {@link CompoundDirectedGraphLayout}. + * @return the parent or null + */ +public Subgraph getParent() { + return parent; +} + +/** + * For internal use only. Returns true if the given node is equal to this + * node. This method is implemented for consitency with Subgraph. + * @param node the node in question + * @return true if nested + */ +bool isNested(Node node) { + return node is this; +} + +/** + * Sets the padding. null indicates that the default padding should be used. + * @param padding an insets or null + */ +public void setPadding(Insets padding) { + this.padding = padding; +} + +/** + * Sets the parent subgraph. This method should not be called directly. The constructor + * will set the parent accordingly. + * @param parent the parent + */ +public void setParent(Subgraph parent) { + this.parent = parent; +} + +/** + * Sets the row sorting constraint for this node. By default, a node's constraint is + * -1. If two nodes have different values both >= 0, the node with the + * smaller constraint will be placed to the left of the other node. In all other cases no + * relative placement is guaranteed. + * @param value the row constraint + * @since 3.2 + */ +public void setRowConstraint(int value) { + this.rowOrder = value; +} + +/** + * Returns the row constraint for this node. + * @return the row constraint + * @since 3.2 + */ +public int getRowConstraint() { + return rowOrder; +} + +/** + * Sets the size of this node to the given dimension. + * @param size the new size + * @since 3.2 + */ +public void setSize(Dimension size) { + width = size.width; + height = size.height; +} + +/** + * @see Object#toString() + */ +public String toString() { + return Format("N({})", data ); //$NON-NLS-1$ //$NON-NLS-2$ +} + +Iterator iteratorNeighbors() { + return new class(outgoing) Iterator { + int offset; + EdgeList list; + this(EdgeList a){ + list = a; + } + public Object next() { + Edge edge = list.getEdge(offset++); + if (offset < list.size()) + return edge.opposite(this.outer); + if (list is outgoing) { + list = incoming; + offset = 0; + } else + list = null; + return edge.opposite(this.outer); + } + + public bool hasNext() { + if (list is null) + return false; + if (offset < list.size()) + return true; + if (list is outgoing) { + list = incoming; + offset = 0; + } + return offset < list.size(); + } + + public void remove() { + throw new RuntimeException("Remove not supported"); //$NON-NLS-1$ + } + }; +} + +/** + * Returns a reference to a node located left from this one + * @return Node on the left from this one + * @since 3.4 + */ +public Node getLeft() { + return left; +} + +/** + * Returns a reference to a node located right from this one + * @return Node on the right from this one + * @since 3.4 + */ +public Node getRight() { + return right; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/NodeCluster.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/NodeCluster.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,243 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.NodeCluster; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.CollapsedEdges; + +/** + * A group of nodes which are interlocked and cannot be separately placed. + * @since 3.1 + */ +class NodeCluster : NodeList { + +alias NodeList.adjustRank adjustRank; + +int toHash_; + +bool isSetMember; +bool isDirty; +bool leftDirty; +bool rightDirty; + +int leftFreedom; +int rightFreedom; +int leftNonzero; +int rightNonzero; +int leftCount = 0; +int rightCount = 0; + +CollapsedEdges[] leftLinks; +CollapsedEdges[] rightLinks; +NodeCluster[] leftNeighbors; +NodeCluster[] rightNeighbors; + +int effectivePull; +int weightedTotal; +int weightedDivisor; +int unweightedTotal; +int unweightedDivisor; + +public this(){ + toHash_ = (new Object()).toHash(); + leftLinks = new CollapsedEdges[10]; + rightLinks = new CollapsedEdges[10]; + leftNeighbors = new NodeCluster[10]; + rightNeighbors = new NodeCluster[10]; +} + +void addLeftNeighbor(NodeCluster neighbor, CollapsedEdges link) { + //Need to grow array in the following case + if (leftNeighbors.length is leftCount) { + int newSize = leftNeighbors.length * 2; + + NodeCluster newNeighbors[] = new NodeCluster[newSize]; + CollapsedEdges newLinks[] = new CollapsedEdges[newSize]; + + System.arraycopy(leftNeighbors, 0, newNeighbors, 0, leftNeighbors.length); + System.arraycopy(leftLinks, 0, newLinks, 0, leftLinks.length); + + leftNeighbors = newNeighbors; + leftLinks = newLinks; + } + leftNeighbors[leftCount] = neighbor; + leftLinks[leftCount++] = link; +} + +void addRightNeighbor(NodeCluster neighbor, CollapsedEdges link) { + if (rightNeighbors.length is rightCount) { + int newSize = rightNeighbors.length * 2; + + NodeCluster newNeighbors[] = new NodeCluster[newSize]; + CollapsedEdges newLinks[] = new CollapsedEdges[newSize]; + + System.arraycopy(rightNeighbors, 0, newNeighbors, 0, rightNeighbors.length); + System.arraycopy(rightLinks, 0, newLinks, 0, rightLinks.length); + + rightNeighbors = newNeighbors; + rightLinks = newLinks; + } + rightNeighbors[rightCount] = neighbor; + rightLinks[rightCount++] = link; +} + +public void adjustRank(int delta, Collection affected) { + adjustRank(delta); + NodeCluster neighbor; + CollapsedEdges edges; + for (int i = 0; i < leftCount; i++) { + neighbor = leftNeighbors[i]; + if (neighbor.isSetMember) + continue; + edges = leftLinks[i]; + + neighbor.weightedTotal += delta * edges.collapsedWeight; + neighbor.unweightedTotal += delta * edges.collapsedCount; + + weightedTotal -= delta * edges.collapsedWeight; + unweightedTotal -= delta * edges.collapsedCount; + + neighbor.rightDirty = leftDirty = true; + if (!neighbor.isDirty) { + neighbor.isDirty = true; + affected.add(neighbor); + } + } + for (int i = 0; i < rightCount; i++) { + neighbor = rightNeighbors[i]; + if (neighbor.isSetMember) + continue; + edges = rightLinks[i]; + + neighbor.weightedTotal += delta * edges.collapsedWeight; + neighbor.unweightedTotal += delta * edges.collapsedCount; + + weightedTotal -= delta * edges.collapsedWeight; + unweightedTotal -= delta * edges.collapsedCount; + + neighbor.leftDirty = rightDirty = true; + if (!neighbor.isDirty) { + neighbor.isDirty = true; + affected.add(neighbor); + } + } + isDirty = true; + affected.add(this); +} + +public override int opEquals(Object o) { + return o is this; +} + +CollapsedEdges getLeftNeighbor(NodeCluster neighbor) { + for (int i = 0; i < leftCount; i++) { + if (leftNeighbors[i] is neighbor) + return leftLinks[i]; + } + return null; +} + +int getPull() { + return effectivePull; +} + +CollapsedEdges getRightNeighbor(NodeCluster neighbor) { + for (int i = 0; i < rightCount; i++) { + if (rightNeighbors[i] is neighbor) + return rightLinks[i]; + } + return null; +} + +public override hash_t toHash() { + return toHash_; +} + +/** + * Initializes pull and freedom values. + */ +void initValues() { + weightedTotal = 0; + weightedDivisor = 0; + unweightedTotal = 0; + int slack; + + leftNonzero = rightNonzero = leftFreedom = rightFreedom = Integer.MAX_VALUE; + for (int i = 0; i < leftCount; i++) { + CollapsedEdges edges = leftLinks[i]; + weightedTotal -= edges.getWeightedPull(); + unweightedTotal -= edges.tightestEdge.getSlack(); + unweightedDivisor += edges.collapsedCount; + weightedDivisor += edges.collapsedWeight; + slack = edges.tightestEdge.getSlack(); + leftFreedom = Math.min(slack, leftFreedom); + if (slack > 0) + leftNonzero = Math.min(slack, leftNonzero); + } + for (int i = 0; i < rightCount; i++) { + CollapsedEdges edges = rightLinks[i]; + weightedTotal += edges.getWeightedPull(); + unweightedDivisor += edges.collapsedCount; + unweightedTotal += edges.tightestEdge.getSlack(); + weightedDivisor += edges.collapsedWeight; + slack = edges.tightestEdge.getSlack(); + rightFreedom = Math.min(slack, rightFreedom); + if (slack > 0) + rightNonzero = Math.min(slack, rightNonzero); + } + updateEffectivePull(); +} + +/** + * Refreshes the left and right freedom. + */ +void refreshValues() { + int slack; + isDirty = false; + if (leftDirty) { + leftDirty = false; + leftNonzero = leftFreedom = Integer.MAX_VALUE; + for (int i = 0; i < leftCount; i++) { + CollapsedEdges edges = leftLinks[i]; + slack = edges.tightestEdge.getSlack(); + leftFreedom = Math.min(slack, leftFreedom); + if (slack > 0) + leftNonzero = Math.min(slack, leftNonzero); + } + } + if (rightDirty) { + rightDirty = false; + rightNonzero = rightFreedom = Integer.MAX_VALUE; + for (int i = 0; i < rightCount; i++) { + CollapsedEdges edges = rightLinks[i]; + slack = edges.tightestEdge.getSlack(); + rightFreedom = Math.min(slack, rightFreedom); + if (slack > 0) + rightNonzero = Math.min(slack, rightNonzero); + } + } + updateEffectivePull(); +} + +private void updateEffectivePull() { + if (weightedDivisor !is 0) + effectivePull = weightedTotal / weightedDivisor; + else if (unweightedDivisor !is 0) + effectivePull = unweightedTotal / unweightedDivisor; + else + effectivePull = 0; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/NodeList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/NodeList.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.NodeList; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.Node; + +/** + * A list containing nodes. + * @author hudsonr + * @since 2.1.2 + */ +public class NodeList : ArrayList { + +/** + * Constructs an empty NodeList. + */ +public this() { } + +/** + * Constructs a NodeList with the elements from the specified list. + * @param list the list whose elements are to be added to this list + */ +public this(NodeList list) { + super(list); +} + +void adjustRank (int delta) { + if (delta is 0) + return; + for (int i = 0; i < size(); i++) + getNode(i).rank += delta; +} + +void resetSortValues() { + for (int i = 0; i < size(); i++) + getNode(i).sortValue = 0.0; +} + +void resetIndices() { + for (int i = 0; i < size(); i++) + getNode(i).index = 0; +} + +void normalizeRanks() { + int minRank = Integer.MAX_VALUE; + for (int i = 0; i < size(); i++) + minRank = Math.min(minRank, getNode(i).rank); + adjustRank(-minRank); +} + +/** + * Returns the Node at the given index. + * @param index the index + * @return the node at a given index + */ +public Node getNode(int index) { + return cast(Node)super.get(index); +} + +void resetFlags() { + for (int i = 0; i < size(); i++) { + getNode(i).flag = false; + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/NodePair.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/NodePair.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.NodePair; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.Node; +import tango.text.convert.Format; + +/** + * @author hudsonr + * @since 2.1 + */ +class NodePair { + +public Node n1; +public Node n2; + +public this() { } + +public this(Node n1, Node n2) { + this.n1 = n1; + this.n2 = n2; +} + +public override int opEquals(Object obj) { + if (auto np = cast(NodePair) obj ) { + return np.n1 is n1 && np.n2 is n2; + } + return false; +} + +public override hash_t toHash() { + return n1.toHash() ^ n2.toHash(); +} + +/** + * @see java.lang.Object#toString() + */ +public String toString() { + return Format("[{}, {}]", n1, n2 ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/Obstacle.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/Obstacle.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.Obstacle; + +import dwt.dwthelper.utils; +import tango.text.convert.Format; + +import dwtx.draw2d.PositionConstants; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.graph.Vertex; +import dwtx.draw2d.graph.ShortestPathRouter; + +/** + * An obstacle representation for the ShortestPathRouting. This is a subclass of Rectangle. + * + * This class is for internal use only. + * @author Whitney Sorenson + * @since 3.0 + */ +class Obstacle + : Rectangle +{ + +bool exclude; +Vertex topLeft, topRight, bottomLeft, bottomRight, center; +private ShortestPathRouter router; + +/** + * Creates a new obstacle from the given rectangle bounds. + * @param rect the bounds + */ +this(Rectangle rect, ShortestPathRouter router) { + init(rect); + this.router = router; +} + +/** + * Returns true if the given point is contained but not on the boundary of + * this obstacle. + * @param p a point + * @return true if properly contained + */ +public bool containsProper(Point p) { + return p.x > this.x + && p.x < this.x + this.width - 1 + && p.y > this.y + && p.y < this.y + this.height - 1; +} + +public int getSpacing() { + return router.getSpacing(); +} + +private void growVertex(Vertex vertex) { + if (vertex.totalCount > 0) + vertex.grow(); +} + +/** + * Grows all vertices on this obstacle. + */ +void growVertices() { + growVertex(topLeft); + growVertex(topRight); + growVertex(bottomLeft); + growVertex(bottomRight); +} + +/** + * Initializes this obstacle to the values of the given rectangle + * + * @param rect bounds of this obstacle + */ +void init(Rectangle rect) { + this.x = rect.x; + this.y = rect.y; + this.width = rect.width; + this.height = rect.height; + + exclude = false; + + topLeft = new Vertex(x, y, this); + topLeft.positionOnObstacle = PositionConstants.NORTH_WEST; + topRight = new Vertex(x + width - 1, y, this); + topRight.positionOnObstacle = PositionConstants.NORTH_EAST; + bottomLeft = new Vertex(x, y + height - 1, this); + bottomLeft.positionOnObstacle = PositionConstants.SOUTH_WEST; + bottomRight = new Vertex(x + width - 1, y + height - 1, this); + bottomRight.positionOnObstacle = PositionConstants.SOUTH_EAST; + center = new Vertex(getCenter(), this); +} + +/** + * Requests a full reset on all four vertices of this obstacle. + */ +void reset() { + topLeft.fullReset(); + bottomLeft.fullReset(); + bottomRight.fullReset(); + topRight.fullReset(); +} + +private void shrinkVertex(Vertex vertex) { + if (vertex.totalCount > 0) + vertex.shrink(); +} + +/** + * Shrinks all four vertices of this obstacle. + */ +void shrinkVertices() { + shrinkVertex(topLeft); + shrinkVertex(topRight); + shrinkVertex(bottomLeft); + shrinkVertex(bottomRight); +} + +/** + * @see dwtx.draw2d.geometry.Rectangle#toString() + */ +public String toString() { + return Format("Obstacle({}, {}, {}, {})", x, y, //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ + width, height );//$NON-NLS-2$//$NON-NLS-1$ +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/Path.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/Path.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,838 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.Path; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.PositionConstants; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.graph.Segment; +import dwtx.draw2d.graph.Vertex; +import dwtx.draw2d.graph.Obstacle; + +/** + * A Path representation for the ShortestPathRouting. A Path has a start and end point + * and may have bendpoints. The output of a path is accessed via the method + * getPoints(). + * + * This class is for internal use only. + * @author Whitney Sorenson + * @since 3.0 + */ +public class Path { + +/** + * A Stack of segments. + */ +private static class SegmentStack : ArrayList { + +Segment pop() { + return cast(Segment)remove(size() - 1); +} + +Obstacle popObstacle() { + return cast(Obstacle)remove(size() - 1); +} + +void push(Object obj) { + add(obj); +} + +} + +private static const Point CURRENT; +private static const double EPSILON = 1.04; +private static const Point NEXT; +private static const double OVAL_CONSTANT = 1.13; + +static this(){ + CURRENT = new Point(); + NEXT = new Point(); +} + +/** + * The bendpoint constraints. The path must go through these bendpoints. + */ +PointList bendpoints; +/** + * An arbitrary data field which can be used to map a Path back to some client object. + */ +public Object data; +List excludedObstacles; +List grownSegments; +/** + * this field is for internal use only. It is true whenever a property has been changed + * which requires the solver to resolve this path. + */ +public bool isDirty = true; + +bool isInverted = false; +bool isMarked = false; +PointList points; + +/** + * The previous cost ratio of the path. The cost ratio is the actual path length divided + * by the length from the start to the end. + */ +private double prevCostRatio; +List segments; + +private SegmentStack stack; +Vertex start, end; +private Path subPath; +double threshold; +Set visibleObstacles; +Set visibleVertices; + +/** + * Constructs a new path. + * @since 3.0 + */ +public this() { + segments = new ArrayList(); + grownSegments = new ArrayList(); + points = new PointList(); + visibleVertices = new HashSet(); + stack = new SegmentStack(); + visibleObstacles = new HashSet(); + excludedObstacles = new ArrayList(); +} + +/** + * Constructs a new path with the given data. + * @since 3.0 + * @param data an arbitrary data field + */ +public this(Object data) { + this(); + this.data = data; +} + +/** + * Constructs a new path with the given data, start and end point. + * + * @param start the start point for this path + * @param end the end point for this path + */ +public this(Point start, Point end) { + this(new Vertex(start, null), new Vertex(end, null)); +} + +/** + * Creates a path between the given vertices. + * + * @param start start vertex + * @param end end vertex + */ +this(Vertex start, Vertex end) { + this(); + this.start = start; + this.end = end; +} + +/** + * Attempts to add all segments between the given obstacles to the visibility graph. + * @param source the source obstacle + * @param target the target obstacle + */ +private void addAllSegmentsBetween(Obstacle source, Obstacle target) { + addConnectingSegment(new Segment(source.bottomLeft, target.bottomLeft), + source, target, false, false); + addConnectingSegment(new Segment(source.bottomRight, target.bottomRight), + source, target, true, true); + addConnectingSegment(new Segment(source.topLeft, target.topLeft), + source, target, true, true); + addConnectingSegment(new Segment(source.topRight, target.topRight), + source, target, false, false); + + if (source.bottom() is target.bottom()) { + addConnectingSegment(new Segment(source.bottomLeft, target.bottomRight), + source, target, false, true); + addConnectingSegment(new Segment(source.bottomRight, target.bottomLeft), + source, target, true, false); + } + if (source.y is target.y) { + addConnectingSegment(new Segment(source.topLeft, target.topRight), + source, target, true, false); + addConnectingSegment(new Segment(source.topRight, target.topLeft), + source, target, false, true); + } + if (source.x is target.x) { + addConnectingSegment(new Segment(source.bottomLeft, target.topLeft), + source, target, false, true); + addConnectingSegment(new Segment(source.topLeft, target.bottomLeft), + source, target, true, false); + } + if (source.right() is target.right()) { + addConnectingSegment(new Segment(source.bottomRight, target.topRight), + source, target, true, false); + addConnectingSegment(new Segment(source.topRight, target.bottomRight), + source, target, false, true); + } +} + +/** + * Attempts to add a segment between the given obstacles to the visibility graph. This + * method is specifically written for the case where the two obstacles intersect and contains + * a bool as to whether to check the diagonal that includes the top right point of the + * other obstacle. + * + * @param segment the segment to check + * @param o1 the first obstacle + * @param o2 the second obstacle + * @param checkTopRight1 whether or not to check the diagonal containing top right point + */ +private void addConnectingSegment(Segment segment, Obstacle o1, Obstacle o2, + bool checkTopRight1, bool checkTopRight2) { + if (threshold !is 0 + && (segment.end.getDistance(end) + segment.end.getDistance(start) > threshold + || segment.start.getDistance(end) + segment.start.getDistance(start) > threshold)) + return; + + if (o2.containsProper(segment.start) || o1.containsProper(segment.end)) + return; + + if (checkTopRight1 && segment.intersects(o1.x, o1.bottom() - 1, o1.right() - 1, o1.y)) + return; + if (checkTopRight2 && segment.intersects(o2.x, o2.bottom() - 1, o2.right() - 1, o2.y)) + return; + if (!checkTopRight1 && segment.intersects(o1.x, o1.y, o1.right() - 1, o1.bottom() - 1)) + return; + if (!checkTopRight2 && segment.intersects(o2.x, o2.y, o2.right() - 1, o2.bottom() - 1)) + return; + + stack.push(o1); + stack.push(o2); + stack.push(segment); +} + +/** + * Adds an obstacle to the visibility graph and generates new segments + * @param newObs the new obstacle, should not be in the graph already + */ +private void addObstacle(Obstacle newObs) { + visibleObstacles.add(newObs); + Iterator oItr = (new HashSet(visibleObstacles)).iterator(); + while (oItr.hasNext()) { + Obstacle currObs = cast(Obstacle)oItr.next(); + if (newObs !is currObs) + addSegmentsFor(newObs, currObs); + } + addPerimiterSegments(newObs); + addSegmentsFor(start, newObs); + addSegmentsFor(end, newObs); +} + +/** + * Adds the segments along the perimiter of an obstacle to the visiblity graph queue. + * @param obs the obstacle + */ +private void addPerimiterSegments(Obstacle obs) { + Segment seg = new Segment(obs.topLeft, obs.topRight); + stack.push(obs); + stack.push(null); + stack.push(seg); + seg = new Segment(obs.topRight, obs.bottomRight); + stack.push(obs); + stack.push(null); + stack.push(seg); + seg = new Segment(obs.bottomRight, obs.bottomLeft); + stack.push(obs); + stack.push(null); + stack.push(seg); + seg = new Segment(obs.bottomLeft, obs.topLeft); + stack.push(obs); + stack.push(null); + stack.push(seg); +} + +/** + * Attempts to add a segment to the visibility graph. + * First checks to see if the segment is outside the threshold oval. Then it compares the segment + * against all obstacles. If it is clean, the segment is finally added to the graph. + * + * @param segment the segment + * @param exclude1 an obstacle to exclude from the search + * @param exclude2 another obstacle to exclude from the search + * @param allObstacles the list of all obstacles + */ +private void addSegment(Segment segment, Obstacle exclude1, Obstacle exclude2, List allObstacles) { + if (threshold !is 0 + && (segment.end.getDistance(end) + segment.end.getDistance(start) > threshold + || segment.start.getDistance(end) + segment.start.getDistance(start) > threshold)) + return; + + for (int i = 0; i < allObstacles.size(); i++) { + Obstacle obs = cast(Obstacle)allObstacles.get(i); + + if (obs is exclude1 || obs is exclude2 || obs.exclude) + continue; + + if (segment.intersects(obs.x, obs.y, obs.right() - 1, obs.bottom() - 1) + || segment.intersects(obs.x, obs.bottom() - 1, obs.right() - 1, obs.y) + || obs.containsProper(segment.start) + || obs.containsProper(segment.end)) { + if (!visibleObstacles.contains(obs)) + addObstacle(obs); + return; + } + } + + linkVertices(segment); +} + +/** + * Adds the segments between the given obstacles. + * @param source source obstacle + * @param target target obstacle + */ +private void addSegmentsFor(Obstacle source, Obstacle target) { + if (source.intersects(target)) + addAllSegmentsBetween(source, target); + else if (target.bottom() - 1 < source.y) + addSegmentsTargetAboveSource(source, target); + else if (source.bottom() - 1 < target.y) + addSegmentsTargetAboveSource(target, source); + else if (target.right() - 1 < source.x) + addSegmentsTargetBesideSource(source, target); + else + addSegmentsTargetBesideSource(target, source); +} + +/** + * Adds the segments between the given obstacles. + * @param source source obstacle + * @param target target obstacle + */ +private void addSegmentsFor(Vertex vertex, Obstacle obs) { + Segment seg = null; + Segment seg2 = null; + + switch (obs.getPosition(vertex)) { + case PositionConstants.SOUTH_WEST : + case PositionConstants.NORTH_EAST : + seg = new Segment(vertex, obs.topLeft); + seg2 = new Segment(vertex, obs.bottomRight); + break; + case PositionConstants.SOUTH_EAST : + case PositionConstants.NORTH_WEST : + seg = new Segment(vertex, obs.topRight); + seg2 = new Segment(vertex, obs.bottomLeft); + break; + case PositionConstants.NORTH : + seg = new Segment(vertex, obs.topLeft); + seg2 = new Segment(vertex, obs.topRight); + break; + case PositionConstants.EAST : + seg = new Segment(vertex, obs.bottomRight); + seg2 = new Segment(vertex, obs.topRight); + break; + case PositionConstants.SOUTH : + seg = new Segment(vertex, obs.bottomRight); + seg2 = new Segment(vertex, obs.bottomLeft); + break; + case PositionConstants.WEST : + seg = new Segment(vertex, obs.topLeft); + seg2 = new Segment(vertex, obs.bottomLeft); + break; + default: + if (vertex.x is obs.x) { + seg = new Segment(vertex, obs.topLeft); + seg2 = new Segment(vertex, obs.bottomLeft); + } else if (vertex.y is obs.y) { + seg = new Segment(vertex, obs.topLeft); + seg2 = new Segment(vertex, obs.topRight); + } else if (vertex.y is obs.bottom() - 1) { + seg = new Segment(vertex, obs.bottomLeft); + seg2 = new Segment(vertex, obs.bottomRight); + } else if (vertex.x is obs.right() - 1) { + seg = new Segment(vertex, obs.topRight); + seg2 = new Segment(vertex, obs.bottomRight); + } else { + throw new RuntimeException("Unexpected vertex conditions"); //$NON-NLS-1$ + } + } + + stack.push(obs); + stack.push(null); + stack.push(seg); + stack.push(obs); + stack.push(null); + stack.push(seg2); +} + +private void addSegmentsTargetAboveSource(Obstacle source, Obstacle target) { + //target located above source + Segment seg = null; + Segment seg2 = null; + if (target.x > source.x) { + seg = new Segment(source.topLeft, target.topLeft); + if (target.x < source.right() - 1) + seg2 = new Segment(source.topRight, target.bottomLeft); + else + seg2 = new Segment(source.bottomRight, target.topLeft); + } else if (source.x is target.x) { + seg = new Segment(source.topLeft, target.bottomLeft); + seg2 = new Segment(source.topRight, target.bottomLeft); + } else { + seg = new Segment(source.bottomLeft, target.bottomLeft); + seg2 = new Segment(source.topRight, target.bottomLeft); + } + + stack.push(source); + stack.push(target); + stack.push(seg); + stack.push(source); + stack.push(target); + stack.push(seg2); + seg = null; + seg2 = null; + + if (target.right() < source.right()) { + seg = new Segment(source.topRight, target.topRight); + if (target.right() - 1 > source.x) + seg2 = new Segment(source.topLeft, target.bottomRight); + else + seg2 = new Segment(source.bottomLeft, target.topRight); + } else if (source.right() is target.right()) { + seg = new Segment(source.topRight, target.bottomRight); + seg2 = new Segment(source.topLeft, target.bottomRight); + } else { + seg = new Segment(source.bottomRight, target.bottomRight); + seg2 = new Segment(source.topLeft, target.bottomRight); + } + + stack.push(source); + stack.push(target); + stack.push(seg); + stack.push(source); + stack.push(target); + stack.push(seg2); +} + +private void addSegmentsTargetBesideSource(Obstacle source, Obstacle target) { + //target located above source + Segment seg = null; + Segment seg2 = null; + if (target.y > source.y) { + seg = new Segment(source.topLeft, target.topLeft); + if (target.y < source.bottom() - 1) + seg2 = new Segment(source.bottomLeft, target.topRight); + else + seg2 = new Segment(source.bottomRight, target.topLeft); + } else if (source.y is target.y) { + //degenerate case + seg = new Segment(source.topLeft, target.topRight); + seg2 = new Segment(source.bottomLeft, target.topRight); + } else { + seg = new Segment(source.topRight, target.topRight); + seg2 = new Segment(source.bottomLeft, target.topRight); + } + stack.push(source); + stack.push(target); + stack.push(seg); + stack.push(source); + stack.push(target); + stack.push(seg2); + seg = null; + seg2 = null; + + if (target.bottom() < source.bottom()) { + seg = new Segment(source.bottomLeft, target.bottomLeft); + if (target.bottom() - 1 > source.y) + seg2 = new Segment(source.topLeft, target.bottomRight); + else + seg2 = new Segment(source.topRight, target.bottomLeft); + } else if (source.bottom() is target.bottom()) { + seg = new Segment(source.bottomLeft, target.bottomRight); + seg2 = new Segment(source.topLeft, target.bottomRight); + } else { + seg = new Segment(source.bottomRight, target.bottomRight); + seg2 = new Segment(source.topLeft, target.bottomRight); + } + stack.push(source); + stack.push(target); + stack.push(seg); + stack.push(source); + stack.push(target); + stack.push(seg2); +} + +/** + * + */ +void cleanup() { + //segments.clear(); + visibleVertices.clear(); +} + +/** + * Begins the creation of the visibility graph with the first segment + * @param allObstacles list of all obstacles + */ +private void createVisibilityGraph(List allObstacles) { + stack.push(null); + stack.push(null); + stack.push(new Segment(start, end)); + + while (!stack.isEmpty()) + addSegment(stack.pop(), stack.popObstacle(), stack.popObstacle(), allObstacles); +} + +/** + * Once the visibility graph is constructed, this is called to label the graph and + * determine the shortest path. Returns false if no path can be found. + * + * @return true if a path can be found. + */ +private bool determineShortestPath() { + if (!labelGraph()) + return false; + Vertex vertex = end; + prevCostRatio = end.cost / start.getDistance(end); + + Vertex nextVertex; + while (!vertex.opEquals(start)) { + nextVertex = vertex.label; + if (nextVertex is null) + return false; + Segment s = new Segment(nextVertex, vertex); + segments.add(s); + vertex = nextVertex; + } + + Collections.reverse(segments); + return true; +} + +/** + * Resets all necessary fields for a solve. + */ +void fullReset() { + visibleVertices.clear(); + segments.clear(); + if (prevCostRatio is 0) { + double distance = start.getDistance(end); + threshold = distance * OVAL_CONSTANT; + } else + threshold = prevCostRatio * EPSILON * start.getDistance(end); + visibleObstacles.clear(); + resetPartial(); +} + +/** + * Creates the visibility graph and returns whether or not a shortest path could be + * determined. + * + * @param allObstacles the list of all obstacles + * @return true if a shortest path was found + */ +bool generateShortestPath(List allObstacles) { + createVisibilityGraph(allObstacles); + + if (visibleVertices.size() is 0) + return false; + + return determineShortestPath(); +} + +/** + * Returns the list of constrained points through which this path must pass or + * null. + * @see #setBendPoints(PointList) + * @return list of bend points + */ +public PointList getBendPoints() { + return bendpoints; +} + +/** + * Returns the end point for this path + * @return end point for this path + */ +public Point getEndPoint() { + return end; +} + +/** + * Returns the solution to this path. + * + * @return the points for this path. + */ +public PointList getPoints() { + return points; +} + +/** + * Returns the start point for this path + * @return start point for this path + */ +public Point getStartPoint() { + return start; +} + +/** + * Returns a subpath for this path at the given segment + * + * @param currentSegment the segment at which the subpath should be created + * @return the new path + */ +Path getSubPath(Segment currentSegment) { + // ready new path + Path newPath = new Path(currentSegment.start, end); + newPath.grownSegments = new ArrayList(grownSegments.subList( + grownSegments.indexOf(currentSegment), + grownSegments.size())); + + // fix old path + grownSegments = new ArrayList(grownSegments.subList( + 0, grownSegments.indexOf(currentSegment) + 1)); + end = currentSegment.end; + + subPath = newPath; + return newPath; +} + +/** + * Resets the vertices that this path has traveled prior to this segment. This is called + * when the path has become inverted and needs to rectify any labeling mistakes it made + * before it knew it was inverted. + * @param currentSegment the segment at which the path found it was inverted + */ +void invertPriorVertices(Segment currentSegment) { + int stop = grownSegments.indexOf(currentSegment); + for (int i = 0; i < stop; i++) { + Vertex vertex = (cast(Segment)grownSegments.get(i)).end; + if (vertex.type is Vertex.INNIE) + vertex.type = Vertex.OUTIE; + else + vertex.type = Vertex.INNIE; + } +} + +/** + * Returns true if this obstacle is in the visibility graph + * @param obs the obstacle + * @return true if obstacle is in the visibility graph + */ +bool isObstacleVisible(Obstacle obs) { + return visibleObstacles.contains(obs); +} + +/** + * Labels the visibility graph to assist in finding the shortest path + * @return false if there was a gap in the visibility graph + */ +private bool labelGraph() { + int numPermanentNodes = 1; + Vertex vertex = start; + Vertex neighborVertex = null; + vertex.isPermanent = true; + double newCost; + while (numPermanentNodes !is visibleVertices.size()) { + List neighbors = vertex.neighbors; + if (neighbors is null) + return false; + // label neighbors if they have a new shortest path + for (int i = 0; i < neighbors.size(); i++) { + neighborVertex = cast(Vertex)neighbors.get(i); + if (!neighborVertex.isPermanent) { + newCost = vertex.cost + vertex.getDistance(neighborVertex); + if (neighborVertex.label is null) { + neighborVertex.label = vertex; + neighborVertex.cost = newCost; + } else if (neighborVertex.cost > newCost) { + neighborVertex.label = vertex; + neighborVertex.cost = newCost; + } + } + } + // find the next none-permanent, labeled vertex with smallest cost + double smallestCost = 0; + Vertex tempVertex = null; + Iterator v = visibleVertices.iterator(); + while (v.hasNext()) { + tempVertex = cast(Vertex)v.next(); + if (!tempVertex.isPermanent && tempVertex.label !is null + && (tempVertex.cost < smallestCost || smallestCost is 0)) { + smallestCost = tempVertex.cost; + vertex = tempVertex; + } + } + // set the new vertex to permanent. + vertex.isPermanent = true; + numPermanentNodes++; + } + return true; +} + +/** + * Links two vertices together in the visibility graph + * @param segment the segment to add + */ +private void linkVertices(Segment segment) { + if (segment.start.neighbors is null) + segment.start.neighbors = new ArrayList(); + if (segment.end.neighbors is null) + segment.end.neighbors = new ArrayList(); + + if (!segment.start.neighbors.contains(segment.end)) { + segment.start.neighbors.add(segment.end); + segment.end.neighbors.add(segment.start); + } + + visibleVertices.add(segment.start); + visibleVertices.add(segment.end); +} + +/** + * Called to reconnect a subpath back onto this path. Does a depth-first search to + * reconnect all paths. Should be called after sorting. + */ +void reconnectSubPaths() { + if (subPath !is null) { + subPath.reconnectSubPaths(); + + Segment changedSegment = cast(Segment)subPath.grownSegments.remove(0); + Segment oldSegment = cast(Segment)grownSegments.get(grownSegments.size() - 1); + + oldSegment.end = changedSegment.end; + grownSegments.addAll(subPath.grownSegments); + + subPath.points.removePoint(0); + points.removePoint(points.size() - 1); + points.addAll(subPath.points); + + visibleObstacles.addAll(subPath.visibleObstacles); + + end = subPath.end; + subPath = null; + } +} + + +/** + * Refreshes the exclude field on the obstacles in the list. Excludes all obstacles + * that contain the start or end point for this path. + * @param allObstacles list of all obstacles + */ +void refreshExcludedObstacles(List allObstacles) { + excludedObstacles.clear(); + + for (int i = 0; i < allObstacles.size(); i++) { + Obstacle o = cast(Obstacle)allObstacles.get(i); + o.exclude = false; + + if (o.contains(start)) { + if (o.containsProper(start)) + o.exclude = true; + else { + /* + * $TODO Check for corners. If the path begins exactly at the corner of + * an obstacle, the exclude should also be true. + * + * Or, change segment intersection so that two segments that share an + * endpoint do not intersect. + */ + } + } + + if (o.contains(end)) { + if (o.containsProper(end)) + o.exclude = true; + else { + //check for corners. See above statement. + } + } + + if (o.exclude && !excludedObstacles.contains(o)) + excludedObstacles.add(o); + } +} + +/** + * Resets the fields for everything in the solve after the visibility graph steps. + */ +void resetPartial() { + isMarked = false; + isInverted = false; + subPath = null; + isDirty = false; + grownSegments.clear(); + points.removeAllPoints(); +} + +/** + * Sets the list of bend points to the given list and dirties the path. + * @param bendPoints the list of bend points + */ +public void setBendPoints(PointList bendPoints) { + this.bendpoints = bendPoints; + isDirty = true; +} + +/** + * Sets the end point for this path to the given point. + * @param end the new end point for this path + */ +public void setEndPoint(Point end) { + if (end.opEquals(this.end)) + return; + this.end = new Vertex(end, null); + isDirty = true; +} + +/** + * Sets the start point for this path to the given point. + * @param start the new start point for this path + */ +public void setStartPoint(Point start) { + if (start.opEquals(this.start)) + return; + this.start = new Vertex(start, null); + isDirty = true; +} + +/** + * Returns true if the path is clean and intersects the given obstacle. Also + * dirties the path in the process. + * @since 3.0 + * @param obs the obstacle + * @return true if a clean path touches the obstacle + */ + bool testAndSet(Obstacle obs) { + if (isDirty) + return false; + //This will never actually happen because obstacles are not stored by identity + if (excludedObstacles.contains(obs)) + return false; + + Segment seg1 = new Segment(obs.topLeft, obs.bottomRight); + Segment seg2 = new Segment(obs.topRight, obs.bottomLeft); + + for (int s = 0; s < points.size() - 1; s++) { + points.getPoint(CURRENT, s); + points.getPoint(NEXT, s + 1); + + if (seg1.intersects(CURRENT, NEXT) || seg2.intersects(CURRENT, NEXT) + || obs.contains(CURRENT) || obs.contains(NEXT)) { + isDirty = true; + return true; + } + } + return false; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/PopulateRanks.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/PopulateRanks.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2003, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.graph.PopulateRanks; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.RevertableChange; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.RankList; +import dwtx.draw2d.graph.Rank; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.VirtualNodeCreation; + +/** + * This class takes a DirectedGraph with an optimal rank assignment and a spanning tree, + * and populates the ranks of the DirectedGraph. Virtual nodes are inserted for edges that + * span 1 or more ranks. + *

+ * Ranks are populated using a pre-order depth-first traversal of the spanning tree. For + * each node, all edges requiring virtual nodes are added to the ranks. + * @author Randy Hudson + * @since 2.1.2 + */ +class PopulateRanks : GraphVisitor { + +private Stack changes; + +public this(){ + changes = new Stack(); +} +/** + * @see GraphVisitor#visit(DirectedGraph) + */ +public void visit(DirectedGraph g) { + if (g.forestRoot !is null) { + for (int i = g.forestRoot.outgoing.size() - 1; i >= 0; i--) + g.removeEdge(g.forestRoot.outgoing.getEdge(i)); + g.removeNode(g.forestRoot); + } + g.ranks = new RankList(); + for (int i = 0; i < g.nodes.size(); i++) { + Node node = g.nodes.getNode(i); + g.ranks.getRank(node.rank).add(node); + } + for (int i = 0; i < g.nodes.size(); i++) { + Node node = g.nodes.getNode(i); + for (int j = 0; j < node.outgoing.size();) { + Edge e = node.outgoing.getEdge(j); + if (e.getLength() > 1) + changes.push(new VirtualNodeCreation(e, g)); + else + j++; + } + } +} + +/** + * @see GraphVisitor#revisit(DirectedGraph) + */ +public void revisit(DirectedGraph g) { + for (int r = 0; r < g.ranks.size(); r++) { + Rank rank = g.ranks.getRank(r); + Node prev = null, cur; + for (int n = 0; n < rank.size(); n++) { + cur = rank.getNode(n); + cur.left = prev; + if (prev !is null) { + prev.right = cur; + } + prev = cur; + } + } + for (int i = 0; i < changes.size(); i++) { + RevertableChange change = cast(RevertableChange)changes.get(i); + change.revert(); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/Rank.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/Rank.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.Rank; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.SubgraphBoundary; +import dwtx.draw2d.graph.NodeList; + +/** + * For Internal Use only. + * @author hudsonr + * @since 2.1.2 + */ +public class Rank : NodeList { + +alias NodeList.add add; + +int bottomPadding; +int height; +int location; + +const int hash; +int topPadding; +int total; + +public this(){ + hash = (new Object()).toHash(); +} + +void add(Node n) { + super.add(n); +} + +void assignIndices() { + total = 0; + Node node; + + int mag; + for (int i = 0; i < size(); i++) { + node = getNode(i); + mag = Math.max(1, node.incoming.size() + node.outgoing.size()); + mag = Math.min(mag, 5); + if (null !is cast(SubgraphBoundary)node ) + mag = 4; + total += mag; + node.index = total; + total += mag; + } +} + +/** + * Returns the number of nodes in this rank. + * @return the number of nodes + */ +public int count() { + return super.size(); +} + +/** + * @see Object#equals(Object) + */ +public override int opEquals(Object o) { + return o is this; +} + +/** + * @see Object#toHash() + * Overridden for speed based on equality. + */ +public override hash_t toHash() { + return hash; +} + +void setDimensions(int location, int rowHeight) { + this.height = rowHeight; + this.location = location; + for (int i = 0; i < size(); i++) { + Node n = getNode(i); + n.y = location; + n.height = rowHeight; + } +} + +/** + * @deprecated Do not call + */ +public void sort() { } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/RankAssignmentSolver.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/RankAssignmentSolver.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,322 @@ +/******************************************************************************* + * Copyright (c) 2003, 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 + *******************************************************************************/ +module dwtx.draw2d.graph.RankAssignmentSolver; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.SpanningTreeVisitor; +import dwtx.draw2d.graph.EdgeList; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.NodeList; + +/** + * Assigns the final rank assignment for a DirectedGraph with an initial feasible + * spanning tree. + * @author Randy Hudson + * @since 2.1.2 + */ +class RankAssignmentSolver : SpanningTreeVisitor { + +DirectedGraph graph; +EdgeList spanningTree; +bool searchDirection; + +int depthFirstCutValue(Edge edge, int count) { + Node n = getTreeTail(edge); + setTreeMin(n, count); + int cutvalue = 0; + int multiplier = (edge.target is n) ? 1 : -1; + EdgeList list; + + list = n.outgoing; + Edge e; + for (int i = 0; i < list.size(); i++) { + e = list.getEdge(i); + if (e.tree && e !is edge) { + count = depthFirstCutValue(e, count); + cutvalue += (e.cut - e.weight) * multiplier; + } else { + cutvalue -= e.weight * multiplier; + } + } + list = n.incoming; + for (int i = 0; i < list.size(); i++) { + e = list.getEdge(i); + if (e.tree && e !is edge) { + count = depthFirstCutValue(e, count); + cutvalue -= (e.cut - e.weight) * multiplier; + } else { + cutvalue += e.weight * multiplier; + } + } + + edge.cut = cutvalue; + if (cutvalue < 0) + spanningTree.add(edge); + setTreeMax(n, count); + return count + 1; +} + +/** + * returns the Edge which should be entered. + * @param branch + * @return Edge + */ +Edge enter(Node branch) { + Node n; + Edge result = null; + int minSlack = Integer.MAX_VALUE; + bool incoming = getParentEdge(branch).target !is branch; +// searchDirection = !searchDirection; + for (int i = 0; i < graph.nodes.size(); i++) { + if (searchDirection) + n = graph.nodes.getNode(i); + else + n = graph.nodes.getNode(graph.nodes.size() - 1 - i); + if (subtreeContains(branch, n)) { + EdgeList edges; + if (incoming) + edges = n.incoming; + else + edges = n.outgoing; + for (int j = 0; j < edges.size(); j++) { + Edge e = edges.getEdge(j); + if (!subtreeContains(branch, e.opposite(n)) + && !e.tree + && e.getSlack() < minSlack) { + result = e; + minSlack = e.getSlack(); + } + } + } + } + return result; +} + +int getTreeMax(Node n) { + return n.workingInts[1]; +} + +int getTreeMin(Node n) { + return n.workingInts[0]; +} + +void initCutValues() { + Node root = graph.nodes.getNode(0); + spanningTree = new EdgeList(); + Edge e; + setTreeMin(root, 1); + setTreeMax(root, 1); + + for (int i = 0; i < root.outgoing.size(); i++) { + e = root.outgoing.getEdge(i); + if (!getSpanningTreeChildren(root).contains(e)) + continue; + setTreeMax(root, depthFirstCutValue(e, getTreeMax(root))); + } + for (int i = 0; i < root.incoming.size(); i++) { + e = root.incoming.getEdge(i); + if (!getSpanningTreeChildren(root).contains(e)) + continue; + setTreeMax(root, depthFirstCutValue(e, getTreeMax(root))); + } +} + +Edge leave() { + Edge result = null; + Edge e; + int minCut = 0; + int weight = -1; + for (int i = 0; i < spanningTree.size(); i++) { + e = spanningTree.getEdge(i); + if (e.cut < minCut) { + result = e; + minCut = result.cut; + weight = result.weight; + } else if (e.cut is minCut && e.weight > weight) { + result = e; + weight = result.weight; + } + } + return result; +} + +void networkSimplexLoop() { + Edge leave_, enter_; + int count = 0; + while ((leave_ = leave()) !is null && count < 900) { + + count++; + + Node leaveTail = getTreeTail(leave_); + Node leaveHead = getTreeHead(leave_); + + enter_ = enter(leaveTail); + if (enter_ is null) + break; + + //Break the "leave" edge from the spanning tree + getSpanningTreeChildren(leaveHead).remove(leave_); + setParentEdge(leaveTail, null); + leave_.tree = false; + spanningTree.remove(leave_); + + Node enterTail = enter_.source; + if (!subtreeContains(leaveTail, enterTail)) + //Oops, wrong end of the edge + enterTail = enter_.target; + Node enterHead = enter_.opposite(enterTail); + + //Prepare enterTail by making it the root of its sub-tree + updateSubgraph(enterTail); + + //Add "enter" edge to the spanning tree + getSpanningTreeChildren(enterHead).add(enter_); + setParentEdge(enterTail, enter_); + enter_.tree = true; + + repairCutValues(enter_); + + Node commonAncestor = enterHead; + + while (!subtreeContains(commonAncestor, leaveHead)) { + repairCutValues(getParentEdge(commonAncestor)); + commonAncestor = getTreeParent(commonAncestor); + } + while (leaveHead !is commonAncestor) { + repairCutValues(getParentEdge(leaveHead)); + leaveHead = getTreeParent(leaveHead); + } + updateMinMax(commonAncestor, getTreeMin(commonAncestor)); + tightenEdge(enter_); + } +} + +void repairCutValues(Edge edge) { + spanningTree.remove(edge); + Node n = getTreeTail(edge); + int cutvalue = 0; + int multiplier = (edge.target is n) ? 1 : -1; + EdgeList list; + + list = n.outgoing; + Edge e; + for (int i = 0; i < list.size(); i++) { + e = list.getEdge(i); + if (e.tree && e !is edge) + cutvalue += (e.cut - e.weight) * multiplier; + else + cutvalue -= e.weight * multiplier; + } + list = n.incoming; + for (int i = 0; i < list.size(); i++) { + e = list.getEdge(i); + if (e.tree && e !is edge) + cutvalue -= (e.cut - e.weight) * multiplier; + else + cutvalue += e.weight * multiplier; + } + + edge.cut = cutvalue; + if (cutvalue < 0) + spanningTree.add(edge); +} + +void setTreeMax(Node n, int value) { + n.workingInts[1] = value; +} + +void setTreeMin(Node n, int value) { + n.workingInts[0] = value; +} + +bool subtreeContains(Node parent, Node child) { + return parent.workingInts[0] <= child.workingInts[1] + && child.workingInts[1] <= parent.workingInts[1]; +} + +void tightenEdge(Edge edge) { + Node tail = getTreeTail(edge); + int delta = edge.getSlack(); + if (tail is edge.target) + delta = -delta; + Node n; + for (int i = 0; i < graph.nodes.size(); i++) { + n = graph.nodes.getNode(i); + if (subtreeContains(tail, n)) + n.rank += delta; + } +} + +int updateMinMax(Node root, int count) { + setTreeMin(root, count); + EdgeList edges = getSpanningTreeChildren(root); + for (int i = 0; i < edges.size(); i++) + count = updateMinMax(getTreeTail(edges.getEdge(i)), count); + setTreeMax(root, count); + return count + 1; +} + +void updateSubgraph(Node root) { + Edge flip = getParentEdge(root); + if (flip !is null) { + Node rootParent = getTreeParent(root); + getSpanningTreeChildren(rootParent).remove(flip); + updateSubgraph(rootParent); + setParentEdge(root, null); + setParentEdge(rootParent, flip); + repairCutValues(flip); + getSpanningTreeChildren(root).add(flip); + } +} + +public void visit(DirectedGraph graph) { + this.graph = graph; + initCutValues(); + networkSimplexLoop(); + if (graph.forestRoot is null) + graph.nodes.normalizeRanks(); + else + normalizeForest(); +} + +private void normalizeForest() { + NodeList tree = new NodeList(); + graph.nodes.resetFlags(); + graph.forestRoot.flag = true; + EdgeList rootEdges = graph.forestRoot.outgoing; + Stack stack = new Stack(); + for (int i = 0; i < rootEdges.size(); i++) { + Node node = rootEdges.getEdge(i).target; + node.flag = true; + stack.push(node); + while (!stack.isEmpty()) { + node = cast(Node) stack.pop(); + tree.add(node); + Iterator neighbors = node.iteratorNeighbors(); + while (neighbors.hasNext()) { + Node neighbor = cast(Node) neighbors.next(); + if (!neighbor.flag) { + neighbor.flag = true; + stack.push(neighbor); + } + } + } + tree.normalizeRanks(); + tree.clear(); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/RankList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/RankList.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.RankList; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.Rank; + +/** + * For internal use only. A list of lists. + * @author hudsonr + * @since 2.1.2 + */ +public final class RankList { + +ArrayList ranks; + +public this(){ + ranks = new ArrayList(); +} +/** + * Returns the specified rank. + * @param rank the row + * @return the rank + */ +public Rank getRank(int rank) { + while (ranks.size() <= rank) + ranks.add(new Rank()); + return cast(Rank)ranks.get(rank); +} + +/** + * Returns the total number or ranks. + * @return the total number of ranks + */ +public int size() { + return ranks.size(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/RankSorter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/RankSorter.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.RankSorter; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.dwtxhelper.Random; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Rank; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.EdgeList; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.GraphUtilities; + +/** + * Sorts Ranks during the up and down sweeps of the MinCross visitor. + * @author Randy Hudson + * @since 2.1.2 + */ +class RankSorter { + +Random flipflop; +Node node; +double rankSize, prevRankSize, nextRankSize; +int currentRow; +Rank rank; +double progress; +DirectedGraph g; + +public this(){ + flipflop = new Random(3); +} + +protected void assignIncomingSortValues() { + rankSize = rank.total; + prevRankSize = g.ranks.getRank(currentRow - 1).total; + if (currentRow < g.ranks.size() - 1) + nextRankSize = g.ranks.getRank(currentRow + 1).total; + for (int n = 0; n < rank.count(); n++) { + node = rank.getNode(n); + sortValueIncoming(); + } +} + +protected void assignOutgoingSortValues() { + rankSize = rank.total; + prevRankSize = g.ranks.getRank(currentRow + 1).total; + if (currentRow > 1) + nextRankSize = g.ranks.getRank(currentRow - 1).total; + + for (int n = 0; n < rank.count(); n++) { + node = rank.getNode(n); + sortValueOutgoing(); + } +} + +double evaluateNodeIncoming() { + bool change = false; + EdgeList incoming = node.incoming; + do { + change = false; + for (int i = 0; i < incoming.size() - 1; i++) { + if (incoming.getSourceIndex(i) > incoming.getSourceIndex(i + 1)) { + Edge e = incoming.getEdge(i); + incoming.set(i, incoming.get(i + 1)); + incoming.set(i + 1, e); + change = true; + } + } + } while (change); + + int n = incoming.size(); + if (n is 0) { + return node.index * prevRankSize / rankSize; + } + if (n % 2 is 1) + return incoming.getSourceIndex(n / 2); + + int l = incoming.getSourceIndex(n / 2 - 1); + int r = incoming.getSourceIndex(n / 2); + if (progress >= 0.8 && n > 2) { + int dl = l - incoming.getSourceIndex(0); + int dr = incoming.getSourceIndex(n - 1) - r; + if (dl < dr) + return l; + if (dl > dr) + return r; + } + if (progress > 0.25 && progress < 0.75) { + if (flipflop.nextBoolean()) + return (l + l + r) / 3.0; + else + return (r + r + l) / 3.0; + } + return (l + r) / 2.0; +} + +double evaluateNodeOutgoing() { + bool change = false; + EdgeList outgoing = node.outgoing; + do { + change = false; + for (int i = 0; i < outgoing.size() - 1; i++) { + if (outgoing.getTargetIndex(i) > outgoing.getTargetIndex(i + 1)) { + Edge e = outgoing.getEdge(i); + outgoing.set(i, outgoing.get(i + 1)); + outgoing.set(i + 1, e); + change = true; + } + } + } while (change); + + int n = outgoing.size(); + if (n is 0) + return node.index * prevRankSize / rankSize; + if (n % 2 is 1) + return outgoing.getTargetIndex(n / 2); + int l = outgoing.getTargetIndex(n / 2 - 1); + int r = outgoing.getTargetIndex(n / 2); + if (progress >= 0.8 && n > 2) { + int dl = l - outgoing.getTargetIndex(0); + int dr = outgoing.getTargetIndex(n - 1) - r; + if (dl < dr) + return l; + if (dl > dr) + return r; + } + if (progress > 0.25 && progress < 0.75) { + if (flipflop.nextBoolean()) + return (l + l + r) / 3.0; + else + return (r + r + l) / 3.0; + } + return (l + r) / 2.0; +} + +public void sortRankIncoming(DirectedGraph g, Rank rank, int row, double progress) { + this.currentRow = row; + this.rank = rank; + this.progress = progress; + assignIncomingSortValues(); + sort(); + postSort(); +} + +public void init(DirectedGraph g) { + this.g = g; + for (int i = 0; i < g.ranks.size(); i++) { + rank = g.ranks.getRank(i); + + //Sort the ranks based on their constraints. Constraints are preserved throughout. + Collections.sort(rank, new class() Comparator { + public int compare(Object left, Object right) { + return (cast(Node)left).rowOrder - (cast(Node)right).rowOrder; + } + }); + postSort(); + } +} + +void optimize(DirectedGraph g) { +} + +protected void postSort() { + rank.assignIndices(); +} + +void sort() { + bool change; + do { + change = false; + for (int i = 0; i < rank.size() - 1; i++) + change |= swap(i); + if (!change) + break; + change = false; + for (int i = rank.size() - 2; i >= 0; i--) + change |= swap(i); + } while (change); +} + +bool swap(int i) { + Node left = rank.getNode(i); + Node right = rank.getNode(i + 1); + if (GraphUtilities.isConstrained(left, right)) + return false; + if (left.sortValue <= right.sortValue) + return false; + rank.set(i, right); + rank.set(i + 1, left); + return true; +} + +public void sortRankOutgoing(DirectedGraph g, Rank rank, int row, double progress) { + this.currentRow = row; + this.rank = rank; + this.progress = progress; + assignOutgoingSortValues(); + sort(); + postSort(); +} + +void sortValueIncoming() { + node.sortValue = evaluateNodeIncoming(); + //$TODO restore this optimization +// if (progress is 0.0 && !(node instanceof VirtualNode)) +// node.sortValue = -1; + double value = evaluateNodeOutgoing(); + if (value < 0) + value = node.index * nextRankSize / rankSize; + node.sortValue += value * progress; +// if (progress < 0.7 && node.sortValue !is -1) +// node.sortValue += Math.random() * rankSize / (5 + 8 * progress); +} + +void sortValueOutgoing() { + node.sortValue = evaluateNodeOutgoing(); + //$TODO restore this optimization +// if (progress is 0.0 && !(node instanceof VirtualNode)) +// node.sortValue = -1; + double value = evaluateNodeIncoming(); + if (value < 0) + value = node.index * nextRankSize / rankSize; + node.sortValue += value * progress; +// if (progress < 0.7 && node.sortValue !is -1) +// node.sortValue += Math.random() * rankSize / (5 + 8 * progress); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/RevertableChange.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/RevertableChange.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.graph.RevertableChange; + +import dwt.dwthelper.utils; + +/** + * @since 3.1 + */ +class RevertableChange { + +void revert() { } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/RouteEdges.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/RouteEdges.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.RouteEdges; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.SubgraphBoundary; +import dwtx.draw2d.graph.ShortestPathRouter; +import dwtx.draw2d.graph.Path; +import dwtx.draw2d.graph.VirtualNode; +import dwtx.draw2d.graph.Node; + +/** + * @author Randy Hudson + */ +class RouteEdges : GraphVisitor { + +/** + * @see GraphVisitor#visit(DirectedGraph) + */ +public void revisit(DirectedGraph g) { + for (int i = 0; i < g.edges.size(); i++) { + Edge edge = cast(Edge)g.edges.get(i); + edge.start = new Point( + edge.getSourceOffset() + edge.source.x, + edge.source.y + edge.source.height); + if (auto boundary = cast(SubgraphBoundary)edge.source ) { + if (boundary.getParent().head is boundary) + edge.start.y = boundary.getParent().y + boundary.getParent().insets.top; + } + edge.end = new Point( + edge.getTargetOffset() + edge.target.x, + edge.target.y); + + if (edge.vNodes !is null) + routeLongEdge(edge, g); + else { + PointList list = new PointList(); + list.addPoint(edge.start); + list.addPoint(edge.end); + edge.setPoints(list); + } + } +} + +static void routeLongEdge(Edge edge, DirectedGraph g) { + ShortestPathRouter router = new ShortestPathRouter(); + Path path = new Path(edge.start, edge.end); + router.addPath(path); + Rectangle o; + Insets padding; + for (int i = 0; i < edge.vNodes.size(); i++) { + VirtualNode node = cast(VirtualNode)edge.vNodes.get(i); + Node neighbor; + if (node.left !is null) { + neighbor = node.left; + o = new Rectangle(neighbor.x, neighbor.y, neighbor.width, neighbor.height); + padding = g.getPadding(neighbor); + o.width += padding.right + padding.left; + o.width += (edge.getPadding() * 2); + o.x -= (padding.left + edge.getPadding()); + o.union_(o.getLocation().translate(-100000, 2)); + router.addObstacle(o); + } + if (node.right !is null) { + neighbor = node.right; + o = new Rectangle(neighbor.x, neighbor.y, neighbor.width, neighbor.height); + padding = g.getPadding(neighbor); + o.width += padding.right + padding.left; + o.width += (edge.getPadding() * 2); + o.x -= (padding.left + edge.getPadding()); + o.union_(o.getLocation().translate(100000, 2)); + router.addObstacle(o); + } + } + router.setSpacing(0); + router.solve(); + edge.setPoints(path.getPoints()); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/Segment.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/Segment.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.Segment; + +import dwt.dwthelper.utils; +import tango.text.convert.Format; + +import dwtx.draw2d.geometry.Geometry; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.graph.Vertex; + +/** + * A Segment representation for the ShortestPathRouting. A segment is a line between + * two vertices. + * + * This class is for internal use only + * @author Whitney Sorenson + * @since 3.0 + */ +class Segment { + +Vertex start, end; + +/** + * Creates a segment between the given start and end points. + * @param start the start vertex + * @param end the end vertex + */ +this(Vertex start, Vertex end) { + this.start = start; + this.end = end; +} + +/** + * Returns the cosine of the made between this segment and the given segment + * @param otherSegment the other segment + * @return cosine value (not arc-cos) + */ +double cosine(Segment otherSegment) { + double cos = (((start.x - end.x) * (otherSegment.end.x - otherSegment.start.x)) + + ((start.y - end.y) * (otherSegment.end.y - otherSegment.start.y))) + / (getLength() * otherSegment.getLength()); + double sin = (((start.x - end.x) * (otherSegment.end.y - otherSegment.start.y)) + - ((start.y - end.y) * (otherSegment.end.x - otherSegment.start.x))); + if (sin < 0.0) + return (1 + cos); + + return -(1 + cos); +} + +/** + * Returns the cross product of this segment and the given segment + * @param otherSegment the other segment + * @return the cross product + */ +long crossProduct(Segment otherSegment) { + return (((start.x - end.x) * (otherSegment.end.y - end.y)) + - ((start.y - end.y) * (otherSegment.end.x - end.x))); +} + +private double getLength() { + return (end.getDistance(start)); +} + +/** + * Returns a number that represents the sign of the slope of this segment. It does + * not return the actual slope. + * @return number representing sign of the slope + */ +double getSlope() { + if (end.x - start.x >= 0) + return (end.y - start.y); + else + return -(end.y - start.y); +} + +/** + * Returns true if the given segment intersects this segment. + * @param sx start x + * @param sy start y + * @param tx end x + * @param ty end y + * @return true if the segments intersect + */ +bool intersects(int sx, int sy, int tx, int ty) { + return Geometry.linesIntersect(start.x, start.y, end.x, end.y, sx, sy, tx, ty); +} + +/** + * Return true if the segment represented by the points intersects this segment. + * @param s start point + * @param t end point + * @return true if the segments intersect + */ +bool intersects(Point s, Point t) { + return intersects(s.x, s.y, t.x, t.y); +} + +/** + * @see java.lang.Object#toString() + */ +public String toString() { + return Format( "{}---{}", start, end ); //$NON-NLS-1$ +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/ShortestPathRouter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/ShortestPathRouter.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,909 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.ShortestPathRouter; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.PositionConstants; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.graph.Path; +import dwtx.draw2d.graph.Vertex; +import dwtx.draw2d.graph.Segment; +import dwtx.draw2d.graph.Obstacle; + +/** + * Bends a collection of {@link Path Paths} around rectangular obstacles. This class + * maintains a list of paths and obstacles. Updates can be made to the paths and/or + * obstacles, and then an incremental solve can be invoked. + *

+ * The algorithm will attempt to find the shortest non-intersecting path between each + * path's start and end points. Once all paths have been found, they will be offset based + * on how many paths bend around the same corner of each obstacle. + *

+ * The worst-case performance of this algorithm is p * s * n^2, where p is the number of + * paths, n is the number of obstacles, and s is the average number of segments in each + * path's final solution. + *

+ * This class is not intended to be subclassed. + * @author Whitney Sorenson + * @author Randy Hudson + * @since 3.0 + */ +public class ShortestPathRouter { + +/** + * A stack of Paths. + */ +static class PathStack : ArrayList { + + Path pop() { + return cast(Path)remove(size() - 1); + } + + void push(Path path) { + add(path); + } +} + +/** + * The number of times to grow obstacles and test for intersections. This is a tradeoff + * between performance and quality of output. + */ +private static const int NUM_GROW_PASSES = 2; + +private int spacing = 4; +private bool growPassChangedObstacles; +private List orderedPaths; +private Map pathsToChildPaths; + +private PathStack stack; +private List subPaths; + +private List userObstacles; +private List userPaths; +private List workingPaths; + +/** + * Creates a new shortest path routing. + */ +public this() { + userPaths = new ArrayList(); + workingPaths = new ArrayList(); + pathsToChildPaths = new HashMap(); + userObstacles = new ArrayList(); +} + +/** + * Adds an obstacle with the given bounds to the obstacles. + * + * @param rect the bounds of this obstacle + * @return true if the added obstacle has dirtied one or more paths + */ +public bool addObstacle(Rectangle rect) { + return internalAddObstacle(new Obstacle(rect, this)); +} + +/** + * Adds a path to the routing. + * @param path the path to add. + */ +public void addPath(Path path) { + userPaths.add(path); + workingPaths.add(path); +} + +/** + * Fills the point lists of the Paths to the correct bent points. + */ +private void bendPaths() { + for (int i = 0; i < orderedPaths.size(); i++) { + Path path = cast(Path) orderedPaths.get(i); + Segment segment = null; + path.points.addPoint(new Point(path.start.x, path.start.y)); + for (int v = 0; v < path.grownSegments.size(); v++) { + segment = cast(Segment) path.grownSegments.get(v); + Vertex vertex = segment.end; + + if (vertex !is null && v < path.grownSegments.size() - 1) { + if (vertex.type is Vertex.INNIE) { + vertex.count++; + path.points.addPoint(vertex.bend(vertex.count)); + } else { + path.points.addPoint(vertex.bend(vertex.totalCount)); + vertex.totalCount--; + } + } + } + path.points.addPoint(new Point(path.end.x, path.end.y)); + } +} + +/** + * Checks a vertex to see if its offset should shrink + * @param vertex the vertex to check + */ +private void checkVertexForIntersections(Vertex vertex) { + if (vertex.nearestObstacle !is 0 || vertex.nearestObstacleChecked) + return; + int sideLength, x, y; + + sideLength = 2 * (vertex.totalCount * getSpacing()) + 1; + + if ((vertex.positionOnObstacle & PositionConstants.NORTH) > 0) + y = vertex.y - sideLength; + else + y = vertex.y; + if ((vertex.positionOnObstacle & PositionConstants.EAST) > 0) + x = vertex.x; + else + x = vertex.x - sideLength; + + Rectangle r = new Rectangle(x, y, sideLength, sideLength); + + int xDist, yDist; + + for (int o = 0; o < userObstacles.size(); o++) { + Obstacle obs = cast(Obstacle)userObstacles.get(o); + if (obs !is vertex.obs && r.intersects(obs)) { + int pos = obs.getPosition(vertex); + if (pos is 0) + continue; + + if ((pos & PositionConstants.NORTH) > 0) + // use top + yDist = obs.y - vertex.y; + else + // use bottom + yDist = vertex.y - obs.bottom() + 1; + if ((pos & PositionConstants.EAST) > 0) + // use right + xDist = vertex.x - obs.right() + 1; + else + // use left + xDist = obs.x - vertex.x; + + if (Math.max(xDist, yDist) < vertex.nearestObstacle + || vertex.nearestObstacle is 0) { + vertex.nearestObstacle = Math.max(xDist, yDist); + vertex.updateOffset(); + } + + } + } + + vertex.nearestObstacleChecked = true; +} + +/** + * Checks all vertices along paths for intersections + */ +private void checkVertexIntersections() { + for (int i = 0; i < workingPaths.size(); i++) { + Path path = cast(Path)workingPaths.get(i); + + for (int s = 0; s < path.segments.size() - 1; s++) { + Vertex vertex = (cast(Segment)path.segments.get(s)).end; + checkVertexForIntersections(vertex); + } + } +} + +/** + * Frees up fields which aren't needed between invocations. + */ +private void cleanup() { + for (int i = 0; i < workingPaths.size(); i++) { + Path path = cast(Path)workingPaths.get(i); + path.cleanup(); + } +} + +/** + * Counts how many paths are on given vertices in order to increment their total count. + */ +private void countVertices() { + for (int i = 0; i < workingPaths.size(); i++) { + Path path = cast(Path) workingPaths.get(i); + for (int v = 0; v < path.segments.size() - 1; v++) + (cast(Segment)path.segments.get(v)).end.totalCount++; + } +} + +/** + * Dirties the paths that are on the given vertex + * @param vertex the vertex that has the paths + */ +private bool dirtyPathsOn(Vertex vertex) { + List paths = vertex.paths; + if (paths !is null && paths.size() !is 0) { + for (int i = 0; i < paths.size(); i++) + (cast(Path)paths.get(i)).isDirty = true; + return true; + } + return false; +} + +/** + * Resyncs the parent paths with any new child paths that are necessary because bendpoints + * have been added to the parent path. + * +private void generateChildPaths() { + for (int i = 0; i < userPaths.size(); i++) { + Path path = (Path)userPaths.get(i); + PointList bendPoints = path.bendpoints; + if (bendPoints !is null && bendPoints.size() !is 0) { + List childPaths = new ArrayList(bendPoints.size() + 1); + Path child = null; + Vertex prevVertex = path.start; + Vertex currVertex = null; + + for (int b = 0; b < bendPoints.size(); b++) { + Point bp = (Point)bendPoints.getPoint(b); + currVertex = new Vertex(bp, null); + child = new Path(prevVertex, currVertex); + childPaths.add(child); + workingPaths.add(child); + prevVertex = currVertex; + } + + child = new Path(prevVertex, path.end); + childPaths.add(child); + workingPaths.add(child); + pathsToChildPaths.put(path, childPaths); + } else + workingPaths.add(path); + } //End FOR +}*/ + +/** + * Returns the closest vertex to the given segment. + * @param v1 the first vertex + * @param v2 the second vertex + * @param segment the segment + * @return v1, or v2 whichever is closest to the segment + */ +private Vertex getNearestVertex(Vertex v1, Vertex v2, Segment segment) { + if (segment.start.getDistance(v1) + segment.end.getDistance(v1) + > segment.start.getDistance(v2) + segment.end.getDistance(v2)) + return v2; + else + return v1; +} + +/** + * Returns the spacing maintained between paths. + * @return the default path spacing + * @see #setSpacing(int) + * @since 3.2 + */ +public int getSpacing() { + return spacing; +} + +/** + * Returns the subpath for a split on the given path at the given segment. + * @param path the path + * @param segment the segment + * @return the new subpath + */ +private Path getSubpathForSplit(Path path, Segment segment) { + Path newPath = path.getSubPath(segment); + workingPaths.add(newPath); + subPaths.add(newPath); + return newPath; +} + +/** + * Grows all obstacles in in routing and tests for new intersections + */ +private void growObstacles() { + growPassChangedObstacles = false; + for (int i = 0; i < NUM_GROW_PASSES; i++) { + if (i is 0 || growPassChangedObstacles) + growObstaclesPass(); + } +} + +/** + * Performs a single pass of the grow obstacles step, this can be repeated as desired. + * Grows obstacles, then tests paths against the grown obstacles. + */ +private void growObstaclesPass() { + // grow obstacles + for (int i = 0; i < userObstacles.size(); i++) + (cast(Obstacle)userObstacles.get(i)).growVertices(); + + // go through paths and test segments + for (int i = 0; i < workingPaths.size(); i++) { + Path path = cast(Path) workingPaths.get(i); + + for (int e = 0; e < path.excludedObstacles.size(); e++) + (cast(Obstacle)path.excludedObstacles.get(e)).exclude = true; + + if (path.grownSegments.size() is 0) { + for (int s = 0; s < path.segments.size(); s++) + testOffsetSegmentForIntersections(cast(Segment)path.segments.get(s), -1, path); + } else { + int counter = 0; + List currentSegments = new ArrayList(path.grownSegments); + for (int s = 0; s < currentSegments.size(); s++) + counter += testOffsetSegmentForIntersections(cast(Segment)currentSegments.get(s), s + counter, path); + } + + for (int e = 0; e < path.excludedObstacles.size(); e++) + (cast(Obstacle)path.excludedObstacles.get(e)).exclude = false; + + } + + // revert obstacles + for (int i = 0; i < userObstacles.size(); i++) + (cast(Obstacle)userObstacles.get(i)).shrinkVertices(); +} + +/** + * Adds an obstacle to the routing + * @param obs the obstacle + */ +private bool internalAddObstacle(Obstacle obs) { + userObstacles.add(obs); + return testAndDirtyPaths(obs); +} + +/** + * Removes an obstacle from the routing. + * @param rect the bounds of the obstacle + * @return the obstacle removed + */ +private bool internalRemoveObstacle(Rectangle rect) { + Obstacle obs = null; + int index = -1; + for (int i = 0; i < userObstacles.size(); i++) { + obs = cast(Obstacle)userObstacles.get(i); + if (obs.opEquals(rect)) { + index = i; + break; + } + } + + userObstacles.remove(index); + + bool result = false; + result |= dirtyPathsOn(obs.bottomLeft); + result |= dirtyPathsOn(obs.topLeft); + result |= dirtyPathsOn(obs.bottomRight); + result |= dirtyPathsOn(obs.topRight); + + for (int p = 0; p < workingPaths.size(); p++) { + Path path = cast(Path)workingPaths.get(p); + if (path.isDirty) + continue; + if (path.isObstacleVisible(obs)) + path.isDirty = result = true; + } + + return result; +} + +/** + * Labels the given path's vertices as innies, or outies, as well as determining if this + * path is inverted. + * @param path the path + */ +private void labelPath(Path path) { + Segment segment = null; + Segment nextSegment = null; + Vertex vertex = null; + bool agree = false; + for (int v = 0; v < path.grownSegments.size() - 1; v++) { + segment = cast(Segment) path.grownSegments.get(v); + nextSegment = cast(Segment) path.grownSegments.get(v + 1); + vertex = segment.end; + long crossProduct = segment.crossProduct(new Segment(vertex, vertex.obs.center)); + + if (vertex.type is Vertex.NOT_SET) { + labelVertex(segment, crossProduct, path); + } else if (!path.isInverted + && ((crossProduct > 0 && vertex.type is Vertex.OUTIE) + || (crossProduct < 0 && vertex.type is Vertex.INNIE))) { + if (agree) { + // split detected. + stack.push(getSubpathForSplit(path, segment)); + return; + } else { + path.isInverted = true; + path.invertPriorVertices(segment); + } + } else if (path.isInverted + && ((crossProduct < 0 && vertex.type is Vertex.OUTIE) + || (crossProduct > 0 && vertex.type is Vertex.INNIE))) { + // split detected. + stack.push(getSubpathForSplit(path, segment)); + return; + } else + agree = true; + + if (vertex.paths !is null) { + for (int i = 0;i < vertex.paths.size();i++) { + Path nextPath = cast(Path)vertex.paths.get(i); + if (!nextPath.isMarked) { + nextPath.isMarked = true; + stack.push(nextPath); + } + } + } + + vertex.addPath(path, segment, nextSegment); + } +} + +/** + * Labels all path's vertices in the routing. + */ +private void labelPaths() { + Path path = null; + for (int i = 0; i < workingPaths.size(); i++) { + path = cast(Path) workingPaths.get(i); + stack.push(path); + } + + while (!stack.isEmpty()) { + path = stack.pop(); + if (!path.isMarked) { + path.isMarked = true; + labelPath(path); + } + } + + // revert is marked so we can use it again in ordering. + for (int i = 0;i < workingPaths.size(); i++) { + path = cast(Path)workingPaths.get(i); + path.isMarked = false; + } +} + +/** + * Labels the vertex at the end of the semgent based on the cross product. + * @param segment the segment to this vertex + * @param crossProduct the cross product of this segment and a segment to the obstacles center + * @param path the path + */ +private void labelVertex(Segment segment, long crossProduct, Path path) { +// assumes vertex in question is segment.end + if (crossProduct > 0) { + if (path.isInverted) + segment.end.type = Vertex.OUTIE; + else + segment.end.type = Vertex.INNIE; + } else if (crossProduct < 0) { + if (path.isInverted) + segment.end.type = Vertex.INNIE; + else + segment.end.type = Vertex.OUTIE; + } else if (segment.start.type !is Vertex.NOT_SET) + segment.end.type = segment.start.type; + else + segment.end.type = Vertex.INNIE; +} + +/** + * Orders the path by comparing its angle at shared vertices with other paths. + * @param path the path + */ +private void orderPath(Path path) { + if (path.isMarked) + return; + path.isMarked = true; + Segment segment = null; + Vertex vertex = null; + for (int v = 0; v < path.grownSegments.size() - 1; v++) { + segment = cast(Segment) path.grownSegments.get(v); + vertex = segment.end; + double thisAngle = (cast(Double)vertex.cachedCosines.get(path)).doubleValue(); + if (path.isInverted) + thisAngle = -thisAngle; + + for (int i = 0; i < vertex.paths.size(); i++) { + Path vPath = cast(Path)vertex.paths.get(i); + if (!vPath.isMarked) { + double otherAngle = (cast(Double)vertex.cachedCosines.get(vPath)).doubleValue(); + + if (vPath.isInverted) + otherAngle = -otherAngle; + + if (otherAngle < thisAngle) + orderPath(vPath); + } + } + } + + orderedPaths.add(path); +} + +/** + * Orders all paths in the graph. + */ +private void orderPaths() { + for (int i = 0; i < workingPaths.size(); i++) { + Path path = cast(Path) workingPaths.get(i); + orderPath(path); + } +} + +/** + * Populates the parent paths with all the child paths that were created to represent + * bendpoints. + */ +private void recombineChildrenPaths() { + // only populate those paths with children paths. + Iterator keyItr = pathsToChildPaths.keySet().iterator(); + while (keyItr.hasNext()) { + Path path = cast(Path)keyItr.next(); + + path.fullReset(); + + List childPaths = cast(List)pathsToChildPaths.get(path); + Path childPath = null; + + for (int i = 0; i < childPaths.size(); i++) { + childPath = cast(Path)childPaths.get(i); + path.points.addAll(childPath.getPoints()); + // path will overlap + path.points.removePoint(path.points.size() - 1); + //path.grownSegments.addAll(childPath.grownSegments); + path.segments.addAll(childPath.segments); + path.visibleObstacles.addAll(childPath.visibleObstacles); + } + + // add last point. + path.points.addPoint(childPath.points.getLastPoint()); + } +} + +/** + * Reconnects all subpaths. + */ +private void recombineSubpaths() { + for (int p = 0; p < orderedPaths.size(); p++) { + Path path = cast(Path)orderedPaths.get(p); + path.reconnectSubPaths(); + } + + orderedPaths.removeAll(subPaths); + workingPaths.removeAll(subPaths); + subPaths = null; +} + +/** + * Removes the obstacle with the rectangle's bounds from the routing. + * + * @param rect the bounds of the obstacle to remove + * @return true if the removal has dirtied one or more paths + */ +public bool removeObstacle(Rectangle rect) { + return internalRemoveObstacle(rect); +} + +/** + * Removes the given path from the routing. + * + * @param path the path to remove. + * @return true if the removal may have affected one of the remaining paths + */ +public bool removePath(Path path) { + userPaths.remove(path); + List children = cast(List)pathsToChildPaths.get(path); + if (children is null) + workingPaths.remove(path); + else + workingPaths.removeAll(children); + return true; +} + +/** + * Resets exclude field on all obstacles + */ +private void resetObstacleExclusions() { + for (int i = 0; i < userObstacles.size(); i++) + (cast(Obstacle)userObstacles.get(i)).exclude = false; +} + +/** + * Resets all vertices found on paths and obstacles. + */ +private void resetVertices() { + for (int i = 0; i < userObstacles.size(); i++) { + Obstacle obs = cast(Obstacle)userObstacles.get(i); + obs.reset(); + } + for (int i = 0; i < workingPaths.size(); i++) { + Path path = cast(Path)workingPaths.get(i); + path.start.fullReset(); + path.end.fullReset(); + } +} + +/** + * Sets the default spacing between paths. The spacing is the minimum distance that path + * should be offset from other paths or obstacles. The default value is 4. When this value + * can not be satisfied, paths will be squeezed together uniformly. + * @param spacing the path spacing + * @since 3.2 + */ +public void setSpacing(int spacing) { + this.spacing = spacing; +} + +/** + * Updates the points in the paths in order to represent the current solution + * with the given paths and obstacles. + * + * @return returns the list of paths which were updated. + */ +public List solve() { + + solveDirtyPaths(); + + countVertices(); + checkVertexIntersections(); + growObstacles(); + + subPaths = new ArrayList(); + stack = new PathStack(); + labelPaths(); + stack = null; + + orderedPaths = new ArrayList(); + orderPaths(); + bendPaths(); + + recombineSubpaths(); + orderedPaths = null; + subPaths = null; + + recombineChildrenPaths(); + cleanup(); + + return Collections.unmodifiableList(userPaths); +} + +/** + * Solves paths that are dirty. + * @return number of dirty paths + */ +private int solveDirtyPaths() { + int numSolved = 0; + + for (int i = 0; i < userPaths.size(); i++) { + Path path = cast(Path)userPaths.get(i); + if (!path.isDirty) + continue; + List children = cast(List)pathsToChildPaths.get(path); + int prevCount = 1, newCount = 1; + if (children is null) + children = Collections.EMPTY_LIST; + else + prevCount = children.size(); + + if (path.getBendPoints() !is null) + newCount = path.getBendPoints().size() + 1; + + if (prevCount !is newCount) + children = regenerateChildPaths(path, children, prevCount, newCount); + refreshChildrenEndpoints(path, children); + } + + for (int i = 0; i < workingPaths.size(); i++) { + Path path = cast(Path)workingPaths.get(i); + path.refreshExcludedObstacles(userObstacles); + if (!path.isDirty) { + path.resetPartial(); + continue; + } + + numSolved++; + path.fullReset(); + + bool pathFoundCheck = path.generateShortestPath(userObstacles); + if (!pathFoundCheck || path.end.cost > path.threshold) { + // path not found, or path found was too long + resetVertices(); + path.fullReset(); + path.threshold = 0; + pathFoundCheck = path.generateShortestPath(userObstacles); + } + + resetVertices(); + } + + resetObstacleExclusions(); + + if (numSolved is 0) + resetVertices(); + + return numSolved; +} + +/** + * @since 3.0 + * @param path + * @param children + */ +private void refreshChildrenEndpoints(Path path, List children) { + Point previous = path.getStartPoint(); + Point next; + PointList bendpoints = path.getBendPoints(); + Path child; + + for (int i = 0; i < children.size(); i++) { + if (i < bendpoints.size()) + next = bendpoints.getPoint(i); + else + next = path.getEndPoint(); + child = cast(Path)children.get(i); + child.setStartPoint(previous); + child.setEndPoint(next); + previous = next; + } +} + +/** + * @since 3.0 + * @param path + * @param children + */ +private List regenerateChildPaths(Path path, List children, int currentSize, int newSize) { + //Path used to be simple but now is compound, children is EMPTY. + if (currentSize is 1) { + workingPaths.remove(path); + currentSize = 0; + children = new ArrayList(newSize); + pathsToChildPaths.put(path, cast(Object)children); + } else + //Path is becoming simple but was compound. children becomes empty. + if (newSize is 1) { + workingPaths.removeAll(children); + workingPaths.add(path); + pathsToChildPaths.remove(path); + return Collections.EMPTY_LIST; + } + + //Add new working paths until the sizes are the same + while (currentSize < newSize) { + Path child = new Path(); + workingPaths.add(child); + children.add(child); + currentSize++; + } + + while (currentSize > newSize) { + Path child = cast(Path)children.remove(children.size() - 1); + workingPaths.remove(child); + currentSize--; + } + + return children; +} + +/** + * Tests a segment that has been offset for new intersections + * @param segment the segment + * @param index the index of the segment along the path + * @param path the path + * @return 1 if new segments have been inserted + */ +private int testOffsetSegmentForIntersections(Segment segment, int index, Path path) { + for (int i = 0; i < userObstacles.size(); i++) { + Obstacle obs = cast(Obstacle) userObstacles.get(i); + + if (segment.end.obs is obs || segment.start.obs is obs || obs.exclude) + continue; + Vertex vertex = null; + + int offset = getSpacing(); + if (segment.getSlope() < 0) { + if (segment.intersects(obs.topLeft.x - offset, + obs.topLeft.y - offset, + obs.bottomRight.x + offset, + obs.bottomRight.y + offset)) + vertex = getNearestVertex(obs.topLeft, obs.bottomRight, segment); + else if (segment.intersects(obs.bottomLeft.x - offset, + obs.bottomLeft.y + offset, + obs.topRight.x + offset, + obs.topRight.y - offset)) + vertex = getNearestVertex(obs.bottomLeft, obs.topRight, segment); + } else { + if (segment.intersects(obs.bottomLeft.x - offset, + obs.bottomLeft.y + offset, + obs.topRight.x + offset, + obs.topRight.y - offset)) + vertex = getNearestVertex(obs.bottomLeft, obs.topRight, segment); + else if (segment.intersects(obs.topLeft.x - offset, + obs.topLeft.y - offset, + obs.bottomRight.x + offset, + obs.bottomRight.y + offset)) + vertex = getNearestVertex(obs.topLeft, obs.bottomRight, segment); + } + + if (vertex !is null) { + Rectangle vRect = vertex.getDeformedRectangle(offset); + if (segment.end.obs !is null) { + Rectangle endRect = segment.end.getDeformedRectangle(offset); + if (vRect.intersects(endRect)) + continue; + } + if (segment.start.obs !is null) { + Rectangle startRect = segment.start.getDeformedRectangle(offset); + if (vRect.intersects(startRect)) + continue; + } + + Segment newSegmentStart = new Segment(segment.start, vertex); + Segment newSegmentEnd = new Segment(vertex, segment.end); + + vertex.totalCount++; + vertex.nearestObstacleChecked = false; + + vertex.shrink(); + checkVertexForIntersections(vertex); + vertex.grow(); + + if (vertex.nearestObstacle !is 0) + vertex.updateOffset(); + + growPassChangedObstacles = true; + + if (index !is -1) { + path.grownSegments.remove(segment); + path.grownSegments.add(index, newSegmentStart); + path.grownSegments.add(index + 1, newSegmentEnd); + } else { + path.grownSegments.add(newSegmentStart); + path.grownSegments.add(newSegmentEnd); + } + return 1; + } + } + if (index is -1) + path.grownSegments.add(segment); + return 0; +} + +/** + * Tests all paths against the given obstacle + * @param obs the obstacle + */ +private bool testAndDirtyPaths(Obstacle obs) { + bool result = false; + for (int i = 0; i < workingPaths.size(); i++) { + Path path = cast(Path)workingPaths.get(i); + result |= path.testAndSet(obs); + } + return result; +} + +/** + * Updates the position of an existing obstacle. + * @param oldBounds the old bounds(used to find the obstacle) + * @param newBounds the new bounds + * @return true if the change the current results to become stale + */ +public bool updateObstacle(Rectangle oldBounds, Rectangle newBounds) { + bool result = internalRemoveObstacle(oldBounds); + result |= addObstacle(newBounds); + return result; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/SortSubgraphs.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/SortSubgraphs.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,238 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.SortSubgraphs; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.NestingTree; +import dwtx.draw2d.graph.CompoundDirectedGraph; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.NodePair; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.RankList; +import dwtx.draw2d.graph.Rank; +import dwtx.draw2d.graph.Subgraph; + +/** + * Performs a topological sort from left to right of the subgraphs in a compound directed + * graph. This ensures that subgraphs do not intertwine. + * @author Randy Hudson + * @since 2.1.2 + */ +class SortSubgraphs : GraphVisitor { + +CompoundDirectedGraph g; + +NestingTree[] nestingTrees; + +Set orderingGraphEdges; +Set orderingGraphNodes; +NodePair pair; + +public this(){ + orderingGraphEdges = new HashSet(); + orderingGraphNodes = new HashSet(); + pair = new NodePair(); +} + +private void breakSubgraphCycles() { + //The stack of nodes which have no unmarked incoming edges + List noLefts = new ArrayList(); + + int index = 1; + //Identify all initial nodes for removal + for (Iterator iter = orderingGraphNodes.iterator(); iter.hasNext();) { + Node node = cast(Node)iter.next(); + if (node.x is 0) + sortedInsert(noLefts, node); + } + + Node cycleRoot; + do { + //Remove all leftmost nodes, updating the nodes to their right + while (noLefts.size() > 0) { + Node node = cast(Node)noLefts.remove(noLefts.size() - 1); + node.sortValue = index++; + orderingGraphNodes.remove(node); +// System.out.println("removed:" + node); + NodeList rightOf = rightOf(node); + if (rightOf is null) + continue; + for (int i = 0; i < rightOf.size(); i++) { + Node right = rightOf.getNode(i); + right.x--; + if (right.x is 0) + sortedInsert(noLefts, right); + } + } + cycleRoot = null; + double min = Double.MAX_VALUE; + for (Iterator iter = orderingGraphNodes.iterator(); iter.hasNext();) { + Node node = cast(Node)iter.next(); + if (node.sortValue < min) { + cycleRoot = node; + min = node.sortValue; + } + } + if (cycleRoot !is null) { + //break the cycle; + sortedInsert(noLefts, cycleRoot); +// System.out.println("breaking cycle with:" + cycleRoot); +// Display.getCurrent().beep(); + cycleRoot.x = -1; //prevent x from ever reaching 0 + } // else if (OGmembers.size() > 0) + //System.out.println("FAILED TO FIND CYCLE ROOT"); //$NON-NLS-1$ + } while (cycleRoot !is null); +} + +private void buildSubgraphOrderingGraph() { + RankList ranks = g.ranks; + nestingTrees = new NestingTree[ranks.size()]; + for (int r = 0; r < ranks.size(); r++) { + NestingTree entry = NestingTree.buildNestingTreeForRank(ranks.getRank(r)); + nestingTrees[r] = entry; + entry.calculateSortValues(); + entry.recursiveSort(false); + } + + for (int i = 0; i < nestingTrees.length; i++) { + NestingTree entry = nestingTrees[i]; + buildSubgraphOrderingGraph(entry); + } +} + +private void buildSubgraphOrderingGraph(NestingTree entry) { + NodePair pair = new NodePair(); + if (entry.isLeaf) + return; + for (int i = 0; i < entry.contents.size(); i++) { + Object right = entry.contents.get(i); + if (auto r = cast(Node)right ) + pair.n2 = r; + else { + pair.n2 = (cast(NestingTree)right).subgraph; + buildSubgraphOrderingGraph(cast(NestingTree)right); + } + if (pair.n1 !is null && !orderingGraphEdges.contains(pair)) { + orderingGraphEdges.add(pair); + leftToRight(pair.n1, pair.n2); + orderingGraphNodes.add(pair.n1); + orderingGraphNodes.add(pair.n2); + pair.n2.x++; //Using x field to count predecessors. + pair = new NodePair(pair.n2, null); + } else { + pair.n1 = pair.n2; + } + } +} + +/** + * Calculates the average position P for each node and subgraph. The average position is + * stored in the sortValue for each node or subgraph. + * + * Runs in approximately linear time with respect to the number of nodes, including + * virtual nodes. + */ +private void calculateSortValues() { + RankList ranks = g.ranks; + + g.subgraphs.resetSortValues(); + g.subgraphs.resetIndices(); + + /* + * For subgraphs, the sum of all positions is kept, along with the number of + * contributions, which is tracked in the subgraph's index field. + */ + for (int r = 0; r < ranks.size(); r++) { + Rank rank = ranks.getRank(r); + for (int j = 0; j < rank.count(); j++) { + Node node = rank.getNode(j); + node.sortValue = node.index; + Subgraph parent = node.getParent(); + while (parent !is null) { + parent.sortValue += node.sortValue; + parent.index++; + parent = parent.getParent(); + } + } + } + + /* + * For each subgraph, divide the sum of the positions by the number of contributions, + * to give the average position. + */ + for (int i = 0; i < g.subgraphs.size(); i++) { + Subgraph subgraph = cast(Subgraph)g.subgraphs.get(i); + subgraph.sortValue /= subgraph.index; + } +} + +private void repopulateRanks() { + for (int i = 0; i < nestingTrees.length; i++) { + Rank rank = g.ranks.getRank(i); + rank.clear(); + nestingTrees[i].repopulateRank(rank); + } +} + +private NodeList rightOf(Node left) { + return cast(NodeList)left.workingData[0]; +} + +private void leftToRight(Node left, Node right) { + rightOf(left).add(right); +} + +void sortedInsert(List list, Node node) { + int insert = 0; + while (insert < list.size() + && (cast(Node)list.get(insert)).sortValue > node.sortValue) + insert++; + list.add(insert, node); +} + +private void topologicalSort() { + for (int i = 0; i < nestingTrees.length; i++) { + nestingTrees[i].getSortValueFromSubgraph(); + nestingTrees[i].recursiveSort(false); + } +} + +void init() { + for (int r = 0; r < g.ranks.size(); r++) { + Rank rank = g.ranks.getRank(r); + for (int i = 0; i < rank.count(); i++) { + Node n = cast(Node)rank.get(i); + n.workingData[0] = new NodeList(); + } + } + for (int i = 0; i < g.subgraphs.size(); i++) { + Subgraph s = cast(Subgraph)g.subgraphs.get(i); + s.workingData[0] = new NodeList(); + } +} + +public void visit(DirectedGraph dg) { + g = cast(CompoundDirectedGraph)dg; + + init(); + buildSubgraphOrderingGraph(); + calculateSortValues(); + breakSubgraphCycles(); + topologicalSort(); + repopulateRanks(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/SpanningTreeVisitor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/SpanningTreeVisitor.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.SpanningTreeVisitor; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.EdgeList; + +/** + * A base class for visitors which operate on the graphs spanning tree used to induce rank + * assignments. + * @author Randy Hudson + * @since 2.1.2 + */ +abstract class SpanningTreeVisitor : GraphVisitor { + +Edge getParentEdge(Node node) { + return cast(Edge)node.workingData[1]; +} + +EdgeList getSpanningTreeChildren(Node node) { + return cast(EdgeList)node.workingData[0]; +} + +protected Node getTreeHead(Edge edge) { + if (getParentEdge(edge.source) is edge) + return edge.target; + return edge.source; +} + +Node getTreeParent(Node node) { + Edge e = getParentEdge(node); + if (e is null) + return null; + return e.opposite(node); +} + +protected Node getTreeTail(Edge edge) { + if (getParentEdge(edge.source) is edge) + return edge.source; + return edge.target; +} + +void setParentEdge(Node node, Edge edge) { + node.workingData[1] = edge; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/Subgraph.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/Subgraph.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.Subgraph; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.NodeList; + + +/** + * A Node which may contain other nodes. A Subgraph is a compound or container node. It + * may have incoming and outgoing edges just like a node. Subgraphs are used in {@link + * CompoundDirectedGraph}s. A proper layout of a compound graph ensures that all of a + * subgraph's children are placed inside its rectangular region. Nodes which do not + * belong to the subgraph must be placed outside that region. + *

+ * A Subgraph may contain another Subgraph. + *

+ * A Subgraph has additional geometric properties which describe the containing box. They + * are: + *

    + *
  • {@link #insets} - the size of the subgraph's border. A subgraph is typically + * rendered as a thin rectangular box. Sometimes this box is labeled or decorated. The + * insets can be used to reserve space for this purpose. + *
  • {@link #innerPadding} - the amount of empty space that must be preserved just + * inside the subgraph's border. This is the minimum space between the border, and the + * children node's contained inside the subgraph. + *
+ * + * @author hudsonr + * @since 2.1.2 + */ +public class Subgraph : Node { + +/** + * The children of this subgraph. Nodes may not belong to more than one subgraph. + */ +public NodeList members; + +Node head; +Node tail; +Node left; +Node right; +int nestingTreeMin; + +/** + * The space required for this subgraph's border. The default value is undefined. + */ +public Insets insets; + +/** + * The minimum space between this subgraph's border and it's children. + */ +public Insets innerPadding; + +private static const Insets NO_INSETS; +static this(){ + NO_INSETS = new Insets(); +} +/** + * Constructs a new subgraph with the given data object. + * @see Node#Node(Object) + * @param data an arbitrary data object + */ +public this(Object data) { + this(data, null); +} + +/** + * Constructs a new subgraph with the given data object and parent subgraph. + * @see Node#Node(Object, Subgraph) + * @param data an arbitrary data object + * @param parent the parent + */ +public this(Object data, Subgraph parent) { + super(data, parent); + innerPadding = NO_INSETS; + members = new NodeList(); + insets = new Insets(1); +} + +/** + * Adds the given node to this subgraph. + * @param n the node to add + */ +public void addMember(Node n) { + members.add(n); +} + +/** + * Returns true if the given node is contained inside the branch represented + * by this subgraph. + * @param n the node in question + * @return true if nested + */ +bool isNested(Node n) { + return n.nestingIndex >= nestingTreeMin + && n.nestingIndex <= nestingIndex; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/SubgraphBoundary.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/SubgraphBoundary.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.SubgraphBoundary; + +import dwt.dwthelper.utils; +import tango.text.convert.Format; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Subgraph; + +/** + * For INTERNAL use only. + * @author hudsonr + * @since 2.1.2 + */ +class SubgraphBoundary : Node { + +/** + * constant indicating TOP. + */ +public static const int TOP = 0; + +/** + * constant indicating LEFT. + */ +public static const int LEFT = 1; + +/** + * constant indicating BOTTOM. + */ +public static const int BOTTOM = 2; + +/** + * constant indicating RIGHT. + */ +public static const int RIGHT = 3; + +/** + * Constructs a new boundary. + * @param s the subgraph + * @param p the padding + * @param side which side + */ +public this(Subgraph s, Insets p, int side) { + super(null, s); + this.width = s.width; + this.height = s.height; + this.padding = new Insets(); + switch (side) { + case LEFT : + width = s.insets.left; + y = s.y; + padding.left = p.left; + padding.right = s.innerPadding.left; + padding.top = padding.bottom = 0; + setParent(s.getParent()); + data = stringcast(Format("left({})", s )); //$NON-NLS-1$ //$NON-NLS-2$ + break; + case RIGHT : + width = s.insets.right; + y = s.y; + padding.right = p.right; + padding.left = s.innerPadding.right; + padding.top = padding.bottom = 0; + setParent(s.getParent()); + data = stringcast(Format("right({})", s )); //$NON-NLS-1$ //$NON-NLS-2$ + break; + case TOP : + height = s.insets.top; + //$TODO width of head/tail should be 0 + width = 5; + padding.top = p.top; + padding.bottom = s.innerPadding.top; + padding.left = padding.right = 0; + data = stringcast(Format("top({})", s )); //$NON-NLS-1$ //$NON-NLS-2$ + break; + case BOTTOM : + height = s.insets.bottom; + //$TODO width of head/tail should be 0 + width = 5; + padding.top = s.innerPadding.bottom; + padding.bottom = p.bottom; + padding.left = padding.right = 0; + data = stringcast(Format("bottom({})", s )); //$NON-NLS-1$ //$NON-NLS-2$ + break; + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/TightSpanningTreeSolver.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/TightSpanningTreeSolver.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.TightSpanningTreeSolver; + +import dwt.dwthelper.utils; +import dwtx.draw2d.graph.SpanningTreeVisitor; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.EdgeList; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.NodeList; + +/** + * Finds a tight spanning tree from the graphs edges which induce a valid rank assignment. + * This process requires that the nodes be initially given a feasible ranking. + * @author Randy Hudson + * @since 2.1.2 + */ +class TightSpanningTreeSolver : SpanningTreeVisitor { + +protected DirectedGraph graph; +protected CandidateList candidates; + +static final class CandidateList { + private Edge[] edges; + private int size_; + + public this(){ + edges = new Edge[10]; + } + public void add(Edge edge) { + if (size_ is edges.length - 1) { + Edge newEdges[] = new Edge[edges.length * 2]; + System.arraycopy(edges, 0, newEdges, 0, edges.length); + edges = newEdges; + } + edges[size_++] = edge; + } + + public Edge getEdge(int index) { + return edges[index]; + } + + public void remove(Edge edge) { + for (int i = 0; i < size_; i++) { + if (edges[i] is edge) { + edges[i] = edges[size_ - 1]; + size_--; + return; + } + } + throw new RuntimeException("Remove called on invalid Edge"); //$NON-NLS-1$ + } + + public int size() { + return size_; + } +} + +protected NodeList members; + +public this(){ + candidates = new CandidateList(); + members = new NodeList(); +} + +public void visit(DirectedGraph graph) { + this.graph = graph; + init(); + solve(); +} + +Node addEdge(Edge edge) { + int delta = edge.getSlack(); + edge.tree = true; + Node node; + if (edge.target.flag) { + delta = -delta; + node = edge.source; + setParentEdge(node, edge); + getSpanningTreeChildren(edge.target).add(edge); + } else { + node = edge.target; + setParentEdge(node, edge); + getSpanningTreeChildren(edge.source).add(edge); + } + members.adjustRank(delta); + addNode(node); + return node; +} + +private bool isNodeReachable(Node node) { + return node.flag; +} + +private void setNodeReachable(Node node) { + node.flag = true; +} + +private bool isCandidate(Edge e) { + return e.flag; +} + +private void setCandidate(Edge e) { + e.flag = true; +} + +void addNode(Node node) { + setNodeReachable(node); + EdgeList list = node.incoming; + Edge e; + for (int i = 0; i < list.size(); i++) { + e = list.getEdge(i); + if (!isNodeReachable(e.source)) { + if (!isCandidate(e)) { + setCandidate(e); + candidates.add(e); + } + } else + candidates.remove(e); + } + + list = node.outgoing; + for (int i = 0; i < list.size(); i++) { + e = list.getEdge(i); + if (!isNodeReachable(e.target)) { + if (!isCandidate(e)) { + setCandidate(e); + candidates.add(e); + } + } else + candidates.remove(e); + } + members.add(node); +} + +void init() { + graph.edges.resetFlags(true); + graph.nodes.resetFlags(); + for (int i = 0; i < graph.nodes.size(); i++) { + Node node = cast(Node)graph.nodes.get(i); + node.workingData[0] = new EdgeList(); + } +} + +protected void solve() { + Node root = graph.nodes.getNode(0); + setParentEdge(root, null); + addNode(root); + while (members.size() < graph.nodes.size()) { + if (candidates.size() is 0) + throw new RuntimeException("graph is not fully connected");//$NON-NLS-1$ + int minSlack = Integer.MAX_VALUE, slack; + Edge minEdge = null, edge; + for (int i = 0; i < candidates.size() && minSlack > 0; i++) { + edge = candidates.getEdge(i); + slack = edge.getSlack(); + if (slack < minSlack) { + minSlack = slack; + minEdge = edge; + } + } + addEdge(minEdge); + } + graph.nodes.normalizeRanks(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/TransposeMetrics.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/TransposeMetrics.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 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 + *******************************************************************************/ + +module dwtx.draw2d.graph.TransposeMetrics; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.PositionConstants; +import dwtx.draw2d.geometry.Transposer; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.VirtualNode; + +class TransposeMetrics : GraphVisitor { + +Transposer t; + +public this(){ + t = new Transposer(); +} + +public void visit(DirectedGraph g) { + if (g.getDirection() is PositionConstants.SOUTH) + return; + t.setEnabled(true); + int temp; + g.setDefaultPadding(t.t(g.getDefaultPadding())); + for (int i = 0; i < g.nodes.size(); i++) { + Node node = g.nodes.getNode(i); + temp = node.width; + node.width = node.height; + node.height = temp; + if (node.getPadding() !is null) + node.setPadding(t.t(node.getPadding())); + } +} + +public void revisit(DirectedGraph g) { + if (g.getDirection() is PositionConstants.SOUTH) + return; + int temp; + g.setDefaultPadding(t.t(g.getDefaultPadding())); + for (int i = 0; i < g.nodes.size(); i++) { + Node node = cast(Node)g.nodes.get(i); + temp = node.width; + node.width = node.height; + node.height = temp; + temp = node.y; + node.y = node.x; + node.x = temp; + if (node.getPadding() !is null) + node.setPadding(t.t(node.getPadding())); + } + for (int i = 0; i < g.edges.size(); i++) { + Edge edge = g.edges.getEdge(i); + edge.start.transpose(); + edge.end.transpose(); + edge.getPoints().transpose(); + List bends = edge.vNodes; + if (bends is null) + continue; + for (int b = 0; b < bends.size(); b++) { + VirtualNode vnode = cast(VirtualNode) bends.get(b); + temp = vnode.y; + vnode.y = vnode.x; + vnode.x = temp; + temp = vnode.width; + vnode.width = vnode.height; + vnode.height = temp; + } + } + g.size.transpose(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/Vertex.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/Vertex.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,224 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.Vertex; + +import tango.text.convert.Format; +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.PositionConstants; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.graph.Obstacle; +import dwtx.draw2d.graph.Path; +import dwtx.draw2d.graph.Segment; + +/** + * A vertex representation for the ShortestPathRouting. Vertices are either one of + * four corners on an Obstacle(Rectangle), or one of the two end points of a + * Path. + * + * This class is not intended to be subclassed. + * @author Whitney Sorenson + * @since 3.0 + */ +class Vertex + : Point +{ + +// constants for the vertex type +static const int NOT_SET = 0; +static const int INNIE = 1; +static const int OUTIE = 2; + +// for shortest path +List neighbors; +bool isPermanent = false; +Vertex label; +double cost = 0; + +// for routing +int nearestObstacle = 0; +double offset = 0; +int type = NOT_SET; +int count = 0; +int totalCount = 0; +Obstacle obs; +List paths; +bool nearestObstacleChecked = false; +Map cachedCosines; +int positionOnObstacle = -1; + +private int origX, origY; + +/** + * Creates a new Vertex with the given x, y position and on the given obstacle. + * + * @param x x point + * @param y y point + * @param obs obstacle - can be null + */ +this(int x, int y, Obstacle obs) { + super(x, y); + origX = x; + origY = y; + this.obs = obs; +} + +/** + * Creates a new Vertex with the given point position and on the given obstacle. + * + * @param p the point + * @param obs obstacle - can be null + */ +this(Point p, Obstacle obs) { + this(p.x, p.y, obs); +} + +/** + * Adds a path to this vertex, calculates angle between two segments and caches it. + * + * @param path the path + * @param start the segment to this vertex + * @param end the segment away from this vertex + */ +void addPath(Path path, Segment start, Segment end) { + if (paths is null) { + paths = new ArrayList(); + cachedCosines = new HashMap(); + } + if (!paths.contains(path)) + paths.add(path); + cachedCosines.put(path, new Double(start.cosine(end))); +} + +/** + * Creates a point that represents this vertex offset by the given amount times + * the offset. + * + * @param modifier the offset + * @return a Point that has been bent around this vertex + */ +Point bend(int modifier) { + Point point = new Point(x, y); + if ((positionOnObstacle & PositionConstants.NORTH) > 0) + point.y -= modifier * offset; + else + point.y += modifier * offset; + if ((positionOnObstacle & PositionConstants.EAST) > 0) + point.x += modifier * offset; + else + point.x -= modifier * offset; + return point; +} + +/** + * Resets all fields on this Vertex. + */ +void fullReset() { + totalCount = 0; + type = NOT_SET; + count = 0; + cost = 0; + offset = getSpacing(); + nearestObstacle = 0; + label = null; + nearestObstacleChecked = false; + isPermanent = false; + if (neighbors !is null) + neighbors.clear(); + if (cachedCosines !is null) + cachedCosines.clear(); + if (paths !is null) + paths.clear(); +} + +/** + * Returns a Rectangle that represents the region around this vertex that + * paths will be traveling in. + * + * @param extraOffset a buffer to add to the region. + * @return the rectangle + */ +Rectangle getDeformedRectangle(int extraOffset) { + Rectangle rect = new Rectangle(0, 0, 0, 0); + + if ((positionOnObstacle & PositionConstants.NORTH) > 0) { + rect.y = y - extraOffset; + rect.height = origY - y + extraOffset; + } else { + rect.y = origY; + rect.height = y - origY + extraOffset; + } + if ((positionOnObstacle & PositionConstants.EAST) > 0) { + rect.x = origX; + rect.width = x - origX + extraOffset; + } else { + rect.x = x - extraOffset; + rect.width = origX - x + extraOffset; + } + + return rect; +} + +private int getSpacing() { + if (obs is null) + return 0; + return obs.getSpacing(); +} + +/** + * Grows this vertex by its offset to its maximum size. + */ +void grow() { + int modifier; + + if (nearestObstacle is 0) + modifier = totalCount * getSpacing(); + else + modifier = (nearestObstacle / 2) - 1; + + if ((positionOnObstacle & PositionConstants.NORTH) > 0) + y -= modifier; + else + y += modifier; + if ((positionOnObstacle & PositionConstants.EAST) > 0) + x += modifier; + else + x -= modifier; +} + +/** + * Shrinks this vertex to its original size. + */ +void shrink() { + x = origX; + y = origY; +} + +/** + * Updates the offset of this vertex based on its shortest distance. + */ +void updateOffset() { + if (nearestObstacle !is 0) + offset = ((nearestObstacle / 2) - 1) / totalCount; +} + +/** + * @see dwtx.draw2d.geometry.Point#toString() + */ +public String toString() { + return Format("V({}, {})", origX, origY ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/VerticalPlacement.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/VerticalPlacement.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.graph.VerticalPlacement; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.Rank; +import dwtx.draw2d.graph.Node; + +/** + * Assigns the Y and Height values to the nodes in the graph. All nodes in the same row + * are given the same height. + * @author Randy Hudson + * @since 2.1.2 + */ +class VerticalPlacement : GraphVisitor { + +void visit(DirectedGraph g) { + Insets pad; + int currentY = g.getMargin().top; + int row, rowHeight; + g.rankLocations = new int[g.ranks.size() + 1]; + for (row = 0; row < g.ranks.size(); row++) { + g.rankLocations[row] = currentY; + Rank rank = g.ranks.getRank(row); + rowHeight = 0; + rank.topPadding = rank.bottomPadding = 0; + for (int n = 0; n < rank.size(); n++) { + Node node = rank.getNode(n); + pad = g.getPadding(node); + rowHeight = Math.max(node.height, rowHeight); + rank.topPadding = Math.max(pad.top, rank.topPadding); + rank.bottomPadding = Math.max(pad.bottom, rank.bottomPadding); + } + currentY += rank.topPadding; + rank.setDimensions(currentY, rowHeight); + currentY += rank.height + rank.bottomPadding; + } + g.rankLocations[row] = currentY; + g.size.height = currentY; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/VirtualNode.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/VirtualNode.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2003, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.graph.VirtualNode; + +import dwt.dwthelper.utils; +import tango.text.convert.Format; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.Subgraph; + +/** + * @deprecated virtual nodes of an edge should be cast to Node. + * @author Randy Hudson + * @since 2.1.2 + */ +public class VirtualNode : Node { + +/** + * The next node. + */ +public Node next; + +/** + * The previous node. + */ +public Node prev; + +/** + * Constructs a virtual node. + * @deprecated This class is for internal use only. + * @param e the edge + * @param i the row + */ +public this(Edge e, int i) { + super(e); + incoming.add(e); + outgoing.add(e); + width = e.width; + height = 0; + rank = i; + setPadding(new Insets(0, e.padding, 0, e.padding)); +} + +/** + * Constructor. + * @param o object + * @param parent subgraph + */ +public this(Object o, Subgraph parent) { + super(o, parent); +} + +/** + * Returns the index of {@link #prev}. + * @return median + */ +public double medianIncoming() { + return prev.index; +} + +/** + * Returns the index of {@link #next}. + * @return outgoing + */ +public double medianOutgoing() { + return next.index; +} + +/** + * For internal use only. Returns the original edge weight multiplied by the omega value + * for the this node and the node on the previous rank. + * @return the weighted weight, or omega + */ +public int omega() { + Edge e = cast(Edge)data; + if (e.source.rank + 1 < rank && rank < e.target.rank) + return 8 * e.weight; + return 2 * e.weight; +} + +/** + * @see java.lang.Object#toString() + */ +public String toString() { + if (auto edge = cast(Edge)data ) + return Format("VN[{}]({})", (edge.vNodes.indexOf(this) + 1) //$NON-NLS-1$ + , data ); //$NON-NLS-1$ //$NON-NLS-2$ + return super.toString(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/graph/VirtualNodeCreation.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/VirtualNodeCreation.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.graph.VirtualNodeCreation; + +import dwt.dwthelper.utils; +import tango.text.convert.Format; + +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.RevertableChange; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Subgraph; +import dwtx.draw2d.graph.VirtualNode; +import dwtx.draw2d.graph.GraphUtilities; +import dwtx.draw2d.graph.NodeList; + +/** + * Encapsulates the conversion of a long edge to multiple short edges and back. + * @since 3.1 + */ +class VirtualNodeCreation : RevertableChange { + +private const Edge edge; +private const DirectedGraph graph; +private Node nodes[]; +private Edge[] edges; + +private static const int INNER_EDGE_X = 2; +private static const int LONG_EDGE_X = 8; + +/** + * Breaks a single edge into multiple edges containing virtual nodes. + * @since 3.1 + * @param edge The edge to convert + * @param graph the graph containing the edge + */ +public this(Edge edge, DirectedGraph graph) { + this.edge = edge; + this.graph = graph; + + int size = edge.target.rank - edge.source.rank - 1; + int offset = edge.source.rank + 1; + + Node prevNode = edge.source; + Node currentNode; + Edge currentEdge; + nodes = new Node[size]; + edges = new Edge[size + 1]; + + Insets padding = new Insets(0, edge.padding, 0, edge.padding); + + Subgraph s = GraphUtilities.getCommonAncestor(edge.source, edge.target); + + for (int i = 0; i < size; i++) { + nodes[i] = currentNode = new VirtualNode(stringcast(Format("Virtual{}:{}", i, edge)), s); //$NON-NLS-1$ + currentNode.width = edge.width; + if (s !is null) { + currentNode.nestingIndex = s.nestingIndex; + } + + currentNode.height = 0; + currentNode.setPadding(padding); + currentNode.rank = offset + i; + graph.ranks.getRank(offset + i).add(currentNode); + + currentEdge = new Edge(prevNode, currentNode, 1, edge.weight * LONG_EDGE_X); + if (i is 0) { + currentEdge.weight = edge.weight * INNER_EDGE_X; + currentEdge.offsetSource = edge.offsetSource; + } + graph.edges.add(edges[i] = currentEdge); + graph.nodes.add(currentNode); + prevNode = currentNode; + } + + currentEdge = new Edge(prevNode, edge.target, 1, edge.weight * INNER_EDGE_X); + currentEdge.offsetTarget = edge.offsetTarget; + graph.edges.add(edges[edges.length - 1] = currentEdge); + graph.removeEdge(edge); +} + +void revert() { + edge.start = edges[0].start; + edge.end = edges[edges.length - 1].end; + edge.vNodes = new NodeList(); + for (int i = 0; i < edges.length; i++) { + graph.removeEdge(edges[i]); + } + for (int i = 0; i < nodes.length; i++) { + edge.vNodes.add(nodes[i]); + graph.removeNode(nodes[i]); + } + edge.source.outgoing.add(edge); + edge.target.incoming.add(edge); + + graph.edges.add(edge); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/internal/MultiValueMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/internal/MultiValueMap.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.internal.MultiValueMap; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +public class MultiValueMap { + private HashMap map; + + public this(){ + map = new HashMap(); + } + + public ArrayList get(Object key) { + Object value = map.get(key); + if (value is null) return null; + + if (auto r = cast(ArrayList)value ) + return r; + ArrayList v = new ArrayList(1); + v.add(value); + return v; + } + + + + public void put(Object key, Object value) { + Object existingValues = map.get(key); + if (existingValues is null) { + map.put(key, value); + return; + } + if (auto v = cast(ArrayList)existingValues ) { + if (!v.contains(value)) + v.add(value); + return; + } + if (existingValues !is value) { + ArrayList v = new ArrayList(2); + v.add(existingValues); + v.add(value); + map.put(key, v); + } + } + + public int remove(Object key, Object value) { + Object existingValues = map.get(key); + if (existingValues !is null) { + if (auto v = cast(ArrayList)existingValues ) { + int result = v.indexOf(value); + if (result is -1) + return -1; + v.remove(result); + if (v.isEmpty()) + map.remove(key); + return result; + } + if (map.remove(key) !is null) + return 0; + } + return -1; + } + + public Object removeValue(Object value) { + Iterator iter = map.values().iterator(); + Object current; + while (iter.hasNext()) { + current = iter.next(); + if (value.opEquals(current)) { + iter.remove(); + return value; + } else if (auto curlist = cast(List)current ) { + if (curlist.remove(value)) { + if (curlist.isEmpty()) + iter.remove(); + return value; + } + } + } + return null; + } + + public int size() { + return map.size(); + } +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/parts/ScrollableThumbnail.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/parts/ScrollableThumbnail.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,284 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.parts.ScrollableThumbnail; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Bean; + +import dwt.DWT; +import dwt.graphics.Image; +import dwt.graphics.ImageData; +import dwt.graphics.PaletteData; +import dwt.graphics.RGB; +import dwt.widgets.Display; +import dwtx.draw2d.ColorConstants; +import dwtx.draw2d.Figure; +import dwtx.draw2d.FigureListener; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.KeyEvent; +import dwtx.draw2d.KeyListener; +import dwtx.draw2d.MouseEvent; +import dwtx.draw2d.MouseListener; +import dwtx.draw2d.MouseMotionListener; +import dwtx.draw2d.Viewport; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.parts.Thumbnail; + +/** + * A scaled image representation of a Figure. If the source Figure is not completely + * visible, a SelectorFigure is placed over the thumbnail representing the viewable area + * and can be dragged around to scroll the source figure. + */ +public final class ScrollableThumbnail + : Thumbnail +{ + +private class ClickScrollerAndDragTransferrer + : MouseMotionListener.Stub + , MouseListener +{ + private bool dragTransfer; + public void mouseDoubleClicked(MouseEvent me) { } + public void mouseDragged(MouseEvent me) { + if (dragTransfer) + syncher.mouseDragged(me); + } + public void mousePressed(MouseEvent me) { + if (!(this.outer.getClientArea().contains(me.getLocation()))) + return; + Dimension selectorCenter = selector.getBounds().getSize().scale(0.5f); + Point scrollPoint = me.getLocation() + .getTranslated(getLocation().getNegated()) + .translate(selectorCenter.negate()) + .scale(1.0f / getViewportScaleX(), 1.0f / getViewportScaleY()) + .translate( + viewport.getHorizontalRangeModel().getMinimum(), + viewport.getVerticalRangeModel().getMinimum()); + viewport.setViewLocation(scrollPoint); + syncher.mousePressed(me); + dragTransfer = true; + } + public void mouseReleased(MouseEvent me) { + syncher.mouseReleased(me); + dragTransfer = false; + } +} + +private class ScrollSynchronizer + : MouseMotionListener.Stub + , MouseListener +{ + private Point startLocation; + private Point viewLocation; + + public void mouseDoubleClicked(MouseEvent me) { } + + public void mouseDragged(MouseEvent me) { + Dimension d = me.getLocation().getDifference(startLocation); + d.scale(1.0f / getViewportScaleX(), 1.0f / getViewportScaleY()); + viewport.setViewLocation(viewLocation.getTranslated(d)); + me.consume(); + } + + public void mousePressed(MouseEvent me) { + startLocation = me.getLocation(); + viewLocation = viewport.getViewLocation(); + me.consume(); + } + + public void mouseReleased(MouseEvent me) { } +} + +private class SelectorFigure + : Figure +{ + this(){ + iBounds = new Rectangle(0, 0, 1, 1); + Display display = Display.getCurrent(); + PaletteData pData = new PaletteData(0xFF, 0xFF00, 0xFF0000); + RGB rgb = ColorConstants.menuBackgroundSelected.getRGB(); + int fillColor = pData.getPixel(rgb); + ImageData iData = new ImageData(1, 1, 24, pData); + iData.setPixel(0, 0, fillColor); + iData.setAlpha(0, 0, 55); + image = new Image(display, iData); + } + private Rectangle iBounds; + + private Image image; + + protected void dispose() { + image.dispose(); + } + + public void paintFigure(Graphics g) { + Rectangle bounds = getBounds().getCopy(); + + // Avoid drawing images that are 0 in dimension + if (bounds.width < 5 || bounds.height < 5) + return; + + // Don't paint the selector figure if the entire source is visible. + Dimension thumbnailSize = new Dimension(getThumbnailImage()); + // expand to compensate for rounding errors in calculating bounds + Dimension size = getSize().getExpanded(1, 1); + if (size.contains(thumbnailSize)) + return; + + bounds.height--; + bounds.width--; + g.drawImage(image, iBounds, bounds); + + g.setForegroundColor(ColorConstants.menuBackgroundSelected); + g.drawRectangle(bounds); + } + +} +private FigureListener figureListener; +private void initFigureListener(){ + figureListener = new class() FigureListener { + public void figureMoved(IFigure source) { + reconfigureSelectorBounds(); + } + }; +} +private KeyListener keyListener; +private void initKeyListener(){ + keyListener = new class() KeyListenerStub { + public void keyPressed(KeyEvent ke) { + int moveX = viewport.getClientArea().width / 4; + int moveY = viewport.getClientArea().height / 4; + if (ke.keycode is DWT.HOME || (isMirrored() ? ke.keycode is DWT.ARROW_RIGHT + : ke.keycode is DWT.ARROW_LEFT)) + viewport.setViewLocation(viewport.getViewLocation().translate(-moveX, 0)); + else if (ke.keycode is DWT.END || (isMirrored() ? ke.keycode is DWT.ARROW_LEFT + : ke.keycode is DWT.ARROW_RIGHT)) + viewport.setViewLocation(viewport.getViewLocation().translate(moveX, 0)); + else if (ke.keycode is DWT.ARROW_UP || ke.keycode is DWT.PAGE_UP) + viewport.setViewLocation(viewport.getViewLocation().translate(0, -moveY)); + else if (ke.keycode is DWT.ARROW_DOWN || ke.keycode is DWT.PAGE_DOWN) + viewport.setViewLocation(viewport.getViewLocation().translate(0, moveY)); + } + }; +} + +private PropertyChangeListener propListener; +private void initPropListener(){ + propListener = new class() PropertyChangeListener { + public void propertyChange(PropertyChangeEvent evt) { + reconfigureSelectorBounds(); + } + }; +} + +private SelectorFigure selector; + +private ScrollSynchronizer syncher; +private Viewport viewport; + +/** + * Creates a new ScrollableThumbnail. + */ +public this() { + super(); + initFigureListener(); + initKeyListener(); + initPropListener(); + initialize(); +} + +/** + * Creates a new ScrollableThumbnail that synchs with the given Viewport. + * @param port The Viewport + */ +public this(Viewport port) { + super(); + initFigureListener(); + initKeyListener(); + initPropListener(); + setViewport(port); + initialize(); +} + +/** + * @see Thumbnail#deactivate() + */ +public void deactivate() { + viewport.removePropertyChangeListener(Viewport.PROPERTY_VIEW_LOCATION, propListener); + viewport.removeFigureListener(figureListener); + remove(selector); + selector.dispose(); + super.deactivate(); +} + +private double getViewportScaleX() { + return cast(double)targetSize.width / viewport.getContents().getBounds().width; +} + +private double getViewportScaleY() { + return cast(double)targetSize.height / viewport.getContents().getBounds().height; +} + +private void initialize() { + selector = new SelectorFigure(); + selector.addMouseListener(syncher = new ScrollSynchronizer()); + selector.addMouseMotionListener(syncher); + selector.setFocusTraversable(true); + selector.addKeyListener(keyListener); + add(selector); + ClickScrollerAndDragTransferrer transferrer = + new ClickScrollerAndDragTransferrer(); + addMouseListener(transferrer); + addMouseMotionListener(transferrer); +} + +private void reconfigureSelectorBounds() { + Rectangle rect = new Rectangle(); + Point offset = viewport.getViewLocation(); + offset.x -= viewport.getHorizontalRangeModel().getMinimum(); + offset.y -= viewport.getVerticalRangeModel().getMinimum(); + rect.setLocation(offset); + rect.setSize(viewport.getClientArea().getSize()); + rect.scale(getViewportScaleX(), getViewportScaleY()); + rect.translate(getClientArea().getLocation()); + selector.setBounds(rect); +} + +/** + * Reconfigures the SelectorFigure's bounds if the scales have changed. + * @param scaleX The X scale + * @param scaleY The Y scale + * @see dwtx.draw2d.parts.Thumbnail#setScales(float, float) + */ +protected void setScales(float scaleX, float scaleY) { + if (scaleX is getScaleX() && scaleY is getScaleY()) + return; + + super.setScales(scaleX, scaleY); + reconfigureSelectorBounds(); +} + +/** + * Sets the Viewport that this ScrollableThumbnail will synch with. + * @param port The Viewport + */ +public void setViewport(Viewport port) { + port.addPropertyChangeListener(Viewport.PROPERTY_VIEW_LOCATION, propListener); + port.addFigureListener(figureListener); + viewport = port; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/parts/Thumbnail.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/parts/Thumbnail.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,513 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.parts.Thumbnail; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; +import dwtx.dwtxhelper.Collection; + +import dwt.DWT; +import dwt.graphics.Color; +import dwt.graphics.GC; +import dwt.graphics.Image; +static import dwtx.draw2d.geometry.Point; +import dwt.widgets.Display; +import dwtx.draw2d.Figure; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.SWTGraphics; +import dwtx.draw2d.ScaledGraphics; +import dwtx.draw2d.UpdateListener; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Rectangle; + +/** + * A Thumbnail is a Figure that displays an image of its source Figure at a + * smaller size. The Thumbnail will maintain the aspect ratio of the source + * Figure. + * + * @author Eric Bordeau + */ +public class Thumbnail + : Figure + , UpdateListener +{ +alias Figure.getPreferredSize getPreferredSize; + + +/** + * This updates the Thumbnail by breaking the thumbnail {@link Image} into + * several tiles and updating each tile individually. + */ +class ThumbnailUpdater : Runnable { + static final int MAX_BUFFER_SIZE = 256; + private int currentHTile, currentVTile; + private int hTiles, vTiles; + private bool isActive_ = true; + + private bool isRunning_ = false; + private GC thumbnailGC; + private ScaledGraphics thumbnailGraphics; + private Dimension tileSize; + + /** + * Stops the updater and disposes of any resources. + */ + public void deactivate() { + setActive(false); + stop(); + if (thumbnailImage !is null) { + thumbnailImage.dispose(); + thumbnailImage = null; + thumbnailImageSize = null; + } + } + + /** + * Returns the current horizontal tile index. + * @return current horizontal tile index. + */ + protected int getCurrentHTile() { + return currentHTile; + } + + /** + * Returns the current vertical tile index. + * @return current vertical tile index. + */ + protected int getCurrentVTile() { + return currentVTile; + } + + /** + * Returns true if this ThumbnailUpdater is active. An inactive + * updater has disposed of its {@link Image}. The updater may be active and + * not currently running. + * @return true if this ThumbnailUpdater is active + */ + public bool isActive() { + return isActive_; + } + + /** + * Returns true if this is currently running and updating at + * least one tile on the thumbnail {@link Image}. + * @return true if this is currently running + */ + public bool isRunning() { + return isRunning_; + } + + /** + * Resets the number of vertical and horizontal tiles, as well as the tile + * size and current tile index. + */ + public void resetTileValues() { + hTiles = cast(int)Math.ceil(cast(float)getSourceRectangle().width + / cast(float)MAX_BUFFER_SIZE); + vTiles = cast(int)Math.ceil(cast(float)getSourceRectangle().height + / cast(float)MAX_BUFFER_SIZE); + + tileSize = new Dimension(cast(int)Math.ceil(cast(float)getSourceRectangle().width + / cast(float)hTiles), + cast(int)Math.ceil(cast(float)getSourceRectangle().height + / cast(float)vTiles)); + + currentHTile = 0; + currentVTile = 0; + } + + /** + * Restarts the updater. + */ + public void restart() { + stop(); + start(); + } + + /** + * Updates the current tile on the Thumbnail. An area of the source Figure + * is painted to an {@link Image}. That Image is then drawn on the + * Thumbnail. Scaling of the source Image is done inside + * {@link GC#drawImage(Image, int, int, int, int, int, int, int, int)} since + * the source and target sizes are different. The current tile indexes are + * incremented and if more updating is necesary, this {@link Runnable} is + * called again in a {@link Display#timerExec(int, Runnable)}. If no more + * updating is required, {@link #stop()} is called. + */ + public void run() { + if (!isActive() || !isRunning()) + return; + int v = getCurrentVTile(); + int sy1 = v * tileSize.height; + int sy2 = Math.min((v + 1) * tileSize.height, getSourceRectangle().height); + + int h = getCurrentHTile(); + int sx1 = h * tileSize.width; + int sx2 = Math.min((h + 1) * tileSize.width, getSourceRectangle().width); + dwtx.draw2d.geometry.Point.Point p = getSourceRectangle().getLocation(); + + Rectangle rect = new Rectangle(sx1 + p.x, sy1 + p.y, sx2 - sx1, sy2 - sy1); + thumbnailGraphics.pushState(); + thumbnailGraphics.setClip(rect); + thumbnailGraphics.fillRectangle(rect); + sourceFigure.paint(thumbnailGraphics); + thumbnailGraphics.popState(); + + if (getCurrentHTile() < (hTiles - 1)) + setCurrentHTile(getCurrentHTile() + 1); + else { + setCurrentHTile(0); + if (getCurrentVTile() < (vTiles - 1)) + setCurrentVTile(getCurrentVTile() + 1); + else + setCurrentVTile(0); + } + + if (getCurrentHTile() !is 0 || getCurrentVTile() !is 0) + Display.getCurrent().asyncExec(this); + else if (isDirty()) { + setDirty(false); + Display.getCurrent().asyncExec(this); + repaint(); + } else { + stop(); + repaint(); + } + } + + /** + * Sets the active flag. + * @param value The active value + */ + public void setActive(bool value) { + isActive_ = value; + } + + /** + * Sets the current horizontal tile index. + * @param count current horizontal tile index + */ + protected void setCurrentHTile(int count) { + currentHTile = count; + } + + /** + * Sets the current vertical tile index. + * @param count current vertical tile index + */ + protected void setCurrentVTile(int count) { + currentVTile = count; + } + + /** + * Starts this updater. This method initializes all the necessary resources + * and puts this {@link Runnable} on the asynch queue. If this updater is + * not active or is already running, this method just returns. + */ + public void start() { + if (!isActive() || isRunning()) + return; + + isRunning_ = true; + setDirty(false); + resetTileValues(); + + if (!targetSize.opEquals(thumbnailImageSize)) { + resetThumbnailImage(); + } + + if (targetSize.isEmpty()) + return; + + thumbnailGC = new GC(thumbnailImage, + sourceFigure.isMirrored() ? DWT.RIGHT_TO_LEFT : DWT.NONE); + thumbnailGraphics = new ScaledGraphics(new SWTGraphics(thumbnailGC)); + thumbnailGraphics.scale(getScaleX()); + thumbnailGraphics.translate(getSourceRectangle().getLocation().negate()); + + Color color = sourceFigure.getForegroundColor(); + if (color !is null) + thumbnailGraphics.setForegroundColor(color); + color = sourceFigure.getBackgroundColor(); + if (color !is null) + thumbnailGraphics.setBackgroundColor(color); + thumbnailGraphics.setFont(sourceFigure.getFont()); + + setScales(targetSize.width / cast(float)getSourceRectangle().width, + targetSize.height / cast(float)getSourceRectangle().height); + + Display.getCurrent().asyncExec(this); + } + + /** + * + * @since 3.2 + */ + private void resetThumbnailImage() { + if (thumbnailImage !is null) + thumbnailImage.dispose(); + + if (!targetSize.isEmpty()) { + thumbnailImage = new Image(Display.getDefault(), + targetSize.width, + targetSize.height); + thumbnailImageSize = new Dimension(targetSize); + } + else { + thumbnailImage = null; + thumbnailImageSize = new Dimension(0, 0); + } + } + + /** + * Stops this updater. Also disposes of resources (except the thumbnail + * image which is still needed for painting). + */ + public void stop() { + isRunning_ = false; + if (thumbnailGC !is null) { + thumbnailGC.dispose(); + thumbnailGC = null; + } + if (thumbnailGraphics !is null) { + thumbnailGraphics.dispose(); + thumbnailGraphics = null; + } + // Don't dispose of the thumbnail image since it is needed to paint the + // figure when the source is not dirty (i.e. showing/hiding the dock). + } +} +private bool isDirty_; +private float scaleX; +private float scaleY; + +private IFigure sourceFigure; +Dimension targetSize; +private Image thumbnailImage; +private Dimension thumbnailImageSize; +private ThumbnailUpdater updater; + +/** + * Creates a new Thumbnail. The source Figure must be set separately if you + * use this constructor. + */ +public this() { + super(); + targetSize = new Dimension(0, 0); + updater = new ThumbnailUpdater(); +} + +/** + * Creates a new Thumbnail with the given IFigure as its source figure. + * @param fig The source figure + */ +public this(IFigure fig) { + this(); + setSource(fig); +} + +private Dimension adjustToAspectRatio(Dimension size, bool adjustToMaxDimension) { + Dimension sourceSize = getSourceRectangle().getSize(); + Dimension borderSize = new Dimension(getInsets().getWidth(), getInsets().getHeight()); + size.expand(borderSize.getNegated()); + int width, height; + if (adjustToMaxDimension) { + width = Math.max(size.width, cast(int)(size.height * sourceSize.width + / cast(float)sourceSize.height + 0.5)); + height = Math.max(size.height, cast(int)(size.width * sourceSize.height + / cast(float)sourceSize.width + 0.5)); + } else { + width = Math.min(size.width, cast(int)(size.height * sourceSize.width + / cast(float)sourceSize.height + 0.5)); + height = Math.min(size.height, cast(int)(size.width * sourceSize.height + / cast(float)sourceSize.width + 0.5)); + } + size.width = width; + size.height = height; + return size.expand(borderSize); +} + +/** + * Deactivates this Thumbnail. + */ +public void deactivate() { + sourceFigure.getUpdateManager().removeUpdateListener(this); + updater.deactivate(); +} + +/** + * Returns the preferred size of this Thumbnail. The preferred size will be + * calculated in a way that maintains the source Figure's aspect ratio. + * + * @param wHint The width hint + * @param hHint The height hint + * @return The preferred size + */ +public Dimension getPreferredSize(int wHint, int hHint) { + if (prefSize is null) + return adjustToAspectRatio(getBounds().getSize(), false); + + Dimension preferredSize = adjustToAspectRatio(prefSize.getCopy(), true); + + if (maxSize is null) + return preferredSize; + + Dimension maximumSize = adjustToAspectRatio(maxSize.getCopy(), true); + if (preferredSize.contains(maximumSize)) + return maximumSize; + else + return preferredSize; +} + +/** + * Returns the scale factor on the X-axis. + * @return X scale + */ +protected float getScaleX() { + return scaleX; +} + +/** + * Returns the scale factor on the Y-axis. + * @return Y scale + */ +protected float getScaleY() { + return scaleY; +} + +/** + * Returns the source figure being used to generate a thumbnail. + * @return the source figure + */ +protected IFigure getSource() { + return sourceFigure; +} + +/** + * Returns the rectangular region relative to the source figure which will be the basis of + * the thumbnail. The value may be returned by reference and should not be modified by + * the caller. + * @since 3.1 + * @return the region of the source figure being used for the thumbnail + */ +protected Rectangle getSourceRectangle() { + return sourceFigure.getBounds(); +} + +/** + * Returns the scaled Image of the source Figure. If the Image needs to be + * updated, the ThumbnailUpdater will notified. + * + * @return The thumbnail image + */ +protected Image getThumbnailImage() { + Dimension oldSize = targetSize; + targetSize = getPreferredSize(); + targetSize.expand((new Dimension(getInsets().getWidth(), + getInsets().getHeight())).negate()); + setScales(targetSize.width / cast(float)getSourceRectangle().width, + targetSize.height / cast(float)getSourceRectangle().height); + if ((isDirty()) && !updater.isRunning()) + updater.start(); + else if (oldSize !is null && !targetSize.opEquals(oldSize)) { + revalidate(); + updater.restart(); + } + + return thumbnailImage; +} + +/** + * Returns true if the source figure has changed. + * @return true if the source figure has changed + */ +protected bool isDirty() { + return isDirty_; +} + +/** + * @see dwtx.draw2d.UpdateListener#notifyPainting(Rectangle, Map) + */ +public void notifyPainting(Rectangle damage, Map dirtyRegions) { + Iterator dirtyFigures = dirtyRegions.keySet().iterator(); + while (dirtyFigures.hasNext()) { + IFigure current = cast(IFigure)dirtyFigures.next(); + while (current !is null) { + if (current is getSource()) { + setDirty(true); + repaint(); + return; + } + current = current.getParent(); + } + } +} + +/** + * @see dwtx.draw2d.UpdateListener#notifyValidating() + */ +public void notifyValidating() { +// setDirty(true); +// revalidate(); +} + +/** + * @see dwtx.draw2d.Figure#paintFigure(Graphics) + */ +protected void paintFigure(Graphics graphics) { + Image thumbnail = getThumbnailImage(); + if (thumbnail is null) + return; + graphics.drawImage(thumbnail, getClientArea().getLocation()); +} + +/** + * Sets the dirty flag. + * @param value The dirty value + */ +public void setDirty(bool value) { + isDirty_ = value; +} + +/** + * Sets the X and Y scales for the Thumbnail. These scales represent the ratio + * between the source figure and the Thumbnail. + * @param x The X scale + * @param y The Y scale + */ +protected void setScales(float x, float y) { + scaleX = x; + scaleY = y; +} + +/** + * Sets the source Figure. Also sets the scales and creates the necessary + * update manager. + * @param fig The source figure + */ +public void setSource(IFigure fig) { + if (sourceFigure is fig) + return; + if (sourceFigure !is null) + sourceFigure.getUpdateManager().removeUpdateListener(this); + sourceFigure = fig; + if (sourceFigure !is null) { + setScales(cast(float)getSize().width / cast(float)getSourceRectangle().width, + cast(float)getSize().height / cast(float)getSourceRectangle().height); + sourceFigure.getUpdateManager().addUpdateListener(this); + repaint(); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/AbstractFlowBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/AbstractFlowBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.AbstractFlowBorder; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.AbstractBorder; +import dwtx.draw2d.Border; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.text.FlowBorder; +import dwtx.draw2d.text.FlowFigure; + +/** + * A basis for implementing {@link FlowBorder}. Subclassing this class will possibly + * guarantee compatibility with future changes to the FlowBorder interface. This class + * also returns default values for many of the required methods as a convenience. + * @since 3.1 + */ +public abstract class AbstractFlowBorder + : AbstractBorder + , FlowBorder +{ +/** + * @see FlowBorder#getBottomMargin() + */ +public int getBottomMargin() { + return 0; +} + +/** + * @see Border#getInsets(IFigure) + */ +public Insets getInsets(IFigure figure) { + return IFigure_NO_INSETS; +} + +/** + * @see FlowBorder#getLeftMargin() + */ +public int getLeftMargin() { + return 0; +} + +/** + * @see FlowBorder#getRightMargin() + */ +public int getRightMargin() { + return 0; +} + +/** + * @see FlowBorder#getTopMargin() + */ +public int getTopMargin() { + return 0; +} + +/** + * This method is not called on FlowBorders. For this reason it is + * implemented here and made final so that clients override the correct + * method. + * @param figure the figure + * @param graphics the graphics + * @param insets the insets + * @see FlowBorder#paint(FlowFigure, Graphics, Rectangle, int) + */ +public final void paint(IFigure figure, Graphics graphics, Insets insets) { } + +/** + * Subclasses should override this method to paint each box's border. + * @see FlowBorder#paint(FlowFigure, Graphics, Rectangle, int) + */ +public void paint(FlowFigure figure, Graphics g, Rectangle where, int sides) { } + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/BidiChars.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/BidiChars.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.text.BidiChars; + +import dwt.dwthelper.utils; + +/** + * @since 3.1 + */ +class BidiChars { + +static const dchar P_SEP = '\u2029'; +static const dchar ZWJ = '\u200d'; +static const dchar LRO = '\u202d'; +static const dchar RLO = '\u202e'; +static const dchar OBJ = '\ufffc'; +static const dchar LRE = '\u202a'; +static const dchar RLE = '\u202b'; + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/BidiInfo.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/BidiInfo.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.text.BidiInfo; + +import dwt.dwthelper.utils; + +/** + * This class is for INTERNAL use only. + * @since 3.1 + */ +public class BidiInfo { + +/** + * Odd-sized array consisting of bidi levels, interleaved with the offsets at which levels + * change. + */ +public int[] levelInfo; + +/** + * Indicates if the ZWJ character needs to be prepended to text being rendered. + */ +public bool leadingJoiner; + +/** + * Indicates if the ZRJ character needs to be appended to the text being rendered.. + */ +public bool trailingJoiner; + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/BidiProcessor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/BidiProcessor.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,262 @@ +/******************************************************************************* + * Copyright (c) 2004, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.text.BidiProcessor; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +//import dwtx.dwtxhelper.UBidi; + +import dwt.DWT; +import dwt.graphics.TextLayout; +import dwtx.draw2d.text.FlowFigure; +import dwtx.draw2d.text.BidiInfo; +import dwtx.draw2d.text.FlowUtilities; + +class Bidi{ + public static bool requiresBidi( char[], int, int ){ + return false; + } +} + +/** + * A helper class for a BlockFlow that does Bidi evaluation of all the text in that block. + *

+ * WARNING: This class is for INTERNAL use only. + * @author Pratik Shah + * @since 3.1 + */ +public final class BidiProcessor { + +/* + * $TODO Workaround for Carbon. AWT DLL cannot start properly on carbon. + * Waiting for bug 82104 + */ +private static const bool isMacOS; +static this(){ + isMacOS = DWT.getPlatform().equals("carbon"); //$NON-NLS-1$ + INSTANCE = new BidiProcessor(); +} + +/** + * A helper class to hold information about contributions made to this processor. + * + * @author Pratik Shah + * @since 3.1 + */ +private static class BidiEntry { + int begin, end; + FlowFigure fig; + this(FlowFigure fig, int offset, int length) { + this.fig = fig; + this.begin = offset; + this.end = offset + length; + } +} + +/** + * A singleton instance. + */ +public static const BidiProcessor INSTANCE; +private StringBuffer bidiText; +private List list; +private int orientation = DWT.LEFT_TO_RIGHT; + +private this() { + list = new ArrayList(); +} + +/** + * Records a String contribution for this bidi context. Contributions are + * concatenated (in the order that they were contributed) to make the final + * String which will determine the bidi info for all contributors. + * @param fig the figure that is contributing the given text + * @param str the text contributed by the given figure + * @see #addControlChar(char) + */ +public void add(FlowFigure fig, String str) { + //We are currently tracking empty contributions ("") + list.add(new BidiEntry(fig, bidiText.length(), str.length)); + bidiText.append(str); +} + +/** + * Records a character contribution for this bidi context. Contributions are + * concatenated (in the order that they were contributed) to make the final + * String which will determine the bidi info for all contributors. + * @param fig the figure that is contributing the given text + * @param c the character being added + * @see #addControlChar(char) + */ +public void add(FlowFigure fig, dchar c) { + auto str = dcharToString(c); + list.add(new BidiEntry(fig, bidiText.length(), str.length)); + bidiText.append(str); +} + +/** + * This methods allows FlowFigures to contribute text that may effect the bidi evaluation, + * but is not text that is visible on the screen. The bidi level of such text is + * reported back to the contributing figure. + * + * @param c the control character + */ +public void addControlChar(dchar c) { + bidiText.append(dcharToString(c)); +} + +/** + * Breaks the given int array into bidi levels for each figure based on their + * contributed text, and assigns those levels to each figure. Also determines + * if shaping needs to occur between figures and sets the appendJoiner, prependJoiner + * accordingly. + * + * @param levels the calculated levels of all the text in the block + */ +private void assignResults(int[] levels) { + BidiEntry prevEntry = null, entry = null; + BidiInfo prevInfo = null, info = null; + int end = 2, start = 0; + for (int i = 0; i < list.size(); i++) { + entry = cast(BidiEntry)list.get(i); + info = new BidiInfo(); + + while (levels[end] < entry.end) + end += 2; + + int levelInfo[]; + if (end is start) { + levelInfo = new int[1]; + if (prevInfo !is null) + levelInfo[0] = prevInfo.levelInfo[prevInfo.levelInfo.length - 1]; + else + levelInfo[0] = (orientation is DWT.LEFT_TO_RIGHT) ? 0 : 1; + } else { + levelInfo = new int[end - start - 1]; + System.arraycopy(levels, start + 1, levelInfo, 0, levelInfo.length); + } + for (int j = 1; j < levelInfo.length; j += 2) + levelInfo[j] -= entry.begin; + info.levelInfo = levelInfo; + + // Compare current and previous for joiners, and commit previous BidiInfo. + if (prevEntry !is null) { + if (// if we started in the middle of a level run + levels[start] < entry.begin + // and the level run is odd + && levels[start + 1] % 2 is 1 + // and the first character of this figure is Arabic + && isJoiner(entry.begin) + // and the last character of the previous figure was Arabic + && isPrecedingJoiner(entry.begin)) + prevInfo.trailingJoiner = info.leadingJoiner = true; + prevEntry.fig.setBidiInfo(prevInfo); + } + prevEntry = entry; + prevInfo = info; + if (entry.end is levels[end]) + start = end; + else + start = end - 2; + } + if (entry !is null) + entry.fig.setBidiInfo(info); +} + +private bool isJoiner(int begin) { + return begin < bidiText.length && isJoiningCharacter(bidiText.slice()[begin.. $].firstCodePoint()); +} + +/** + * @param the character to be evaluated + * @return true if the given character is Arabic or ZWJ + */ +private bool isJoiningCharacter(dchar c) { + return false; + //FIXME: DWT Missing functionality +// return Character.getDirectionality(c) is Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC +// || c is BidiChars.ZWJ; +} + +private bool isPrecedingJoiner(int begin) { + return begin > 0 && isJoiningCharacter(bidiText.slice()[begin - 1..$].firstCodePoint()); +} + +/** + * Processes the contributed text, determines the Bidi levels, and assigns them to + * the FlowFigures that made thet contributions. This class is for INTERNAL use + * only. Shaping of visually contiguous Arabic characters that are split in different + * figures is also handled. This method will do nothing if the contributed text does not + * require Bidi evaluation. All contributions are discarded at the end of this method. + */ +public void process() { + try { + if (bidiText.length is 0 || isMacOS) + return; + char[] chars = bidiText.slice().dup; + + if (orientation !is DWT.RIGHT_TO_LEFT + && !Bidi.requiresBidi(chars, 0, chars.length - 1)) + return; + + int[] levels = new int[15]; + TextLayout layout = FlowUtilities.getTextLayout(); + + layout.setOrientation(orientation); + layout.setText(bidiText.toString()); + int j = 0, offset, prevLevel = -1; + for (offset = 0; offset < chars.length; offset++) { + int newLevel = layout.getLevel(offset); + if (newLevel !is prevLevel) { + if (j + 3 > levels.length) { + int temp[] = levels; + levels = new int[levels.length * 2 + 1]; + System.arraycopy(temp, 0, levels, 0, temp.length); + } + levels[j++] = offset; + levels[j++] = newLevel; + prevLevel = newLevel; + } + } + levels[j++] = offset; + + if (j !is levels.length) { + int[] newLevels = new int[j]; + System.arraycopy(levels, 0, newLevels, 0, j); + levels = newLevels; + } + assignResults(levels); + + // reset the orientation of the layout, in case it was set to RTL + layout.setOrientation(DWT.LEFT_TO_RIGHT); + } finally { + //will cause the fields to be reset for the next string to be processed + //bidiText = null; + bidiText.clear(); + list.clear(); + } +} + +/** + * Sets the paragraph embedding. The given orientation will be used on TextLayout + * when determining the Bidi levels. + * + * @param newOrientation DWT.LEFT_TO_RIGHT or DWT.RIGHT_TO_LEFT + */ +public void setOrientation(int newOrientation) { + bidiText = new StringBuffer(); + list.clear(); + orientation = newOrientation; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/BlockBox.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/BlockBox.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.BlockBox; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.text.CompositeBox; +import dwtx.draw2d.text.BlockFlow; +import dwtx.draw2d.text.LineRoot; +import dwtx.draw2d.text.FlowBox; + +/** + * A CompositeBox suitable for containing multiple LineBox fragments. + * @author hudsonr + * @since 2.1 + */ +public class BlockBox + : CompositeBox +{ + +int height; +private int y; +BlockFlow owner; + +this(BlockFlow owner) { + this.owner = owner; +} + +/** + * @see CompositeBox#add(FlowBox) + */ +public void add(FlowBox box) { + width = Math.max(width, box.getWidth()); + height = Math.max(height, box.getBaseline() + box.getDescent()); +} + +/** + * @see FlowBox#containsPoint(int, int) + */ +public bool containsPoint(int x, int y) { + return true; +} + +/** + * @see FlowBox#getAscent() + */ +public int getAscent() { + return 0; +} + +/** + * @see FlowBox#getBaseline() + */ +public int getBaseline() { + return y; +} + +int getBottomMargin() { + return owner.getBottomMargin(); +} + +/** + * @see FlowBox#getDescent() + */ +public int getDescent() { + return height; +} + +/** + * @return Returns the height. + */ +public int getHeight() { + return height; +} + +LineRoot getLineRoot() { + return null; +} + +int getTopMargin() { + return owner.getTopMargin(); +} + +/** + * Sets the height. + * @param h The height + */ +public void setHeight(int h) { + height = h; +} + +/** + * @see CompositeBox#setLineTop(int) + */ +public void setLineTop(int y) { + this.y = y; +} + +Rectangle toRectangle() { + return new Rectangle(getX(), y, Math.max(getWidth(), recommendedWidth), height); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/BlockFlow.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/BlockFlow.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,313 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.BlockFlow; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwt.DWT; +import dwtx.draw2d.ColorConstants; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.PositionConstants; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.text.FlowFigure; +import dwtx.draw2d.text.BlockBox; +import dwtx.draw2d.text.BidiProcessor; +import dwtx.draw2d.text.FlowFigureLayout; +import dwtx.draw2d.text.BidiChars; +import dwtx.draw2d.text.BlockFlowLayout; +import dwtx.draw2d.text.FlowBorder; + +/** + * A FlowFigure represented by a single {@link BlockBox} containing one or + * more lines. A BlockFlow is a creator of LineBoxes, which its children require during + * layout. A BlockFlow can be thought of as a foundation for a paragraph. + *

+ * BlockFlows must be parented by a FlowFigure. {@link FlowPage} can be + * used as a "root" block and can be parented by normal Figures. + *

+ * Only {@link FlowFigure}s can be added to a BlockFlow. + *

+ * WARNING: This class is not intended to be subclassed by clients. + * @author hudsonr + * @since 2.1 + */ +public class BlockFlow + : FlowFigure +{ + +private const BlockBox blockBox; +private int alignment = PositionConstants.NONE; +private int orientation = DWT.NONE; +private bool bidiValid; + +/** + * Constructs a new BlockFlow. + */ +public this() { + blockBox = createBlockBox(); +} + +/** + * BlockFlows contribute a paragraph separator so as to keep the Bidi state of the text + * on either side of this block from affecting each other. Since each block is like a + * different paragraph, it does not contribute any actual text to its containing block. + * + * @see dwtx.draw2d.text.FlowFigure#contributeBidi(dwtx.draw2d.text.BidiProcessor) + */ +protected void contributeBidi(BidiProcessor proc) { + proc.addControlChar(BidiChars.P_SEP); +} + +BlockBox createBlockBox() { + return new BlockBox(this); +} + +/** + * @see dwtx.draw2d.text.FlowFigure#createDefaultFlowLayout() + */ +protected FlowFigureLayout createDefaultFlowLayout() { + return new BlockFlowLayout(this); +} + +/** + * Returns the BlockBox associated with this. + * @return This BlockFlow's BlockBox + */ +protected BlockBox getBlockBox() { + return blockBox; +} +package BlockBox getBlockBox_package() { + return getBlockBox(); +} + +int getBottomMargin() { + int margin = 0; + if (auto border = cast(FlowBorder)getBorder() ) { + return border.getBottomMargin(); + } + List children = getChildren(); + int childIndex = children.size() - 1; + if (childIndex >= 0 && null !is cast(BlockFlow)children.get(childIndex) ) { + margin = Math.max(margin, + (cast(BlockFlow)children.get(childIndex)).getBottomMargin()); + } + return margin; +} + +/** + * Returns the effective horizontal alignment. This method will never return {@link + * PositionConstants#NONE}. If the value is none, it will return the inherited alignment. + * If no alignment was inherited, it will return the default alignment ({@link + * PositionConstants#LEFT}). + * @return the effective alignment + */ +public int getHorizontalAligment() { + if (alignment !is PositionConstants.NONE) + return alignment; + IFigure parent = getParent(); + while (parent !is null && !( null !is cast(BlockFlow)parent )) + parent = parent.getParent(); + if (parent !is null) + return (cast(BlockFlow)parent).getHorizontalAligment(); + return PositionConstants.LEFT; +} + +int getLeftMargin() { + if ( auto b = cast(FlowBorder)getBorder() ) + return b.getLeftMargin(); + return 0; +} + +/** + * Returns the orientation set on this block. + * @return LTR, RTL or NONE + * @see #setOrientation(int) + * @since 3.1 + */ +public int getLocalOrientation() { + return orientation; +} + +/** + * Returns the horizontal alignment set on this block. + * @return LEFT, RIGHT, ALWAYS_LEFT, ALWAYS_RIGHT, NONE + * @see #setHorizontalAligment(int) + * @since 3.1 + */ +public int getLocalHorizontalAlignment() { + return alignment; +} + +/** + * Returns this block's Bidi orientation. If none was set on this block, it + * will inherit the one from its containing block. If there is no containing block, it + * will return the default orientation (DWT.RIGHT_TO_LEFT if mirrored; DWT.LEFT_TO_RIGHT + * otherwise). + * + * @return DWT.RIGHT_TO_LEFT or DWT.LEFT_TO_RIGHT + * @see #setOrientation(int) + * @since 3.1 + */ +public int getOrientation() { + if (orientation !is DWT.NONE) + return orientation; + IFigure parent = getParent(); + while (parent !is null && !(null !is cast(BlockFlow)parent )) + parent = parent.getParent(); + if (parent !is null) + return (cast(BlockFlow)parent).getOrientation(); + return isMirrored() ? DWT.RIGHT_TO_LEFT : DWT.LEFT_TO_RIGHT; +} + +int getRightMargin() { + if (auto b = cast(FlowBorder)getBorder() ) + return b.getRightMargin(); + return 0; +} + +int getTopMargin() { + int margin = 0; + if (auto border = cast(FlowBorder)getBorder() ) { + return border.getTopMargin(); + } + List children = getChildren(); + if (children.size() > 0 && null !is cast(BlockFlow)children.get(0) ) { + margin = Math.max(margin, + (cast(BlockFlow)children.get(0)).getTopMargin()); + } + return margin; +} + +/** + * @see dwtx.draw2d.Figure#paintBorder(dwtx.draw2d.Graphics) + */ +public void paintBorder(Graphics graphics) { + if ( auto b = cast(FlowBorder)getBorder() ) { + Rectangle where = getBlockBox().toRectangle(); + where.crop(new Insets(getTopMargin(), getLeftMargin(), + getBottomMargin(), getRightMargin())); + (cast(FlowBorder)getBorder()).paint(this, graphics, where, DWT.LEAD | DWT.TRAIL); + } else + super.paintBorder(graphics); + if (selectionStart !is -1) { + graphics.restoreState(); + graphics.setXORMode(true); + graphics.setBackgroundColor(ColorConstants.white); + graphics.fillRectangle(getBounds()); + } +} + +/** + * @see dwtx.draw2d.text.FlowFigure#postValidate() + */ +public void postValidate() { + Rectangle newBounds = getBlockBox().toRectangle(); + newBounds.crop(new Insets(getTopMargin(), getLeftMargin(), + getBottomMargin(), getRightMargin())); + setBounds(newBounds); +} + +/** + * @see FlowFigure#revalidate() + */ +public void revalidate() { + BlockFlowLayout layout = cast(BlockFlowLayout)getLayoutManager(); + layout.blockContentsChanged(); + super.revalidate(); +} + +/** + * A Block will invalidate the Bidi state of all its children, so that it is + * re-evaluated when this block is next validated. + * @see dwtx.draw2d.text.FlowFigure#revalidateBidi(dwtx.draw2d.IFigure) + */ +protected void revalidateBidi(IFigure origin) { + if (bidiValid) { + bidiValid = false; + revalidate(); + } +} + +/** + * Sets the horitontal aligment of the block. Valid values are: + *

    + *
  • {@link PositionConstants#NONE NONE} - (default) Alignment is inherited from + * parent. If a parent is not found then LEFT is used.
  • + *
  • {@link PositionConstants#LEFT} - Alignment is with leading edge
  • + *
  • {@link PositionConstants#RIGHT} - Alignment is with trailing edge
  • + *
  • {@link PositionConstants#CENTER}
  • + *
  • {@link PositionConstants#ALWAYS_LEFT} - Left, irrespective of orientation
  • + *
  • {@link PositionConstants#ALWAYS_RIGHT} - Right, irrespective of orientation
  • + *
+ * @param value the aligment + * @see #getHorizontalAligment() + */ +public void setHorizontalAligment(int value) { + value &= PositionConstants.LEFT | PositionConstants.CENTER | PositionConstants.RIGHT + | PositionConstants.ALWAYS_LEFT | PositionConstants.ALWAYS_RIGHT; + if (value is alignment) + return; + alignment = value; + revalidate(); +} + +/** + * Sets the orientation for this block. Orientation can be one of: + *
    + *
  • {@link DWT#LEFT_TO_RIGHT} + *
  • {@link DWT#RIGHT_TO_LEFT} + *
  • {@link DWT#NONE} (default) + *
+ * NONE is used to indicate that orientation should be inherited from the + * encompassing block. + * + * @param orientation LTR, RTL or NONE + * @see #getOrientation() + * @since 3.1 + */ +public void setOrientation(int orientation) { + orientation &= DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT; + if (this.orientation is orientation) + return; + this.orientation = orientation; + revalidateBidi(this); +} + +/** + * @see dwtx.draw2d.Figure#useLocalCoordinates() + */ +protected bool useLocalCoordinates() { + return true; +} + +/** + * Re-evaluate the Bidi state of all the fragments if it has been + * invalidated. + * @see dwtx.draw2d.IFigure#validate() + */ +public void validate() { + if (!bidiValid) { + BidiProcessor.INSTANCE.setOrientation(getOrientation()); + if (getOrientation() is DWT.LEFT_TO_RIGHT && isMirrored()) + BidiProcessor.INSTANCE.addControlChar(BidiChars.LRE); + super.contributeBidi(BidiProcessor.INSTANCE); + BidiProcessor.INSTANCE.process(); + bidiValid = true; + } + super.validate(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/BlockFlowLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/BlockFlowLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,251 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.BlockFlowLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwt.DWT; +import dwtx.draw2d.Figure; +import dwtx.draw2d.PositionConstants; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.text.FlowContainerLayout; +import dwtx.draw2d.text.BlockBox; +import dwtx.draw2d.text.BlockFlow; +import dwtx.draw2d.text.CompositeBox; +import dwtx.draw2d.text.FlowFigure; +import dwtx.draw2d.text.LineRoot; + +/** + * The layout for {@link BlockFlow} figures. + * + *

WARNING: This class is not intended to be subclassed by clients. + * @author hudsonr + * @since 2.1 + */ +public class BlockFlowLayout + : FlowContainerLayout +{ + +BlockBox blockBox; +bool blockInvalid = false; +private bool continueOnSameLine = false; +private CompositeBox previousLine = null; + +/** + * Creates a new BlockFlowLayout with the given BlockFlow. + * @param blockFlow the BlockFlow + */ +public this(BlockFlow blockFlow) { + super(blockFlow); +} + +private void addBelowPreviousLine(CompositeBox line) { + if (previousLine is null) + line.setLineTop(line.getTopMargin()); + else + line.setLineTop(previousLine.getBaseline() + previousLine.getDescent() + + Math.max(previousLine.getBottomMargin(), line.getTopMargin())); + + int alignment = getBlockFlow().getHorizontalAligment(); + if (alignment is PositionConstants.LEFT || alignment is PositionConstants.RIGHT) { + int orientation = getBlockFlow().getOrientation(); + if (alignment is PositionConstants.LEFT) + alignment = orientation is DWT.LEFT_TO_RIGHT + ? PositionConstants.ALWAYS_LEFT : PositionConstants.ALWAYS_RIGHT; + else + alignment = orientation is DWT.LEFT_TO_RIGHT + ? PositionConstants.ALWAYS_RIGHT : PositionConstants.ALWAYS_LEFT; + } + if (alignment !is PositionConstants.CENTER && getBlockFlow().isMirrored()) + alignment = (PositionConstants.ALWAYS_LEFT | PositionConstants.ALWAYS_RIGHT) + & ~alignment; + + switch (alignment) { + case PositionConstants.ALWAYS_RIGHT: + line.setX(blockBox.getRecommendedWidth() - line.getWidth()); + break; + case PositionConstants.CENTER: + line.setX((blockBox.getRecommendedWidth() - line.getWidth()) / 2); + break; + case PositionConstants.ALWAYS_LEFT: + line.setX(0); + break; + default: + throw new RuntimeException("Unexpected state"); //$NON-NLS-1$ + } + blockBox.add(line); + previousLine = line; +} + +/** + * Align the line horizontally and then commit it. + */ +protected void addCurrentLine() { + addBelowPreviousLine(currentLine); + (cast(LineRoot)currentLine).commit(); +} + +/** + * @see FlowContext#addLine(CompositeBox) + */ +public void addLine(CompositeBox box) { + endLine(); + addBelowPreviousLine(box); +} + +/** + * Marks the blocks contents as changed. This means that children will be invalidated + * during validation. + * @since 3.1 + */ +public void blockContentsChanged() { + blockInvalid = true; +} + +/** + * @see FlowContainerLayout#cleanup() + */ +protected void cleanup() { + super.cleanup(); + previousLine = null; +} + +/** + * @see FlowContainerLayout#createNewLine() + */ +protected void createNewLine() { + currentLine = new LineRoot(getBlockFlow().isMirrored()); + currentLine.setRecommendedWidth(blockBox.getRecommendedWidth()); +} + +/** + * Called by flush(), adds the BlockBox associated with this BlockFlowLayout + * to the current line and then ends the line. + */ +protected void endBlock() { + if (blockInvalid) { + Insets insets = getBlockFlow().getInsets(); + blockBox.height += insets.getHeight(); + blockBox.width += insets.getWidth(); + } + + if (getContext() !is null) + getContext().addLine(blockBox); + + if (blockInvalid) { + blockInvalid = false; + List v = getFlowFigure().getChildren(); + for (int i = 0; i < v.size(); i++) + (cast(FlowFigure)v.get(i)).postValidate(); + } +} + +/** + * @see FlowContext#endLine() + */ +public void endLine() { + if (currentLine is null || !currentLine.isOccupied()) + return; + addCurrentLine(); + currentLine = null; +} + +/** + * @see FlowContainerLayout#flush() + */ +protected void flush() { + endLine(); + endBlock(); +} + +bool forceChildInvalidation(Figure f) { + return blockInvalid; +} + +/** + * Returns the BlockFlow associated with this BlockFlowLayout + * @return the BlockFlow + */ +protected final BlockFlow getBlockFlow() { + return cast(BlockFlow)getFlowFigure(); +} + +int getContextWidth() { + return getContext().getRemainingLineWidth(); +} + +/** + * @see FlowContext#getContinueOnSameLine() + */ +public bool getContinueOnSameLine() { + return continueOnSameLine; +} + +/** + * @see FlowContext#getWidthLookahead(FlowFigure, int[]) + */ +public void getWidthLookahead(FlowFigure child, int result[]) { + List children = getFlowFigure().getChildren(); + int index = -1; + if (child !is null) + index = children.indexOf(child); + + for (int i = index + 1; i < children.size(); i++) + if ((cast(FlowFigure)children.get(i)).addLeadingWordRequirements(result)) + return; +} + +/** + * @see FlowContainerLayout#preLayout() + */ +protected void preLayout() { + setContinueOnSameLine(false); + blockBox = getBlockFlow().getBlockBox_package(); + setupBlock(); + //Probably could setup current and previous line here, or just previous +} + +/** + * @see dwtx.draw2d.text.FlowContext#setContinueOnSameLine(bool) + */ +public void setContinueOnSameLine(bool value) { + continueOnSameLine = value; +} + +/** + * sets up the single block that contains all of the lines. + */ +protected void setupBlock() { + int recommended = getContextWidth(); + if (recommended is Integer.MAX_VALUE) + recommended = -1; + BlockFlow bf = getBlockFlow(); + if (recommended > 0) { + int borderCorrection = bf.getInsets().getWidth() + bf.getLeftMargin() + + bf.getRightMargin(); + recommended = Math.max(0, recommended - borderCorrection); + } + + if (recommended !is blockBox.recommendedWidth) { + blockInvalid = true; + blockBox.setRecommendedWidth(recommended); + } + + if (blockInvalid) { + blockBox.height = 0; + blockBox.setWidth(Math.max(0, recommended)); + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/CaretInfo.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/CaretInfo.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.text.CaretInfo; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.geometry.Translatable; + +/** + * Stores positional information about where a caret should be placed. This structure + * currently only offers integer precision. Scaling operations will result in rounding. + * @since 3.1 + */ +public class CaretInfo + : Translatable +{ + +private int ascent, lineAscent, descent, lineDescent, baseline, x; + +/** + * Constructor for use by TextFlow. Constructs a new CaretInfo with the figure's ascent + * and descent and line information. + *

+ * WARNING: This constructor should not be called by clients. It is for use by + * {@link TextFlow}, and may change in future releases. + * @param x the x location + * @param y the y location of the top of the caret + * @param ascent the ascent + * @param descent the descent + * @param lineAscent the ascent of the line on which the caret is placed + * @param lineDescent the descent of the line on which the caret is placed + */ +/+protected+/ this(int x, int y, int ascent, int descent, int lineAscent, int lineDescent) { + this.x = x; + this.baseline = y + ascent; + this.ascent = ascent; + this.descent = descent; + this.lineAscent = lineAscent; + this.lineDescent = lineDescent; +} + +/** + * Constructs a CaretInfo object by copying the values from another instance. + * @param info the reference + * @since 3.2 + */ +/+protected+/ this(CaretInfo info) { + this.ascent = info.ascent; + this.baseline = info.baseline; + this.descent = info.descent; + this.lineAscent = info.lineAscent; + this.lineDescent = info.lineDescent; + this.x = info.x; +} + +/** + * Returns the y location of the baseline. + * @return the y coordinate of the baseline + */ +public int getBaseline() { + return baseline; +} + +/** + * Returns the total height of the caret. The height is the sum of the ascent and descent. + * @return the height + */ +public int getHeight() { + return ascent + descent; +} + +/** + * @return the total height of the line on which the caret is placed + */ +public int getLineHeight() { + return lineAscent + lineDescent; +} + +/** + * @return the y location of the line on which the caret is placed + */ +public int getLineY() { + return baseline - lineAscent; +} + +/** + * Returns the x location of the caret. + * @return the x coordinate + */ +public int getX() { + return x; +} + +/** + * Returns the y location of the caret. + * @return the y coordinate + */ +public int getY() { + return baseline - ascent; +} + +/** + * @see Translatable#performScale(double) + */ +public void performScale(double factor) { + x *= factor; + baseline *= factor; + descent *= factor; + ascent *= factor; + lineAscent *= factor; + lineDescent *= factor; +} + +/** + * @see Translatable#performTranslate(int, int) + */ +public void performTranslate(int dx, int dy) { + x += dx; + baseline += dy; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/CompositeBox.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/CompositeBox.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.CompositeBox; + +import dwt.dwthelper.utils; +import dwtx.draw2d.text.FlowBox; + +/** + * A FlowBox that can contain other FlowBoxes. The contained FlowBoxes are called + * fragments. + * @author hudsonr + * @since 2.1 + */ +public abstract class CompositeBox + : FlowBox +{ + +int recommendedWidth = -1; + +/** + * Adds the given box and updates properties of this composite box. + * @param box the child being added + */ +public abstract void add(FlowBox box); + + +abstract int getBottomMargin(); + +/** + * Returns the recommended width for this CompositeBox. + * @return the recommended width + */ +public int getRecommendedWidth() { + return recommendedWidth; +} + +abstract int getTopMargin(); + +/** + * Sets the recommended width for this CompositeBox. + * @param w the width + */ +public void setRecommendedWidth(int w) { + recommendedWidth = w; +} + +/** + * Positions the box vertically by setting the y coordinate for the top of the content of + * the line. For internal use only. + * @param top the y coordinate + * @since 3.1 + */ +public abstract void setLineTop(int top); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/ContentBox.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/ContentBox.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.ContentBox; + +import dwt.dwthelper.utils; +import dwtx.draw2d.text.FlowBox; +import dwtx.draw2d.text.LineRoot; + +/** + * FlowBoxes that are leaf nodes. + * + * @author Pratik Shah + * @since 3.1 + */ +public abstract class ContentBox : FlowBox { + +private int bidiLevel = -1; +private LineRoot lineRoot; + +/** + * @see FlowBox#getBaseline() + */ +public int getBaseline() { + return lineRoot.getBaseline(); +} + +/** + * @return the Bidi level of this box, if one has been set; -1 otherwise + * @see #setBidiLevel(int) + */ +public int getBidiLevel() { + return bidiLevel; +} + +/** + * @see dwtx.draw2d.text.FlowBox#getLineRoot() + */ +LineRoot getLineRoot() { + return lineRoot; +} + +/** + * Returns true if the bidi level for this box is specified, and is not the + * default level (0). + * @see dwtx.draw2d.text.FlowBox#requiresBidi() + */ +public bool requiresBidi() { + return bidiLevel > 0; +} + +/** + * Sets the Bidi level of this fragment. It is used to rearrange fragments as defined + * by the Unicode Bi-directional algorithm. Valid values are -1 (meaning no Bidi level), + * or any non-negative integer less than 62. + * @param newLevel the new BidiLevel + * @see #getBidiLevel() + */ +public void setBidiLevel(int newLevel) { + bidiLevel = newLevel; +} + +void setLineRoot(LineRoot root) { + this.lineRoot = root; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/FlowAdapter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/FlowAdapter.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.FlowAdapter; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.IFigure; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.text.FlowFigure; +import dwtx.draw2d.text.BidiProcessor; +import dwtx.draw2d.text.FlowFigureLayout; +import dwtx.draw2d.text.FlowContext; +import dwtx.draw2d.text.ContentBox; +import dwtx.draw2d.text.BidiInfo; +import dwtx.draw2d.text.BidiChars; + + +/** + * Adapts non-flow figures for use within a parent hierarchy requiring flow figures. + * Normal draw2d figures can be added as children. If a normal LayoutManager is set, the + * children will be positioned by that layout manager. The size of this figure within + * the flow will be determined by its preferred size. + *

+ * WARNING: This class is not intended to be subclassed by clients. + * + * @author Pratik Shah + * @since 3.1 + */ +public class FlowAdapter + : FlowFigure +{ + +private FlowContext context; +private FigureBox box; + +public this(){ + box = new FigureBox(); +} +/** + * This FlowFigure contributes an Object Replacement Character. + * @see FlowFigure#contributeBidi(BidiProcessor) + */ +protected void contributeBidi(BidiProcessor proc) { + box.setBidiLevel(-1); + // contributes a single object replacement char + proc.add(this, BidiChars.OBJ); +} + +/** + * @return null + * @see dwtx.draw2d.text.FlowFigure#createDefaultFlowLayout() + */ +protected FlowFigureLayout createDefaultFlowLayout() { + return null; +} + +/** + * Sizes the content box to be big enough to display all figures. Wraps to the next line + * if there is not enough room on the current one. + * @see dwtx.draw2d.Figure#layout() + */ +protected void layout() { + int wHint = context.getRemainingLineWidth(); + if (wHint is Integer.MAX_VALUE) + wHint = -1; + Dimension prefSize = getPreferredSize(wHint, -1); + if (context.isCurrentLineOccupied() + && prefSize.width > context.getRemainingLineWidth()) { + context.endLine(); + prefSize = getPreferredSize(context.getRemainingLineWidth(), -1); + } + box.setSize(prefSize); + context.addToCurrentLine(box); +} + +/** + * Updates the bounds of this figure to match that of its content box, and lays out this + * figure's children. + * @see FlowFigure#postValidate() + */ +public void postValidate() { + setBounds(new Rectangle(box.getX(), box.getBaseline() - box.ascent, + box.width, box.ascent)); + super.layout(); + for (Iterator itr = getChildren().iterator(); itr.hasNext();) + (cast(IFigure)itr.next()).validate(); +} + +/** + * Sets the bidi level of the content box associated with this Figure + * @see FlowFigure#setBidiInfo(BidiInfo) + */ +public void setBidiInfo(BidiInfo info) { + box.setBidiLevel(info.levelInfo[0]); +} + +/** + * @see dwtx.draw2d.IFigure#setBounds(dwtx.draw2d.geometry.Rectangle) + */ +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) { + fireFigureMoved(); + repaint(); + } +} + +/** + * @see FlowFigure#setFlowContext(FlowContext) + */ +public void setFlowContext(FlowContext flowContext) { + context = flowContext; +} + +/** + * Do not validate children. + * @see dwtx.draw2d.IFigure#validate() + */ +public void validate() { + if (isValid()) + return; + setValid(true); + layout(); +} + +private class FigureBox : ContentBox { + private int ascent; + public bool containsPoint(int x, int y) { + return this.outer.containsPoint(x, y); + } + public int getAscent() { + return ascent; + } + public int getDescent() { + return 0; + } + public void setSize(Dimension size) { + ascent = size.height; + width = size.width; + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/FlowBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/FlowBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.text.FlowBorder; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.Border; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.text.FlowFigure; + +/** + * Experimental API. This is a special type of border for use with {@link + * dwtx.draw2d.text.FlowFigure}s. This interface should not be implemented by + * clients. Clients should extend {@link dwtx.draw2d.text.AbstractFlowBorder}. + * @since 3.1 + */ +public interface FlowBorder : Border { + +/** + * Returns the collapsable bottom margin in pixels. Margin is the space external to the + * border and the flow box on which it is rendered. Vertical margins (top and bottom) may + * collapse in some situations, such as adjacent or nested blocks. + * @return the bottom margin + * @since 3.1 + */ +int getBottomMargin(); + +/** + * Returns the left margin in pixels. Margin is the space external to the border and the + * flow box on which it is rendered. + * @return the left margin + * @since 3.1 + */ +int getLeftMargin(); + +/** + * Returns the right margin in pixels. Margin is the space external to the border and the + * flow box on which it is rendered. + * @return the right margin + * @since 3.1 + */ +int getRightMargin(); + +/** + * Returns the collapsable top margin in pixels. Margin is the space external to the + * border and the flow box on which it is rendered. Vertical margins (top and bottom) may + * collapse in some situations, such as adjacent or nested blocks. + * @return the top margin + * @since 3.1 + */ +int getTopMargin(); + +/** + * Paints the border around the given box location. The border is asked to paint each of + * the FlowFigure's boxes. The sideInfo parameter is used to indicate whether + * the left and right sides should be rendered. This parameter will contain the following + * bit flags: + *

    + *
  • {@link dwt.DWT#LEAD} + *
  • {@link dwt.DWT#TRAIL} + *
  • {@link dwt.DWT#RIGHT_TO_LEFT} + *
+ * @param figure the flow figure whose border is being painted + * @param g the graphics + * @param where the relative location of the box + * @param sides bits indicating sides and bidi orientation + * @since 3.1 + */ +void paint(FlowFigure figure, Graphics g, Rectangle where, int sides); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/FlowBox.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/FlowBox.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.FlowBox; + +import dwt.dwthelper.utils; +import dwtx.draw2d.text.LineRoot; + +/** + * A Geometric object for representing a region on a line of Text. This class adds the + * notion of a baseline to {@link dwtx.draw2d.geometry.Rectangle}. Ascent is + * the distance above the baseline. Descent is the distance below the baseline. + *

+ * This class should not be treated as a Rectangle by clients. It is + * important to use getters when available for lazy calculation of values. + * + * @author hudsonr + * @since 2.1 + */ +public abstract class FlowBox { + +int width; + +/** + * The x location + */ +private int x; + +/** + * This method must be called on a block that is completely positioned and committed. + * @param x X + * @param y Y + * @return true if the FlowBox contains the point + */ +public abstract bool containsPoint(int x, int y); + +/** + * Returns the amount of line content in pixels which is above the baseline. Ascent and + * descent are used to space consecutive lines apart. Certain types of line content, such + * as borders, extend beyond the ascent and descent. + * @return the descent in pixels below the baseline + */ +public abstract int getAscent(); + +/** + * Returns y coordinate for the box's baseline. + * @return the baseline location + * @since 3.1 + */ +public abstract int getBaseline(); + +/** + * Returns the amount of line content in pixels which is below the baseline. + * @return the descent in pixels + * @see #getAscent() + */ +public abstract int getDescent(); + +/** + * Returns the root LineBox in which this box is placed. The root line is interesting + * when painting selection or hit testing. All boxes in a line should render selection at + * the same top and bottom location. + * @return the line root. + * @since 3.1 + */ +abstract LineRoot getLineRoot(); + +/** + * Returns the outer ascent of this box. The outer ascent is the ascent above the + * baseline including the border size and margin. This is used when adding content into a + * LineBox. The linebox's own border must be drawn around the children. + */ +int getOuterAscent() { + return getAscent(); +} + +/** + * Returns the outer descent of this box. The outer descent is the space below the + * baseline including the border size and margin. This is used when adding content into a + * LineBox. The linebox's own border must be drawn around the children. + */ +int getOuterDescent() { + return getDescent(); +} + +int getAscentWithBorder() { + throw new RuntimeException("Not valid on this box type"); //$NON-NLS-1$ +} + +int getDescentWithBorder() { + throw new RuntimeException("Not valid on this box type"); //$NON-NLS-1$ +} + +/** + * Returns the width of the box. + * @return the box's width + */ +public int getWidth() { + return width; +} + +/** + * Returns the X coordinate of the box. + * @return the x coordinate + * @since 3.1 + */ +public int getX() { + return x; +} + +/** + * Returns true if any of the children are bi-directional. Default + * implementation returns false. + * + * @return true if the box is bi-directional + * @since 3.1 + */ +public bool requiresBidi() { + return false; +} + +/** + * Sets the line root. + * @param root the line root + * @since 3.1 + */ +void setLineRoot(LineRoot root) { +} + +/** + * Sets the width of the box. + * @param width the new width + * @since 3.1 + */ +public void setWidth(int width) { + this.width = width; +} + +/** + * Sets the x coordinate for this box. + * @param x the x coordinate + * @since 3.1 + */ +public void setX(int x) { + this.x = x; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/FlowContainerLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/FlowContainerLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.FlowContainerLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.Figure; +import dwtx.draw2d.text.FlowFigureLayout; +import dwtx.draw2d.text.FlowBox; +import dwtx.draw2d.text.LineBox; +import dwtx.draw2d.text.FlowContext; +import dwtx.draw2d.text.FlowFigure; + +/** + * A layout for FlowFigures with children. + * + *

WARNING: This class is not intended to be subclassed by clients. + * @author hudsonr + * @since 2.1 + */ +public abstract class FlowContainerLayout + : FlowFigureLayout + , FlowContext +{ + +/** + * the current line + */ +LineBox currentLine; + +/** + * @see dwtx.draw2d.text.FlowFigureLayout#FlowFigureLayout(FlowFigure) + */ +protected this(FlowFigure flowFigure) { + super(flowFigure); +} + +/** + * Adds the given box the current line and clears the context's state. + * @see dwtx.draw2d.text.FlowContext#addToCurrentLine(FlowBox) + */ +public void addToCurrentLine(FlowBox child) { + getCurrentLine().add(child); + setContinueOnSameLine(false); +} + +/** + * Flush anything pending and free all temporary data used during layout. + */ +protected void cleanup() { + currentLine = null; +} + +/** + * Used by getCurrentLine(). + */ +protected abstract void createNewLine(); + +/** + * Called after {@link #layoutChildren()} when all children have been laid out. This + * method exists to flush the last line. + */ +protected abstract void flush(); + +/** + * FlowBoxes shouldn't be added directly to the current line. Use + * {@link #addToCurrentLine(FlowBox)} for that. + * @see dwtx.draw2d.text.FlowContext#getCurrentLine() + */ +LineBox getCurrentLine() { + if (currentLine is null) + createNewLine(); + return currentLine; +} + +/** + * @see FlowContext#getRemainingLineWidth() + */ +public int getRemainingLineWidth() { + return getCurrentLine().getAvailableWidth(); +} + +/** + * @see FlowContext#isCurrentLineOccupied() + */ +public bool isCurrentLineOccupied() { + return currentLine !is null && currentLine.isOccupied(); +} + +/** + * @see FlowFigureLayout#layout() + */ +protected void layout() { + preLayout(); + layoutChildren(); + flush(); + cleanup(); +} + +/** + * Layout all children. + */ +protected void layoutChildren() { + List children = getFlowFigure().getChildren(); + for (int i = 0; i < children.size(); i++) { + Figure f = cast(Figure)children.get(i); + if (forceChildInvalidation(f)) + f.invalidate(); + f.validate(); + } +} + +bool forceChildInvalidation(Figure f) { + return true; +} + +/** + * Called before layoutChildren() to setup any necessary state. + */ +protected abstract void preLayout(); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/FlowContext.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/FlowContext.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.FlowContext; + +import dwt.dwthelper.utils; +import dwtx.draw2d.text.FlowBox; +import dwtx.draw2d.text.CompositeBox; +import dwtx.draw2d.text.FlowFigure; + +/** + * The context that a {@link FlowFigureLayout} uses to perform its layout. + * + *

WARNING: This interface is not intended to be implemented by clients. It exists to + * define the API between the layout and its context. + */ +public interface FlowContext { + +/** + * Adds the given box into the current line. + * @param box the FlowBox to add + */ +void addToCurrentLine(FlowBox box); + +/** + * Adds an entire line into the context. If there is a previous line, it is ended. + * @param box the line being added + * @since 3.1 + */ +void addLine(CompositeBox box); + +/** + * The current line should be committed if it is occupied, and then set to + * null. Otherwise, do nothing. + */ +void endLine(); + +/** + * This method can be used to query the amount of space left on the current line. It + * can help determine where to wrap during layout. + * @return the amount of space left on the current line + * @since 3.1 + */ +int getRemainingLineWidth(); + +/** + * This method is used to convey layout state to different FlowFigures. This state is + * cleared when a fragment is added to the current line and once the layout is complete. + * @return true if the next fragment should be placed on the current line + * @since 3.1 + * @see #setContinueOnSameLine(bool) + */ +bool getContinueOnSameLine(); + +/** + * This method looks ahead for line-breaks. When laying out, this method can be used + * to determine the next line-break across multiple figures. + * + * @param child the search will occur starting from the figure after the given child + * @param width the width before the next line-break (if one's found; all the width, + * otherwise) will be added on to the first int in the given array + * @since 3.1 + */ +void getWidthLookahead(FlowFigure child, int width[]); + +/** + * @return true if the current line contains any fragments + */ +bool isCurrentLineOccupied(); + +/** + * This method is used to convey layout state to different FlowFigures. This state is + * cleared when a fragment is added and once the layout is complete. + * + * @param value true indicates that the first fragment of the next TextFlow + * should be laid out on the current line, and not a new one + * @since 3.1 + * @see #getContinueOnSameLine() + */ +void setContinueOnSameLine(bool value); + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/FlowFigure.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/FlowFigure.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.FlowFigure; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.Figure; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.text.BidiProcessor; +import dwtx.draw2d.text.FlowFigureLayout; +import dwtx.draw2d.text.BidiInfo; +import dwtx.draw2d.text.FlowContext; + +/** + * The base implementation for text flow figures. A flow figure is used to render a + * document in which elements are laid out horizontally within a "line" until that line is + * filled. Layout continues on the next line. + * + *

WARNING: This class is not intended to be subclassed by clients. Future versions may + * contain additional abstract methods. + * + * @author hudsonr + * @since 2.1 + */ +public abstract class FlowFigure + : Figure +{ + alias Figure.add add; + +/** + * integer indicating whether selection should be displayed. + */ +protected int selectionStart = -1; + +/** + * Constructs a new FlowFigure. + */ +public this() { + setLayoutManager(createDefaultFlowLayout()); +} + +/** + * If the child is a FlowFigure, its FlowContext is passed to it. + * @see dwtx.draw2d.IFigure#add(IFigure, Object, int) + */ +public void add(IFigure child, Object constraint, int index) { + super.add(child, constraint, index); + //If this layout manager is a FlowContext, then the child *must* be a FlowFigure + if (null !is cast(FlowContext)getLayoutManager() ) + (cast(FlowFigure)child).setFlowContext(cast(FlowContext)getLayoutManager()); + revalidateBidi(this); +} + +/** + * Calculates the width of text before the next line-break is encountered. + *

+ * Default implementation treats each FlowFigure as a line-break. It adds no width and + * returns true. Sub-classes should override as needed. + * + * @param width the width before the next line-break (if one's found; all the width, + * otherwise) will be added on to the first int in the given array + * @return bool indicating whether or not a line-break was found + * @since 3.1 + */ +public bool addLeadingWordRequirements(int[] width) { + return true; +} + +/** + * FlowFigures can contribute text for their block to the given {@link BidiProcessor}, + * which will process the contributions to determine Bidi levels and shaping requirements. + *

+ * This method is invoked as part of validating Bidi. + *

+ * Sub-classes that cache the BidiInfo and/or the bidi level in ContentBoxes should clear + * the cached values when this method is invoked. + * + * @param proc the BidiProcessor to which contributions should be made + * @see BidiProcessor#add(FlowFigure, String) + * @since 3.1 + */ +protected void contributeBidi(BidiProcessor proc) { + for (Iterator iter = getChildren().iterator(); iter.hasNext();) + (cast(FlowFigure)iter.next()).contributeBidi(proc); +} + +/** + * Creates the default layout manager + * @return The default layout + */ +protected abstract FlowFigureLayout createDefaultFlowLayout(); + +/** + * Called after validate has occurred. This is used to update the bounds of the FlowFigure + * to encompass its new flow boxed created during validate. + */ +public abstract void postValidate(); + +/** + * Overridden to revalidateBidi when fragments are removed. + * @see dwtx.draw2d.IFigure#remove(dwtx.draw2d.IFigure) + */ +public void remove(IFigure figure) { + super.remove(figure); + revalidateBidi(this); +} + +/** + * This method should be invoked whenever a change that can potentially affect the + * Bidi evaluation is made (eg., adding or removing children, changing text, etc.). + *

+ * The default implementation delegates the revalidation task to the parent. Only + * {@link BlockFlow#revalidateBidi(IFigure) blocks} perform the actual revalidation. + *

+ * The given IFigure is the one that triggered the revalidation. This can be used to + * optimize bidi evaluation. + * + * @param origin the figure that was revalidated + * @since 3.1 + */ +protected void revalidateBidi(IFigure origin) { + if (getParent() !is null) + (cast(FlowFigure)getParent()).revalidateBidi(origin); +} + +/** + * Sets the bidi information for this figure. A flow figure contributes bidi text in + * {@link #contributeBidi(BidiProcessor)}. If the figure contributes text associated with + * it, this method is called back to indicate the bidi properties for that text within its + * block. + * + * @param info the BidiInfo for this figure + * @since 3.1 + */ +public void setBidiInfo(BidiInfo info) { } + +/** + * FlowFigures override setBounds() to prevent translation of children. "bounds" is a + * derived property for FlowFigures, calculated from the fragments that make up the + * FlowFigure. + * @see Figure#setBounds(Rectangle) + */ +public void setBounds(Rectangle r) { + if (bounds.opEquals(r)) + return; + if (!r.contains(bounds)) + erase(); + bounds.x = r.x; + bounds.y = r.y; + bounds.width = r.width; + bounds.height = r.height; + fireFigureMoved(); + if (isCoordinateSystem()) + fireCoordinateSystemChanged(); + repaint(); +} + +/** + * Sets the flow context. + * @param flowContext the flow context for this flow figure + */ +public void setFlowContext(FlowContext flowContext) { + (cast(FlowFigureLayout)getLayoutManager()).setFlowContext(flowContext); +} + +/** + * Sets the selection or a range of selection. A start value of -1 is used to indicate no + * selection. A start value >=0 indicates show selection. A start and end value can be + * used to represent a range of offsets which should render selection. + * @param start the start offset + * @param end the end offset + * @since 3.1 + */ +public void setSelection(int start, int end) { + if (selectionStart is start) + return; + selectionStart = start; + repaint(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/FlowFigureLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/FlowFigureLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.FlowFigureLayout; + +import dwt.dwthelper.utils; + +import dwtx.draw2d.IFigure; +import dwtx.draw2d.LayoutManager; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.text.FlowContext; +import dwtx.draw2d.text.FlowFigure; + +/** + * A LayoutManager for use with FlowFigure. + * + *

WARNING: This class is not intended to be subclassed by clients. + * @author hudsonr + * @since 2.1 + */ +public abstract class FlowFigureLayout + : LayoutManager +{ + +/** + * The flow context in which this LayoutManager exists. + */ +private FlowContext context; + +/** + * The figure passed by layout(Figure) is held for convenience. + */ +private final FlowFigure flowFigure; + +/** + * Constructs a new FlowFigureLayout with the given FlowFigure. + * @param flowfigure the FlowFigure + */ +protected this(FlowFigure flowfigure) { + this.flowFigure = flowfigure; +} + +/** + * Not applicable. + * @see dwtx.draw2d.LayoutManager#getConstraint(dwtx.draw2d.IFigure) + */ +public Object getConstraint(IFigure child) { + return null; +} + +/** + * Returns this layout's context or null. + * @return null or a context + * @since 3.1 + */ +protected FlowContext getContext() { + return context; +} + +/** + * @return the FlowFigure + */ +protected FlowFigure getFlowFigure() { + return flowFigure; +} + +/** + * Not applicable. + * @see dwtx.draw2d.LayoutManager#getMinimumSize(dwtx.draw2d.IFigure, int, int) + */ +public Dimension getMinimumSize(IFigure container, int wHint, int hHint) { + return null; +} + +/** + * Not applicable. + * @see dwtx.draw2d.LayoutManager#getPreferredSize(dwtx.draw2d.IFigure, int, int) + */ +public Dimension getPreferredSize(IFigure container, int wHint, int hHint) { + return null; +} + +/** + * Not applicable. + * @see dwtx.draw2d.LayoutManager#invalidate() + */ +public void invalidate() { } + +/** + * Called during {@link #layout(IFigure)}. + */ +protected abstract void layout(); + +/** + * @see dwtx.draw2d.LayoutManager#layout(IFigure) + */ +public final void layout(IFigure figure) { + layout (); +} + +/** + * Not applicable. + * @see dwtx.draw2d.LayoutManager#remove(dwtx.draw2d.IFigure) + */ +public void remove(IFigure child) { } + +/** + * Not applicable. + * @see dwtx.draw2d.LayoutManager#setConstraint(dwtx.draw2d.IFigure, java.lang.Object) + */ +public void setConstraint(IFigure child, Object constraint) { } + +/** + * Sets the context for this layout manager. + * @param flowContext the context of this layout + */ +public void setFlowContext(FlowContext flowContext) { + context = flowContext; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/FlowPage.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/FlowPage.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.FlowPage; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.text.BlockFlow; +import dwtx.draw2d.text.FlowFigureLayout; +import dwtx.draw2d.text.PageFlowLayout; +import dwtx.draw2d.text.FlowFigure; + +/** + * The root of a Flow hierarchy. A flow page can be treated as a normal figure, but + * contains FlowFigures. + *

+ * A FlowPage will not have a defined width unless it is inside a figure whose layout + * provides width hints when calling + * {@link dwtx.draw2d.IFigure#getPreferredSize(int, int)}. + * + *

WARNING: This class is not intended to be subclassed by clients. + */ +public class FlowPage + : BlockFlow +{ + alias BlockFlow.add add; + +private Dimension pageSize; +private int recommendedWidth; +private int[] pageSizeCacheKeys; +private Dimension[] pageSizeCacheValues; + +public this(){ + pageSize = new Dimension(); + pageSizeCacheKeys = new int[3]; + pageSizeCacheValues = new Dimension[3]; +} +/** + * @see dwtx.draw2d.Figure#addNotify() + */ +public void addNotify() { + super.addNotify(); + setValid(false); +} + +/** + * @see dwtx.draw2d.text.BlockFlow#createDefaultFlowLayout() + */ +protected FlowFigureLayout createDefaultFlowLayout() { + return new PageFlowLayout(this); +} + +/** + * @see dwtx.draw2d.Figure#getMinimumSize(int, int) + */ +public Dimension getMinimumSize(int w, int h) { + return getPreferredSize(w, h); +} + +/** + * @see dwtx.draw2d.Figure#invalidate() + */ +public void invalidate() { + pageSizeCacheValues = new Dimension[3]; + super.invalidate(); +} + +/** + * @see dwtx.draw2d.Figure#getPreferredSize(int, int) + */ +public Dimension getPreferredSize(int width, int h) { + for (int i = 0; i < 3; i++) { + if (pageSizeCacheKeys[i] is width && pageSizeCacheValues[i] !is null) + return pageSizeCacheValues[i]; + } + + pageSizeCacheKeys[2] = pageSizeCacheKeys[1]; + pageSizeCacheKeys[1] = pageSizeCacheKeys[0]; + pageSizeCacheKeys[0] = width; + + pageSizeCacheValues[2] = pageSizeCacheValues[1]; + pageSizeCacheValues[1] = pageSizeCacheValues[0]; + + //Flowpage must temporarily layout to determine its preferred size + int oldWidth = getPageWidth(); + setPageWidth(width); + validate(); + pageSizeCacheValues[0] = pageSize.getCopy(); + + if (width !is oldWidth) { + setPageWidth(oldWidth); + getUpdateManager().addInvalidFigure(this); + } + return pageSizeCacheValues[0]; +} + +int getPageWidth() { + return recommendedWidth; +} + +/** + * @see BlockFlow#postValidate() + */ +public void postValidate() { + Rectangle r = getBlockBox().toRectangle(); + pageSize.width = r.width; + pageSize.height = r.height; + List v = getChildren(); + for (int i = 0; i < v.size(); i++) + (cast(FlowFigure)v.get(i)).postValidate(); +} + +/** + * Overridden to set valid. + * @see dwtx.draw2d.IFigure#removeNotify() + */ +public void removeNotify() { + super.removeNotify(); + setValid(true); +} + +/** + * @see FlowFigure#setBounds(Rectangle) + */ +public void setBounds(Rectangle r) { + if (getBounds().opEquals(r)) + return; + bool invalidate = getBounds().width !is r.width || getBounds().height !is r.height; + super.setBounds(r); + int newWidth = r.width; + if (invalidate || getPageWidth() !is newWidth) { + setPageWidth(newWidth); + getUpdateManager().addInvalidFigure(this); + } +} + +private void setPageWidth(int width) { + if (recommendedWidth is width) + return; + recommendedWidth = width; + super.invalidate(); +} + +/** + * @see dwtx.draw2d.Figure#validate() + */ +public void validate() { + if (isValid()) + return; + super.validate(); + postValidate(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/FlowUtilities.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/FlowUtilities.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,409 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +module dwtx.draw2d.text.FlowUtilities; + +import dwt.dwthelper.utils; + +import dwtx.dwtxhelper.mangoicu.UBreakIterator; +import dwtx.dwtxhelper.mangoicu.ULocale; + +import dwt.DWT; +import dwt.graphics.Font; +import dwt.graphics.Rectangle; +import dwt.graphics.TextLayout; +import dwt.widgets.Display; +import dwtx.draw2d.FigureUtilities; +import dwtx.draw2d.TextUtilities; +import dwtx.draw2d.text.TextFragmentBox; +import dwtx.draw2d.text.InlineFlow; +import dwtx.draw2d.text.FlowContext; +import dwtx.draw2d.text.FlowBorder; +import dwtx.draw2d.text.ParagraphTextLayout; +import dwtx.draw2d.text.TextFlow; + +interface LookAhead { + int getWidth(); +} + +/** + * Utility class for FlowFigures. + * @author hudsonr + * @since 3.4 + */ +public class FlowUtilities +{ + +/** + * a singleton default instance + */ +public static FlowUtilities INSTANCE; + +private static const UBreakIterator INTERNAL_LINE_BREAK; +private static TextLayout layout; + +static const UBreakIterator LINE_BREAK; + +static this(){ + INSTANCE = new FlowUtilities(); + INTERNAL_LINE_BREAK = UBreakIterator.openLineIterator( ULocale.Default ); + LINE_BREAK = UBreakIterator.openLineIterator( ULocale.Default ); +} + +static bool canBreakAfter(dchar c) { + bool result = CharacterIsWhitespace(c) || c is '-'; + if (!result && (c < 'a' || c > 'z')) { + // chinese characters and such would be caught in here + // LINE_BREAK is used here because INTERNAL_LINE_BREAK might be in use + LINE_BREAK.setText(dcharToString(c) ~ "a"); //$NON-NLS-1$ + result = LINE_BREAK.isBoundary(1); + } + return result; +} + +private static int findFirstDelimeter(String string) { + int macNL = string.indexOf('\r'); + int unixNL = string.indexOf('\n'); + + if (macNL is -1) + macNL = Integer.MAX_VALUE; + if (unixNL is -1) + unixNL = Integer.MAX_VALUE; + + return Math.min(macNL, unixNL); +} + +/** + * Gets the average character width. + * + * @param fragment the supplied TextFragmentBox to use for calculation. + * if the length is 0 or if the width is or below 0, + * the average character width is taken from standard + * font metrics. + * @param font the font to use in case the TextFragmentBox conditions + * above are true. + * @return the average character width + */ +protected float getAverageCharWidth(TextFragmentBox fragment, Font font) { + if (fragment.getWidth() > 0 && fragment.length !is 0) + return fragment.getWidth() / cast(float)fragment.length; + return FigureUtilities.getFontMetrics(font).getAverageCharWidth(); +} + +static int getBorderAscent(InlineFlow owner) { + if (null !is cast(FlowBorder)owner.getBorder() ) { + FlowBorder border = cast(FlowBorder)owner.getBorder(); + return border.getInsets(owner).top; + } + return 0; +} + +static int getBorderAscentWithMargin(InlineFlow owner) { + if (null !is cast(FlowBorder)owner.getBorder() ) { + FlowBorder border = cast(FlowBorder)owner.getBorder(); + return border.getTopMargin() + border.getInsets(owner).top; + } + return 0; +} + +static int getBorderDescent(InlineFlow owner) { + if (null !is cast(FlowBorder)owner.getBorder() ) { + FlowBorder border = cast(FlowBorder)owner.getBorder(); + return border.getInsets(owner).bottom; + } + return 0; +} + +static int getBorderDescentWithMargin(InlineFlow owner) { + if (null !is cast(FlowBorder)owner.getBorder() ) { + FlowBorder border = cast(FlowBorder)owner.getBorder(); + return border.getBottomMargin() + border.getInsets(owner).bottom; + } + return 0; +} + +/** + * Provides a TextLayout that can be used by the Draw2d text package for Bidi. This + * TextLayout should not be disposed by clients. The provided TextLayout's orientation + * will be LTR. + * + * @return an DWT TextLayout that can be used for Bidi + * @since 3.1 + */ +static TextLayout getTextLayout() { + if (layout is null) + layout = new TextLayout(Display.getDefault()); + layout.setOrientation(DWT.LEFT_TO_RIGHT); + return layout; +} + +/** + * @param frag + * @param string + * @param font + * @since 3.1 + */ +private static void initBidi(TextFragmentBox frag, String string, Font font) { + if (frag.requiresBidi()) { + TextLayout textLayout = getTextLayout(); + textLayout.setFont(font); + //$TODO need to insert overrides in front of string. + textLayout.setText(string); + } +} + +private int measureString(TextFragmentBox frag, String string, int guess, Font font) { + if (frag.requiresBidi()) { + // The text and/or could have changed if the lookAhead was invoked. This will + // happen at most once. + return getTextLayoutBounds(string, font, 0, guess - 1).width; + } else + return getTextUtilities().getStringExtents(string.substring(0, guess), font).width; +} + +/** + * Sets up the fragment width based using the font and string passed in. + * + * @param fragment + * the text fragment whose width will be set + * @param font + * the font to be used in the calculation + * @param string + * the string to be used in the calculation + */ +final protected void setupFragment(TextFragmentBox fragment, Font font, String string) { + if (fragment.getWidth() is -1 || fragment.isTruncated()) { + int width; + if (string.length is 0 || fragment.length is 0) + width = 0; + else if (fragment.requiresBidi()) { + width = getTextLayoutBounds(string, font, 0, fragment.length - 1).width; + } else + width = getTextUtilities().getStringExtents(string.substring(0, fragment.length), font).width; + if (fragment.isTruncated()) + width += getEllipsisWidth(font); + fragment.setWidth(width); + } +} +package void setupFragment_package(TextFragmentBox fragment, Font font, String string) { + setupFragment( fragment, font, string ); +} + +/** + * Sets up a fragment and returns the number of characters consumed from the given + * String. An average character width can be provided as a hint for faster calculation. + * If a fragment's bidi level is set, a TextLayout will be used to calculate the width. + * + * @param frag the TextFragmentBox + * @param string the String + * @param font the Font used for measuring + * @param context the flow context + * @param wrapping the word wrap style + * @return the number of characters that will fit in the given space; can be 0 (eg., when + * the first character of the given string is a newline) + */ +final protected int wrapFragmentInContext(TextFragmentBox frag, String string, + FlowContext context, LookAhead lookahead, Font font, int wrapping) { + frag.setTruncated(false); + int strLen = string.length; + if (strLen is 0) { + frag.setWidth(-1); + frag.length = 0; + setupFragment(frag, font, string); + context.addToCurrentLine(frag); + return 0; + } + + INTERNAL_LINE_BREAK.setText(string); + + initBidi(frag, string, font); + float avgCharWidth = getAverageCharWidth(frag, font); + frag.setWidth(-1); + + /* + * Setup initial boundaries within the string. + */ + int absoluteMin = 0; + int max, min = 1; + if (wrapping is ParagraphTextLayout.WORD_WRAP_HARD) { + absoluteMin = INTERNAL_LINE_BREAK.next(); + while (absoluteMin > 0 && CharacterIsWhitespace(string[absoluteMin - 1 .. $].firstCodePoint())) + absoluteMin--; + min = Math.max(absoluteMin, 1); + } + int firstDelimiter = findFirstDelimeter(string); + if (firstDelimiter is 0) + min = max = 0; + else + max = Math.min(strLen, firstDelimiter) + 1; + + + int availableWidth = context.getRemainingLineWidth(); + int guess = 0, guessSize = 0; + + while (true) { + if ((max - min) <= 1) { + if (min is absoluteMin + && context.isCurrentLineOccupied() + && !context.getContinueOnSameLine() + && availableWidth < measureString(frag, string, min, font) + + ((min is strLen && lookahead !is null) ? lookahead.getWidth() : 0) + ) { + context.endLine(); + availableWidth = context.getRemainingLineWidth(); + max = Math.min(strLen, firstDelimiter) + 1; + if ((max - min) <= 1) + break; + } else + break; + } + // Pick a new guess size + // New guess is the last guess plus the missing width in pixels + // divided by the average character size in pixels + guess += 0.5f + (availableWidth - guessSize) / avgCharWidth; + + if (guess >= max) guess = max - 1; + if (guess <= min) guess = min + 1; + + guessSize = measureString(frag, string, guess, font); + + if (guess is strLen + && lookahead !is null + && !canBreakAfter(string.charAt(strLen - 1)) + && guessSize + lookahead.getWidth() > availableWidth) { + max = guess; + continue; + } + + if (guessSize <= availableWidth) { + min = guess; + frag.setWidth(guessSize); + if (guessSize is availableWidth) + max = guess + 1; + } else + max = guess; + } + + int result = min; + bool continueOnLine = false; + if (min is strLen) { + //Everything fits + if (string.charAt(strLen - 1) is ' ') { + if (frag.getWidth() is -1) { + frag.length = result; + frag.setWidth(measureString(frag, string, result, font)); + } + if (lookahead.getWidth() > availableWidth - frag.getWidth()) { + frag.length = result - 1; + frag.setWidth(-1); + } else + frag.length = result; + } else { + continueOnLine = !canBreakAfter(string.charAt(strLen - 1)); + frag.length = result; + } + } else if (min is firstDelimiter) { + //move result past the delimiter + frag.length = result; + if (string.charAt(min) is '\r') { + result++; + if (++min < strLen && string.charAt(min) is '\n') + result++; + } else if (string.charAt(min) is '\n') + result++; + } else if (string.charAt(min) is ' ' + || canBreakAfter(string.charAt(min - 1)) + || INTERNAL_LINE_BREAK.isBoundary(min)) { + frag.length = min; + if (string.charAt(min) is ' ') + result++; + else if (string.charAt(min - 1) is ' ') { + frag.length--; + frag.setWidth(-1); + } + } else { +out_: + // In the middle of an unbreakable offset + result = INTERNAL_LINE_BREAK.previous(min); + if (result is 0) { + switch (wrapping) { + case ParagraphTextLayout.WORD_WRAP_TRUNCATE : + int truncatedWidth = availableWidth - getEllipsisWidth(font); + if (truncatedWidth > 0) { + //$TODO this is very slow. It should be using avgCharWidth to go faster + while (min > 0) { + guessSize = measureString(frag, string, min, font); + if (guessSize <= truncatedWidth) + break; + min--; + } + frag.length = min; + } else + frag.length = 0; + frag.setTruncated(true); + result = INTERNAL_LINE_BREAK.next(max - 1); + goto out_; + + default: + result = min; + break; + } + } + frag.length = result; + if (string.charAt(result - 1) is ' ') + frag.length--; + frag.setWidth(-1); + } + + setupFragment(frag, font, string); + context.addToCurrentLine(frag); + context.setContinueOnSameLine(continueOnLine); + return result; +} +package int wrapFragmentInContext_package(TextFragmentBox frag, String string, + FlowContext context, LookAhead lookahead, Font font, int wrapping) { + return wrapFragmentInContext( frag, string, context, lookahead, font, wrapping ); +} + +/** + * @see TextLayout#getBounds() + */ +protected Rectangle getTextLayoutBounds(String s, Font f, int start, int end) { + TextLayout textLayout = getTextLayout(); + textLayout.setFont(f); + textLayout.setText(s); + return textLayout.getBounds(start, end); +} + +/** + * Returns an instance of a TextUtililities class on which + * text calculations can be performed. Clients may override to customize. + * + * @return the TextUtililities instance + * @since 3.4 + */ +protected TextUtilities getTextUtilities() { + return TextUtilities.INSTANCE; +} + +/** + * Gets the ellipsis width. + * + * @param font + * the font to be used in the calculation + * @return the width of the ellipsis + * @since 3.4 + */ +private int getEllipsisWidth(Font font) { + return getTextUtilities().getStringExtents(TextFlow.ELLIPSIS, font).width; +} +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/InlineFlow.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/InlineFlow.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.text.InlineFlow; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwt.DWT; +import dwtx.draw2d.Border; +import dwtx.draw2d.ColorConstants; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.text.FlowFigure; +import dwtx.draw2d.text.FlowFigureLayout; +import dwtx.draw2d.text.FlowBox; +import dwtx.draw2d.text.InlineFlowLayout; +import dwtx.draw2d.text.FlowBorder; + +/** + * A FlowFigure represented by multiple LineBox fragments. An + * InlineFlow's parent must be either a {@link BlockFlow} or another + * InlineFlow. + * + *

An InlineFlow may contain other InlineFlow figures. + * + *

WARNING: This class is not intended to be subclassed by clients. + * @author Randy Hudson + * @since 2.0 + */ +public class InlineFlow : FlowFigure { + +List fragments; + +public this(){ + fragments = new ArrayList(1); +} + +/** + * Iterates over the children to find the width before a line-break is encountered. + * @see dwtx.draw2d.text.FlowFigure#addLeadingWordRequirements(int[]) + */ +public bool addLeadingWordRequirements(int[] width) { + Iterator iter = getChildren().iterator(); + while (iter.hasNext()) { + if ((cast(FlowFigure)iter.next()).addLeadingWordRequirements(width)) + return true; + } + return false; +} + +/** + * Extended to return false if the point is not also contained by at least one fragment. + * @return true if a fragment contains the given point + * @param x the relative x coordinate + * @param y the relative y coordinate + */ +public bool containsPoint(int x, int y) { + if (super.containsPoint(x, y)) { + List frags = getFragments(); + for (int i = 0; i < frags.size(); i++) + if ((cast(FlowBox)frags.get(i)).containsPoint(x, y)) + return true; + } + + return false; +} + +/** + * @see FlowFigure#createDefaultFlowLayout() + */ +protected FlowFigureLayout createDefaultFlowLayout() { + return new InlineFlowLayout(this); +} + +/** + * Returns the FlowBox fragments contained in this InlineFlow. The returned + * list should not be modified. + * @return The fragments + */ +public List getFragments() { + return fragments; +} + +/** + * Overridden to paint a {@link FlowBorder} if present, and draw selection. The border is + * painted first, followed by selection which is generally done in XOR, which still allows + * the border to be seen. + * @param graphics the graphics + */ +protected void paintBorder(Graphics graphics) { + if (getBorder() !is null) { + FlowBorder fb = cast(FlowBorder)getBorder(); + List frags = getFragments(); + Rectangle where = new Rectangle(); + int sides; + for (int i = 0; i < frags.size(); i++) { + FlowBox box = cast(FlowBox)frags.get(i); + + where.x = box.getX(); + where.width = box.getWidth(); + where.y = -box.getAscentWithBorder(); + where.height = box.getDescentWithBorder() - where.y; + where.y += box.getBaseline(); + sides = 0; + if (i is 0) + sides = DWT.LEAD; + if (i is frags.size() - 1) + sides |= DWT.TRAIL; + fb.paint(this, graphics, where, sides); + } + graphics.restoreState(); + } + if (selectionStart !is -1) + paintSelection(graphics); +} + +/** + * Renders the XOR selection rectangles to the graphics. + * @param graphics the graphics to paint on + * @since 3.1 + */ +protected void paintSelection(Graphics graphics) { + graphics.restoreState(); + graphics.setXORMode(true); + graphics.setBackgroundColor(ColorConstants.white); + List list = getFragments(); + FlowBox box; + for (int i = 0; i < list.size(); i++) { + box = cast(FlowBox)list.get(i); + int top = box.getLineRoot().getVisibleTop(); + int bottom = box.getLineRoot().getVisibleBottom(); + graphics.fillRectangle(box.getX(), top, box.getWidth(), bottom - top); + } +} + +/** + * @see FlowFigure#postValidate() + */ +public void postValidate() { + List list = getFragments(); + FlowBox box; + int left = Integer.MAX_VALUE, top = left; + int right = Integer.MIN_VALUE, bottom = right; + + for (int i = 0; i < list.size(); i++) { + box = cast(FlowBox)list.get(i); + left = Math.min(left, box.getX()); + right = Math.max(right, box.getX() + box.getWidth()); + top = Math.min(top, box.getLineRoot().getVisibleTop()); + bottom = Math.max(bottom, box.getLineRoot().getVisibleBottom()); + } + + setBounds(new Rectangle(left, top, right - left, bottom - top)); + repaint(); + list = getChildren(); + for (int i = 0; i < list.size(); i++) + (cast(FlowFigure)list.get(i)).postValidate(); +} + +/** + * Overridden to assert that only {@link FlowBorder} is used. null is still a + * valid value as well. + * @param border null or a FlowBorder + */ +public void setBorder(Border border) { + if (border is null || null !is cast(FlowBorder)border ) + super.setBorder(border); + else + throw new RuntimeException("Border must be an instance of FlowBorder"); //$NON-NLS-1$ +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/InlineFlowLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/InlineFlowLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.InlineFlowLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.text.FlowContainerLayout; +import dwtx.draw2d.text.FlowFigure; +import dwtx.draw2d.text.LineBox; +import dwtx.draw2d.text.CompositeBox; +import dwtx.draw2d.text.NestedLine; +import dwtx.draw2d.text.InlineFlow; + +/** + * The layout manager for {@link InlineFlow} figures. + * + *

WARNING: This class is not intended to be subclassed by clients. + * @author hudsonr + * @since 2.1 + */ +public class InlineFlowLayout + : FlowContainerLayout +{ + +/** + * Creates a new InlineFlowLayout with the given FlowFigure. + * @param flow The FlowFigure + */ +public this(FlowFigure flow) { + super(flow); +} + +/** + * Adds the given box as a line below the current line. + * @param box the box to add + */ +public void addLine(CompositeBox box) { + endLine(); + getContext().addLine(box); +} + +/** + * @see FlowContainerLayout#createNewLine() + */ +protected void createNewLine() { + currentLine = new NestedLine(cast(InlineFlow)getFlowFigure()); + setupLine(currentLine); +} + +/** + * @see FlowContext#endLine() + */ +public void endLine() { + flush(); + getContext().endLine(); +} + +/** + * @see FlowContainerLayout#flush() + */ +protected void flush() { + if (currentLine !is null && currentLine.isOccupied()) { + // We want to preserve the state when a linebox is being added + bool sameLine = getContext().getContinueOnSameLine(); + getContext().addToCurrentLine(currentLine); + (cast(InlineFlow)getFlowFigure()).getFragments().add(currentLine); + currentLine = null; + getContext().setContinueOnSameLine(sameLine); + } +} + +/** + * InlineFlowLayout gets this information from its context. + * @see FlowContext#getContinueOnSameLine() + */ +public bool getContinueOnSameLine() { + return getContext().getContinueOnSameLine(); +} + +/** + * @see FlowContext#getWidthLookahead(FlowFigure, int[]) + */ +public void getWidthLookahead(FlowFigure child, int result[]) { + List children = getFlowFigure().getChildren(); + int index = -1; + if (child !is null) + index = children.indexOf(child); + + for (int i = index + 1; i < children.size(); i++) + if ((cast(FlowFigure)children.get(i)).addLeadingWordRequirements(result)) + return; + + getContext().getWidthLookahead(getFlowFigure(), result); +} + +/** + * @see FlowContainerLayout#isCurrentLineOccupied() + */ +public bool isCurrentLineOccupied() { + return (currentLine !is null && !currentLine.getFragments().isEmpty()) + || getContext().isCurrentLineOccupied(); +} + +/** + * Clears out all fragments prior to the call to layoutChildren(). + */ +public void preLayout() { + (cast(InlineFlow)getFlowFigure()).getFragments().clear(); +} + +/** + * InlineFlow passes this information to its context. + * @see FlowContext#setContinueOnSameLine(bool) + */ +public void setContinueOnSameLine(bool value) { + getContext().setContinueOnSameLine(value); +} + +/** + * Initializes the given LineBox. Called by createNewLine(). + * @param line The LineBox to initialize. + */ +protected void setupLine(LineBox line) { + line.setX(0); + line.setRecommendedWidth(getContext().getRemainingLineWidth()); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/LineBox.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/LineBox.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.LineBox; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.text.CompositeBox; +import dwtx.draw2d.text.FlowBox; + +/** + * @author hudsonr + * @since 2.1 + */ +public abstract class LineBox + : CompositeBox +{ + +/** + * The maximum ascent of all contained fragments. + */ +int contentAscent; + +/** + * The maximum descent of all contained fragments. + */ +int contentDescent; + +List fragments; + +public this(){ + fragments = new ArrayList(); +} + +/** + * @see dwtx.draw2d.text.CompositeBox#add(dwtx.draw2d.text.FlowBox) + */ +public void add(FlowBox child) { + fragments.add(child); + width += child.getWidth(); + contentAscent = Math.max(contentAscent, child.getOuterAscent()); + contentDescent = Math.max(contentDescent, child.getOuterDescent()); +} + +/** + * @see dwtx.draw2d.text.FlowBox#getAscent() + */ +public int getAscent() { + int ascent = 0; + for (int i = 0; i < fragments.size(); i++) + ascent = Math.max(ascent, (cast(FlowBox)fragments.get(i)).getAscent()); + return ascent; +} + +/** + * Returns the remaining width available for line content. + * @return the available width in pixels + */ +int getAvailableWidth() { + if (recommendedWidth < 0) + return Integer.MAX_VALUE; + return recommendedWidth - getWidth(); +} + +int getBottomMargin() { + return 0; +} + +/** + * @see dwtx.draw2d.text.FlowBox#getDescent() + */ +public int getDescent() { + int descent = 0; + for (int i = 0; i < fragments.size(); i++) + descent = Math.max(descent, (cast(FlowBox)fragments.get(i)).getDescent()); + return descent; +} + +/** + * @return Returns the fragments. + */ +List getFragments() { + return fragments; +} + +int getTopMargin() { + return 0; +} + +/** + * @return true if this box contains any fragments + */ +public bool isOccupied() { + return !fragments.isEmpty(); +} + +/** + * @see dwtx.draw2d.text.FlowBox#requiresBidi() + */ +public bool requiresBidi() { + for (Iterator iter = getFragments().iterator(); iter.hasNext();) { + FlowBox box = cast(FlowBox)iter.next(); + if (box.requiresBidi()) + return true; + } + return false; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/LineRoot.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/LineRoot.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,275 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.text.LineRoot; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.text.LineBox; +import dwtx.draw2d.text.FlowBox; +import dwtx.draw2d.text.NestedLine; +import dwtx.draw2d.text.ContentBox; +import dwtx.draw2d.text.InlineFlow; + +/** + * LineRoot is the top-most container on a line of text displayed in Draw2d. Hence, a + * LineRoot can tell you of things like the highest ascent or descent on a line, which + * is required to display selection and such. All + * {@link dwtx.draw2d.text.ContentBox fragments} know of the LineRoot they belong + * to. + * @author Randy Hudson + * @author Pratik Shah + * @since 3.1 + */ +public class LineRoot + : LineBox +{ + +private int baseline; +private bool isMirrored; + +/** + * Constructor + * @param isMirrored true if the line is to be displayed in a mirrored control + */ +public this(bool isMirrored) { + this.isMirrored = isMirrored; +} + +/** + * @see dwtx.draw2d.text.CompositeBox#add(dwtx.draw2d.text.FlowBox) + */ +public void add(FlowBox child) { + super.add(child); + child.setLineRoot(this); +} + +private void bidiCommit() { + int xLocation = getX(); + BidiLevelNode root = new BidiLevelNode(); + List branches = new ArrayList(); + // branches does not include this LineRoot; all the non-leaf child fragments of a + // parent will be listed before the parent itself in this list + buildBidiTree(this, root, branches); + List result = new ArrayList(); + root.emit(result); + int i = isMirrored ? result.size() - 1 : 0; + while (i >= 0 && i < result.size()) { + FlowBox box = cast(FlowBox)result.get(i); + box.setX(xLocation); + xLocation += box.getWidth(); + i += isMirrored ? -1 : 1; + } + // set the bounds of the composite boxes, and break overlapping ones into two + layoutNestedLines(branches); +} + +private void buildBidiTree(FlowBox box, BidiLevelNode node, List branches) { + if ( auto lb = cast(LineBox)box ) { + List children = lb.getFragments(); + for (int i = 0; i < children.size(); i++) + buildBidiTree(cast(FlowBox)children.get(i), node, branches); + if (box !is this) + branches.add(box); + } else { + ContentBox leafBox = cast(ContentBox)box; + while (leafBox.getBidiLevel() < node.level) + node = node.pop(); + while (leafBox.getBidiLevel() > node.level) + node = node.push(); + node.add(leafBox); + } +} + +/** + * Committing a LineRoot will position its children correctly. All children boxes are made + * to have the same baseline, and are laid out according to the Unicode BiDi Algorithm, + * or left-to-right if Bidi is not necessary. + */ +public void commit() { + if (requiresBidi()) + bidiCommit(); + else + contiguousCommit(this, getX()); +} + +/** + * A LineRoot cannot be targetted. + * @see dwtx.draw2d.text.FlowBox#containsPoint(int, int) + */ +public bool containsPoint(int x, int y) { + return false; +} + +/* + * Simply lays out all fragments from left-to-right in the order in which they're + * contained. + */ +private void contiguousCommit(FlowBox box, int x) { + box.setX(x); + if (auto lb = cast(LineBox)box ) { + List fragments = lb.getFragments(); + int i = isMirrored ? fragments.size() - 1 : 0; + while (i >= 0 && i < fragments.size()) { + FlowBox child = cast(FlowBox)fragments.get(i); + contiguousCommit(child, x); + x += child.getWidth(); + i += isMirrored ? -1 : 1; + } + } +} + +private Result findParent(NestedLine line, List branches, int afterIndex) { + for (int i = afterIndex + 1; i < branches.size(); i++) { + NestedLine box = cast(NestedLine)branches.get(i); + int index = box.getFragments().indexOf(line); + if (index >= 0) + return new Result(box, index); + } + return new Result(this, getFragments().indexOf(line)); +} + +/** + * @see dwtx.draw2d.text.FlowBox#getBaseline() + */ +public int getBaseline() { + return baseline; +} + +LineRoot getLineRoot() { + return this; +} + +int getVisibleBottom() { + return baseline + contentDescent; +} + +int getVisibleTop() { + return baseline - contentAscent; +} + +private void layoutNestedLines(List branches) { + for (int i = 0; i < branches.size(); i++) { + NestedLine parent = cast(NestedLine)branches.get(i); + FlowBox prevChild = null; + Rectangle bounds = null; + List frags = parent.getFragments(); + for (int j = 0; j < frags.size(); j++) { + FlowBox child = cast(FlowBox) frags.get(j); + if (prevChild !is null && prevChild.getX() + prevChild.width !is child.getX() + && child.getX() + child.width !is prevChild.getX()) { + // the boxes are not adjacent, and hence the parent box needs to + // be broken up + InlineFlow parentFig = parent.owner; + // Create and initialize a new line box + NestedLine newBox = new NestedLine(parentFig); + newBox.setLineRoot(this); + // Add all remaining fragments from the current line box to the new one + for (int k = j; k < frags.size();) + newBox.fragments.add(frags.remove(k)); + // Add the new line box to the parent box's list of fragments + Result result = findParent(parent, branches, i); + result.parent.getFragments().add(result.index + 1, newBox); + // Add the new line box to the flow figure's list of fragments + parentFig.fragments.add(parentFig.fragments.indexOf(parent) + 1, newBox); + branches.add(i + 1, newBox); + break; + } + if (bounds is null) + bounds = new Rectangle(child.getX(), 1, child.getWidth(), 1); + else + bounds.union_(child.getX(), 1, child.getWidth(), 1); + prevChild = child; + } + parent.setX(bounds.x); + parent.setWidth(bounds.width); + } +} + +/** + * Positions the line vertically by settings its baseline. + * @param baseline the baseline + */ +public void setBaseline(int baseline) { + this.baseline = baseline; +} + +/** + * @see dwtx.draw2d.text.CompositeBox#setLineTop(int) + */ +public void setLineTop(int top) { + this.baseline = top + getAscent(); +} + +private static class BidiLevelNode : ArrayList +{ + int level; + final BidiLevelNode parent; + + this() { + this(null, 0); + } + + this(BidiLevelNode parent, int level) { + this.parent = parent; + this.level = level; + } + + void emit(List list) { + if (level % 2 is 1) { + for (int i = size() - 1; i >= 0; i--) { + Object child = get(i); + if (auto bln = cast(BidiLevelNode)child ) + bln.emit(list); + else + list.add(child); + } + } else { + for (int i = 0; i < size(); i++) { + Object child = get(i); + if (auto bln = cast(BidiLevelNode)child ) + bln.emit(list); + else + list.add(child); + } + } + } + + BidiLevelNode pop() { + return parent; + } + + BidiLevelNode push() { + if (!isEmpty()) { + Object last = get(size() - 1); + if (null !is cast(BidiLevelNode)last && (cast(BidiLevelNode)last).level is level + 1) + return cast(BidiLevelNode)last; + } + BidiLevelNode child = new BidiLevelNode(this, level + 1); + add(child); + return child; + } +} + +private static class Result { + private int index; + private LineBox parent; + private this(LineBox box, int i) { + parent = box; + index = i; + } +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/NestedLine.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/NestedLine.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ + +module dwtx.draw2d.text.NestedLine; + +import dwt.dwthelper.utils; +import dwtx.draw2d.text.LineBox; +import dwtx.draw2d.text.LineRoot; +import dwtx.draw2d.text.InlineFlow; +import dwtx.draw2d.text.FlowUtilities; +import dwtx.draw2d.text.FlowBox; + +/** + * @since 3.1 + */ +public class NestedLine : LineBox { + +InlineFlow owner; +private LineRoot root; + +this(InlineFlow owner) { + this.owner = owner; +} + +/** + * @see dwtx.draw2d.text.FlowBox#containsPoint(int, int) + */ +public bool containsPoint(int x, int y) { + //$TODO should contains use LineRoot? + return x >= getX() + && x < getX() + getWidth() + && y >= getBaseline() - getAscentWithBorder() + && y < getBaseline() + getDescentWithBorder(); +} + +int getAscentWithBorder() { + return contentAscent + FlowUtilities.getBorderAscent(owner); +} + +int getDescentWithBorder() { + return contentDescent + FlowUtilities.getBorderDescent(owner); +} + +/** + * @see dwtx.draw2d.text.FlowBox#getBaseline() + */ +public int getBaseline() { + return root.getBaseline(); +} + +LineRoot getLineRoot() { + return root; +} + +//int getVisibleAscent() { +// return contentAscent + FlowUtilities.getBorderAscent(owner); +//} +// +//int getVisibleDescent() { +// return contentDescent + FlowUtilities.getBorderDescent(owner); +//} + +/** + * Returns the outer ascent of this box. The outer ascent is the ascent above the + * baseline including the border size and margin. This is used when adding content into a + * LineBox. The linebox's own border must be drawn around the children. + * @return the outer ascent of this box + */ +public int getOuterAscent() { + return contentAscent + FlowUtilities.getBorderAscentWithMargin(owner); +} + +/** + * Returns the outer descent of this box. The outer descent is the space below the + * baseline including the border size and margin. This is used when adding content into a + * LineBox. The linebox's own border must be drawn around the children. + * @return the outer descent of this box + */ +public int getOuterDescent() { + return contentDescent + FlowUtilities.getBorderDescentWithMargin(owner); +} + +void setLineRoot(LineRoot root) { + this.root = root; + for (int i = 0; i < fragments.size(); i++) + (cast(FlowBox)fragments.get(i)).setLineRoot(root); +} + +/** + * @see dwtx.draw2d.text.CompositeBox#setLineTop(int) + */ +public void setLineTop(int top) { + throw new RuntimeException("not supported"); //$NON-NLS-1$ +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/PageFlowLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/PageFlowLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.PageFlowLayout; + +import dwt.dwthelper.utils; +import dwtx.draw2d.text.BlockFlowLayout; +import dwtx.draw2d.text.FlowPage; + +/** + * A block layout which requires no FlowContext to perform its layout. This class is used + * by {@link FlowPage}. + *

+ * WARNING: This class is not intended to be subclassed by clients. + */ +public class PageFlowLayout + : BlockFlowLayout +{ + +/** + * Creates a new PageFlowLayout with the given FlowPage + * @param page the FlowPage + */ +public this(FlowPage page) { + super(page); +} + +/** + * @see dwtx.draw2d.text.BlockFlowLayout#getContextWidth() + */ +int getContextWidth() { + return (cast(FlowPage)getFlowFigure()).getPageWidth(); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/ParagraphTextLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/ParagraphTextLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,218 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +module dwtx.draw2d.text.ParagraphTextLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwt.graphics.Font; +import dwtx.draw2d.text.TextLayout; +import dwtx.draw2d.text.TextFlow; +import dwtx.draw2d.text.FlowUtilities; +import dwtx.draw2d.text.FlowContext; +import dwtx.draw2d.text.TextFragmentBox; +import dwtx.draw2d.text.FlowBorder; + +/** + * The layout for {@link TextFlow}. + * @author hudsonr + * @since 2.1 + */ +public class ParagraphTextLayout + : TextLayout +{ + +/** + * Wrapping will ONLY occur at valid line breaks + */ +public static const int WORD_WRAP_HARD = 0; + +/** + * Wrapping will always occur at the end of the available space, breaking in the middle of + * a word. + */ +public static const int WORD_WRAP_SOFT = 1; + +/** + * Wrapping will always occur at the end of available space, truncating a word if it + * doesn't fit. Note that truncation is not supported across multiple figures and + * with BiDi. Undesired effects may result if that is the case. + */ +public static const int WORD_WRAP_TRUNCATE = 2; + +private int wrappingStyle = WORD_WRAP_HARD; + +/** + * Constructs a new ParagraphTextLayout on the specified TextFlow. + * @param flow the TextFlow + */ +public this(TextFlow flow) { + super(flow); +} + +/** + * Constructs the layout with the specified TextFlow and wrapping style. The wrapping + * style must be one of: + *

    + *
  • {@link #WORD_WRAP_HARD}
  • + *
  • {@link #WORD_WRAP_SOFT}
  • + *
  • {@link #WORD_WRAP_TRUNCATE}
  • + *
+ * @param flow the textflow + * @param style the style of wrapping + */ +public this(TextFlow flow, int style) { + this(flow); + wrappingStyle = style; +} + +/** + * Given the Bidi levels of the given text, this method breaks the given text up by + * its level runs. + * @param text the String that needs to be broken up into its level runs + * @param levelInfo the Bidi levels + * @return the requested segment + */ +private String[] getSegments(String text, int levelInfo[]) { + if (levelInfo.length is 1) + return [text]; + + String result[] = new String[levelInfo.length / 2 + 1]; + + int i; + int endOffset; + int beginOffset = 0; + + for (i = 0; i < result.length - 1; i++) { + endOffset = levelInfo[i * 2 + 1]; + result[i] = text.substring(beginOffset, endOffset); + beginOffset = endOffset; + } + endOffset = text.length; + result[i] = text.substring(beginOffset, endOffset); + return result; +} + +class SegmentLookahead : /+FlowUtilities.+/LookAhead { + private int seg = -1; + private String segs[]; + private int[] width; + private final int trailingBorderSize; + this(String segs[], int trailingBorderSize) { + this.segs = segs; + this.trailingBorderSize = trailingBorderSize; + } + public int getWidth() { + if (width is null) { + width = new int[1]; + int startingIndex = seg + 1; + TextFlow textFlow = cast(TextFlow)getFlowFigure(); + + if (startingIndex is segs.length) { + width[0] += trailingBorderSize; + getContext().getWidthLookahead(textFlow, width); + } else { + String rest = segs[startingIndex]; + for (int k = startingIndex + 1; k < segs.length; k++) + rest ~= segs[k]; + if (!textFlow.addLeadingWordWidth(rest, width)) { + width[0] += trailingBorderSize; + getContext().getWidthLookahead(textFlow, width); + } + } + } + return width[0]; + } + public void setIndex(int value) { + this.seg = value; + width = null; + } +} + +/** + * @see dwtx.draw2d.text.FlowFigureLayout#layout() + */ +protected void layout() { + TextFlow textFlow = cast(TextFlow)getFlowFigure(); + int offset = 0; + + FlowContext context = getContext(); + List fragments = textFlow.getFragments(); + Font font = textFlow.getFont(); + int fragIndex = 0; + int advance = 0; + + TextFragmentBox fragment; + int levelInfo[] = (textFlow.getBidiInfo() is null) + ? [-1] + : textFlow.getBidiInfo().levelInfo; + + String segment; + String[] segments = getSegments(textFlow.getText(), levelInfo); + FlowBorder border = null; + if ( null !is cast(FlowBorder)textFlow.getBorder() ) + border = cast(FlowBorder)textFlow.getBorder(); + + SegmentLookahead lookahead = new SegmentLookahead(segments, border is null ? 0 : border.getRightMargin()); + int seg; + + if (border !is null) { + fragment = getFragment(fragIndex++, fragments); + fragment.setBidiLevel(levelInfo[0]); + fragment.setTruncated(false); + fragment.offset = fragment.length = -1; + fragment.setWidth(border.getLeftMargin() + border.getInsets(textFlow).left); + if (context.getRemainingLineWidth() + < fragment.getWidth() + lookahead.getWidth()) + context.endLine(); + context.addToCurrentLine(fragment); + } + + FlowUtilities flowUtilities = textFlow.getFlowUtilities_package(); + for (seg = 0; seg < segments.length; seg++) { + segment = segments[seg]; + lookahead.setIndex(seg); + + do { + fragment = getFragment(fragIndex++, fragments); + + fragment.offset = offset; + fragment.setBidiLevel(levelInfo[seg * 2]); + + advance = flowUtilities.wrapFragmentInContext_package(fragment, segment, + context, lookahead, font, wrappingStyle); + segment = segment.substring(advance); + offset += advance; + if ((segment.length > 0 + || fragment.length < advance) + || fragment.isTruncated()) + context.endLine(); + } while (segment.length > 0 + || (!fragment.isTruncated() && fragment.length < advance)); + } + + if (border !is null) { + fragment = getFragment(fragIndex++, fragments); + fragment.setBidiLevel(levelInfo[0]); + fragment.setTruncated(false); + fragment.offset = fragment.length = -1; + fragment.setWidth(border.getRightMargin() + border.getInsets(textFlow).right); + context.addToCurrentLine(fragment); + } + + //Remove the remaining unused fragments. + while (fragIndex < fragments.size()) + fragments.remove(fragments.size() - 1); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/SimpleTextLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/SimpleTextLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.text.SimpleTextLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwt.graphics.Font; +import dwtx.draw2d.text.TextLayout; +import dwtx.draw2d.text.TextFlow; +import dwtx.draw2d.text.TextFragmentBox; +import dwtx.draw2d.text.FlowUtilities; + +/** + * @author hudsonr + * @since 2.1 + */ +public class SimpleTextLayout : TextLayout { + +private static const String[] DELIMITERS = [ + "\r\n", //$NON-NLS-1$ + "\n", //$NON-NLS-1$ + "\r"];//$NON-NLS-1$ + +private static int result; +private static int delimeterLength; + +/** + * Creates a new SimpleTextLayout with the given TextFlow + * @param flow the TextFlow + */ +public this(TextFlow flow) { + super (flow); +} + +/** + * @see dwtx.draw2d.text.FlowFigureLayout#layout() + */ +protected void layout() { + TextFlow textFlow = cast(TextFlow)getFlowFigure(); + String text = textFlow.getText(); + List fragments = textFlow.getFragments(); + Font font = textFlow.getFont(); + TextFragmentBox fragment; + int i = 0; + int offset = 0; + FlowUtilities flowUtilities = textFlow.getFlowUtilities_package(); + + do { + nextLineBreak(text, offset); + fragment = getFragment(i++, fragments); + fragment.length = result - offset; + fragment.offset = offset; + fragment.setWidth(-1); + flowUtilities.setupFragment_package(fragment, font, text.substring(offset, result)); + getContext().addToCurrentLine(fragment); + getContext().endLine(); + offset = result + delimeterLength; + } while (offset < text.length); + //Remove the remaining unused fragments. + while (i < fragments.size()) + fragments.remove(i++); +} + +private int nextLineBreak(String text, int offset) { + result = text.length; + delimeterLength = 0; + int current; + for (int i = 0; i < DELIMITERS.length; i++) { + current = text.indexOf(DELIMITERS[i], offset); + if (current !is -1 && current < result) { + result = current; + delimeterLength = DELIMITERS[i].length; + } + } + return result; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/TextFlow.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/TextFlow.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,686 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 + *******************************************************************************/ +module dwtx.draw2d.text.TextFlow; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwt.DWT; +import dwt.graphics.Color; +import dwt.graphics.TextLayout; +import dwtx.draw2d.ColorConstants; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.TextUtilities; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.text.InlineFlow; +import dwtx.draw2d.text.BidiInfo; +import dwtx.draw2d.text.BidiProcessor; +import dwtx.draw2d.text.FlowFigureLayout; +import dwtx.draw2d.text.TextFragmentBox; +import dwtx.draw2d.text.CaretInfo; +import dwtx.draw2d.text.FlowUtilities; +import dwtx.draw2d.text.ParagraphTextLayout; +import dwtx.draw2d.text.BidiChars; +import dwtx.draw2d.text.FlowBorder; + +import tango.text.convert.Format; + +/** + * An inline flow figure that renders a string of text across one or more lines. A + * TextFlow cannot contain children. All InlineFlow figure's must be + * parented by a FlowFigure. + *

+ * WARNING: This class is not intended to be subclassed by clients. + * @author hudsonr + * @author Pratik Shah + * @since 2.1 + */ +public class TextFlow + : InlineFlow +{ + +static final String ELLIPSIS = "..."; //$NON-NLS-1$ +private BidiInfo bidiInfo; +private int selectionEnd = -1; +private String text; + +/** + * Constructs a new TextFlow with the empty String. + * @see java.lang.Object#Object() + */ +public this() { + this(""/+new String()+/); +} + +/** + * Constructs a new TextFlow with the specified String. + * @param s the string + */ +public this(String s) { + text = s; +} + +/** + * Returns the width of the text until the first line-break. + * @see dwtx.draw2d.text.FlowFigure#addLeadingWordRequirements(int[]) + */ +public bool addLeadingWordRequirements(int[] width) { + return addLeadingWordWidth(getText(), width); +} + +/** + * Calculates the width taken up by the given text before a line-break is encountered. + * + * @param text the text in which the break is to be found + * @param width the width before the next line-break (if one's found; the width of all + * the given text, otherwise) will be added on to the first int in the given array + * @return true if a line-break was found + * @since 3.1 + */ +bool addLeadingWordWidth(String text, int[] width) { + if (text.length is 0) + return false; + if (CharacterIsWhitespace(text.firstCodePoint())) + return true; + + text = 'a' ~ text ~ 'a'; + FlowUtilities.LINE_BREAK.setText(text); + int index = FlowUtilities.LINE_BREAK.next() - 1; + if (index is 0) + return true; + while (CharacterIsWhitespace(text[index..$].firstCodePoint())) + index--; + bool result = index < text.length - 1; + // index should point to the end of the actual text (not including the 'a' that was + // appended), if there were no breaks + if (index is text.length - 1) + index--; + text = text.substring(1, index + 1); + + if (bidiInfo is null) + width[0] += getTextUtilities().getStringExtents(text, getFont()).width; + else { + TextLayout textLayout = FlowUtilities.getTextLayout(); + textLayout.setFont(getFont()); + textLayout.setText(text); + width[0] += textLayout.getBounds().width; + } + return result; +} + +/** + * A TextFlow contributes its text. + * @see dwtx.draw2d.text.FlowFigure#contributeBidi(dwtx.draw2d.text.BidiProcessor) + */ +protected void contributeBidi(BidiProcessor proc) { + bidiInfo = null; + proc.add(this, getText()); +} + +/** + * @see dwtx.draw2d.text.InlineFlow#createDefaultFlowLayout() + */ +protected FlowFigureLayout createDefaultFlowLayout() { + return new ParagraphTextLayout(this); +} + +private int findNextLineOffset(Point p, int[] trailing) { + if (getBounds().bottom() <= p.y) + return -1; + + TextFragmentBox closestBox = null; + int index = 0; + List fragments = getFragmentsWithoutBorder(); + for (int i = fragments.size() - 1; i >= 0; i--) { + TextFragmentBox box = cast(TextFragmentBox)fragments.get(i); + if (box.getBaseline() - box.getLineRoot().getAscent() > p.y + && (closestBox is null + || box.getBaseline() < closestBox.getBaseline() + || (box.getBaseline() is closestBox.getBaseline() + && hDistanceBetween(box, p.x) < hDistanceBetween(closestBox, p.x)))) { + closestBox = box; + index = i; + } + } + return findOffset(p, trailing, closestBox, index); +} + +private int findOffset(Point p, int[] trailing, TextFragmentBox box, int boxIndex) { + if (box is null) + return -1; + TextLayout layout = FlowUtilities.getTextLayout(); + layout.setFont(getFont()); + layout.setText(getBidiSubstring(box, boxIndex)); + int x = p.x - box.getX(); + if (isMirrored()) + x = box.getWidth() - x; + int layoutOffset = layout.getOffset(x, p.y - box.getTextTop(), trailing); + return box.offset + layoutOffset - getBidiPrefixLength(box, boxIndex); +} + +private int findPreviousLineOffset(Point p, int[] trailing) { + if (getBounds().y > p.y) + return -1; + + TextFragmentBox closestBox = null; + int index = 0; + List fragments = getFragmentsWithoutBorder(); + for (int i = fragments.size() - 1; i >= 0; i--) { + TextFragmentBox box = cast(TextFragmentBox)fragments.get(i); + if (box.getBaseline() + box.getLineRoot().getDescent() < p.y + && (closestBox is null + || box.getBaseline() > closestBox.getBaseline() + || (box.getBaseline() is closestBox.getBaseline() + && hDistanceBetween(box, p.x) < hDistanceBetween(closestBox, p.x)))) { + closestBox = box; + index = i; + } + } + return findOffset(p, trailing, closestBox, index); +} + +int getAscent() { + return getTextUtilities().getAscent(getFont()); +} + +/** + * Returns the BidiInfo for this figure or null. + * @return null or the info + * @since 3.1 + */ +public BidiInfo getBidiInfo() { + return bidiInfo; +} + +private int getBidiPrefixLength(TextFragmentBox box, int index) { + if (box.getBidiLevel() < 1) + return 0; + if (index > 0 || !bidiInfo.leadingJoiner) + return 1; + return 2; +} + +/** + * @param box which fragment + * @param index the fragment index + * @return the bidi string for that fragment + * @since 3.1 + */ +protected String getBidiSubstring(TextFragmentBox box, int index) { + if (box.getBidiLevel() < 1) + return getText().substring(box.offset, box.offset + box.length); + + StringBuffer buffer = new StringBuffer(box.length + 3); + buffer.append( dcharToString( box.isRightToLeft() ? BidiChars.RLO : BidiChars.LRO )); + if (index is 0 && bidiInfo.leadingJoiner) + buffer.append(dcharToString(BidiChars.ZWJ)); + buffer.append(getText().substring(box.offset, box.offset + box.length)); + if (index is getFragmentsWithoutBorder().size() - 1 && bidiInfo.trailingJoiner) + buffer.append(dcharToString(BidiChars.ZWJ)); + return buffer.toString(); +} + +/** + * Returns the CaretInfo in absolute coordinates. The offset must be between 0 and the + * length of the String being displayed. + * @since 3.1 + * @param offset the location in this figure's text + * @param trailing true if the caret is being placed after the offset + * @exception IllegalArgumentException If the offset is not between 0 and the + * length of the string inclusively + * @return the caret bounds relative to this figure + */ +public CaretInfo getCaretPlacement(int offset, bool trailing) { + if (offset < 0 || offset > getText().length) + throw new IllegalArgumentException(Format("Offset: {} is invalid", offset //$NON-NLS-1$ + )); //$NON-NLS-1$ + + if (offset is getText().length) + trailing = false; + + List fragments = getFragmentsWithoutBorder(); + int i = fragments.size(); + TextFragmentBox box; + do + box = cast(TextFragmentBox)fragments.get(--i); + while (offset < box.offset && i > 0); + + // Cannot be trailing and after the last char, so go to first char in next box + if (trailing && box.offset + box.length <= offset) { + box = cast(TextFragmentBox)fragments.get(++i); + offset = box.offset; + trailing = false; + } + + Point where = getPointInBox(box, offset, i, trailing); + CaretInfo info = new CaretInfo(where.x, where.y, box.getAscent(), box.getDescent(), + box.getLineRoot().getAscent(), box.getLineRoot().getDescent()); + translateToAbsolute(info); + return info; +} + +Point getPointInBox(TextFragmentBox box, int offset, int index, bool trailing) { + offset -= box.offset; + offset = Math.min(box.length, offset); + Point result = new Point(0, box.getTextTop()); + if (bidiInfo is null) { + if (trailing && offset < box.length) + offset++; + String substring = getText().substring(box.offset, box.offset + offset); + result.x = getTextUtilities().getStringExtents(substring, getFont()).width; + } else { + TextLayout layout = FlowUtilities.getTextLayout(); + layout.setFont(getFont()); + String fragString = getBidiSubstring(box, index); + layout.setText(fragString); + offset += getBidiPrefixLength(box, index); + result.x = layout.getLocation(offset, trailing).x; + if (isMirrored()) + result.x = box.width - result.x; + } + result.x += box.getX(); + return result; +} + +int getDescent() { + return getTextUtilities().getDescent(getFont()); +} + +/** + * Returns the minimum character offset which is on the given baseline y-coordinate. The y + * location should be relative to this figure. The return value will be between + * 0 and N-1. If no fragment is located on the baseline, -1 is returned. + * @since 3.1 + * @param baseline the relative baseline coordinate + * @return -1 or the lowest offset for the line + */ +public int getFirstOffsetForLine(int baseline) { + TextFragmentBox box; + List fragments = getFragmentsWithoutBorder(); + for (int i = 0; i < fragments.size(); i++) { + box = cast(TextFragmentBox)fragments.get(i); + if (baseline is box.getBaseline()) + return box.offset; + } + return -1; +} + +/** + * Returns the TextFragmentBox fragments contained in this TextFlow, not + * including the border fragments. The returned list should not be modified. + * @return list of fragments without the border fragments + * @since 3.4 + */ +protected List getFragmentsWithoutBorder() { + List fragments = getFragments(); + if (getBorder() !is null) + fragments = fragments.subList(1, fragments.size() - 1); + return fragments; +} + +/** + * Returns the maximum offset for a character which is on the given baseline y-coordinate. + * The y location should be relative to this figure. The return value will be between + * 0 and N-1. If no fragment is located on the baseline, -1 is returned. + * @since 3.1 + * @param baseline the relative baseline coordinate + * @return -1 or the highest offset at the given baseline + */ +public int getLastOffsetForLine(int baseline) { + TextFragmentBox box; + List fragments = getFragmentsWithoutBorder(); + for (int i = fragments.size() - 1; i >= 0; i--) { + box = cast(TextFragmentBox)fragments.get(i); + if (baseline is box.getBaseline()) + return box.offset + box.length - 1; + } + return -1; +} + +/** + * Returns the offset nearest the given point either up or down one line. If no offset + * is found, -1 is returned. trailing[0] will be set to 1 if the reference + * point is closer to the trailing edge of the offset than it is to the leading edge. + * @since 3.1 + * @param p a reference point + * @param down true if the search is down + * @param trailing an int array + * @return the next offset or -1 + */ +public int getNextOffset(Point p, bool down, int[] trailing) { + return down ? findNextLineOffset(p, trailing) : findPreviousLineOffset(p, trailing); +} + +/** + * Returns the next offset which is visible in at least one fragment or -1 if there is + * not one. A visible offset means that the character or the one preceding it is + * displayed, which implies that a caret can be positioned at such an offset. This is + * useful for advancing a caret past characters which resulted in a line wrap. + * + * @param offset the reference offset + * @return the next offset which is visible + * @since 3.1 + */ +public int getNextVisibleOffset(int offset) { + TextFragmentBox box; + List fragments = getFragmentsWithoutBorder(); + for (int i = 0; i < fragments.size(); i++) { + box = cast(TextFragmentBox)fragments.get(i); + if (box.offset + box.length <= offset) + continue; + return Math.max(box.offset, offset + 1); + } + return -1; +} + +/** + * Returns the offset of the character directly below or nearest the given location. The + * point must be relative to this figure. The return value will be between 0 and N-1. If + * the proximity argument is not null, the result may also be -1 + * if no offset was found within the proximity. + *

+ * For a typical character, the trailing argument will be filled in to indicate whether + * the point is closer to the leading edge (0) or the trailing edge (1). When the point + * is over a cluster composed of multiple characters, the trailing argument will be filled + * with the position of the character in the cluster that is closest to the point. + *

+ * If the proximity argument is not null, then the location may be no further + * than the proximity given. Passing null is equivalent to passing new + * Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE). The width field of + * the proximity will contain the horizontal distance, height will contain + * vertical. Vertical proximity is more important than horizontal. The returned offset is + * the lowest index with minimum vertical proximity not exceeding the given limit, with + * horizontal proximity not exceeding the given limit. If an offset that is within the + * proximity is found, then the given Dimension will be updated to reflect + * the new proximity. + * + * + * @since 3.1 + * @param p the point relative to this figure + * @param trailing the trailing buffer + * @param proximity restricts and records the distance of the returned offset + * @return the nearest offset in this figure's text + */ +public int getOffset(Point p, int trailing[], Dimension proximity) { + if (proximity is null) + proximity = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + TextFragmentBox closestBox = null; + int index = 0; + int dy; + int dx; + int i = 0; + int size = fragments.size(); + if (null !is cast(FlowBorder)getBorder() ) { + i++; + size--; + } + for (; i < size; i++) { + TextFragmentBox box = cast(TextFragmentBox)fragments.get(i); + dy = vDistanceBetween(box, p.y); + if (dy > proximity.height) + continue; + dx = hDistanceBetween(box, p.x); + if (dy is proximity.height && dx >= proximity.width) + continue; + proximity.height = dy; + proximity.width = dx; + closestBox = box; + index = i; + } + return findOffset(p, trailing, closestBox, index); +} + +/** + * Returns the previous offset which is visible in at least one fragment or -1 if there + * is not one. See {@link #getNextVisibleOffset(int)} for more. + * + * @param offset a reference offset + * @return -1 or the previous offset which is visible + * @since 3.1 + */ + +public int getPreviousVisibleOffset(int offset) { + TextFragmentBox box; + if (offset is -1) + offset = Integer.MAX_VALUE; + List fragments = getFragmentsWithoutBorder(); + for (int i = fragments.size() - 1; i >= 0; i--) { + box = cast(TextFragmentBox)fragments.get(i); + if (box.offset >= offset) + continue; + return Math.min(box.offset + box.length, offset - 1); + } + return -1; +} + +/** + * @return the String being displayed; will not be null + */ +public String getText() { + return text; +} + +int getVisibleAscent() { + if (null !is cast(FlowBorder)getBorder() ) { + FlowBorder border = cast(FlowBorder)getBorder(); + return border.getInsets(this).top + getAscent(); + } + return getAscent(); +} + +int getVisibleDescent() { + if (null !is cast(FlowBorder)getBorder() ) { + FlowBorder border = cast(FlowBorder)getBorder(); + return border.getInsets(this).bottom + getDescent(); + } + return getDescent(); +} + +private int hDistanceBetween(TextFragmentBox box, int x) { + if (x < box.getX()) + return box.getX() - x; + return Math.max(0, x - (box.getX() + box.getWidth())); +} + +/** + * Returns true if a portion if the text is truncated using ellipses ("..."). + * @return true if the text is truncated with ellipses + */ +public bool isTextTruncated() { + for (int i = 0; i < fragments.size(); i++) { + if ((cast(TextFragmentBox)fragments.get(i)).isTruncated()) + return true; + } + return false; +} + +/** + * @see dwtx.draw2d.Figure#paintFigure(Graphics) + */ +protected void paintFigure(Graphics g) { + TextFragmentBox frag; + g.getClip(Rectangle.SINGLETON); + int yStart = Rectangle.SINGLETON.y; + int yEnd = Rectangle.SINGLETON.bottom(); + + for (int i = 0; i < fragments.size(); i++) { + frag = cast(TextFragmentBox)fragments.get(i); +// g.drawLine(frag.getX(), frag.getLineRoot().getVisibleTop(), +// frag.getWidth() + frag.getX(), frag.getLineRoot().getVisibleTop()); +// g.drawLine(frag.getX(), frag.getBaseline(), frag.getWidth() + frag.getX(), frag.getBaseline()); + if (frag.offset is -1) + continue; + //Loop until first visible fragment + if (yStart > frag.getLineRoot().getVisibleBottom() + 1)//The + 1 is for disabled text + continue; + //Break loop at first non-visible fragment + if (yEnd < frag.getLineRoot().getVisibleTop()) + break; + + String draw = getBidiSubstring(frag, i); + + if (frag.isTruncated()) + draw ~= ELLIPSIS; + + if (!isEnabled()) { + Color fgColor = g.getForegroundColor(); + g.setForegroundColor(ColorConstants.buttonLightest); + paintText(g, draw, + frag.getX() + 1, + frag.getBaseline() - getAscent() + 1, + frag.getBidiLevel()); + g.setForegroundColor(ColorConstants.buttonDarker); + paintText(g, draw, + frag.getX(), + frag.getBaseline() - getAscent(), + frag.getBidiLevel()); + g.setForegroundColor(fgColor); + } else { + paintText(g, draw, + frag.getX(), + frag.getBaseline() - getAscent(), + frag.getBidiLevel()); + } + } +} + +/** + * @see InlineFlow#paintSelection(dwtx.draw2d.Graphics) + */ +protected void paintSelection(Graphics graphics) { + if (selectionStart is -1) + return; + graphics.setXORMode(true); + graphics.setBackgroundColor(ColorConstants.white); + + TextFragmentBox frag; + for (int i = 0; i < fragments.size(); i++) { + frag = cast(TextFragmentBox)fragments.get(i); + //Loop until first visible fragment + if (frag.offset + frag.length <= selectionStart) + continue; + if (frag.offset > selectionEnd) + return; + if (selectionStart <= frag.offset && selectionEnd >= frag.offset + frag.length) { + int y = frag.getLineRoot().getVisibleTop(); + int height = frag.getLineRoot().getVisibleBottom() - y; + graphics.fillRectangle(frag.getX(), y, frag.getWidth(), height); + } else if (selectionEnd > frag.offset && selectionStart < frag.offset + frag.length) { + Point p1 = getPointInBox(frag, Math.max(frag.offset, selectionStart), i, false); + Point p2 = getPointInBox(frag, Math.min(frag.offset + frag.length, selectionEnd) - 1, i, true); + Rectangle rect = new Rectangle(p1, p2); + rect.width--; + rect.y = frag.getLineRoot().getVisibleTop(); + rect.height = frag.getLineRoot().getVisibleBottom() - rect.y; + graphics.fillRectangle(rect); + } + } +} + +protected void paintText(Graphics g, String draw, int x, int y, int bidiLevel) { + if (bidiLevel is -1) { + g.drawString(draw, x, y); + } else { + TextLayout tl = FlowUtilities.getTextLayout(); + if (isMirrored()) + tl.setOrientation(DWT.RIGHT_TO_LEFT); + tl.setFont(g.getFont()); + tl.setText(draw); + g.drawTextLayout(tl, x, y); + } +} + +/** + * @see dwtx.draw2d.text.FlowFigure#setBidiInfo(dwtx.draw2d.text.BidiInfo) + */ +public void setBidiInfo(BidiInfo info) { + this.bidiInfo = info; +} + +/** + * Sets the extent of selection. The selection range is inclusive. For example, the + * range [0, 0] indicates that the first character is selected. + * @param start the start offset + * @param end the end offset + * @since 3.1 + */ +public void setSelection(int start, int end) { + bool repaint_ = false; + + if (selectionStart is start) { + if (selectionEnd is end) + return; + repaint_ = true; + } else + repaint_ = selectionStart !is selectionEnd || start !is end; + + selectionStart = start; + selectionEnd = end; + if (repaint_) + repaint(); +} + +/** + * Sets the text being displayed. The string may not be null. + * @param s The new text + */ +public void setText(String s) { + if (s !is null && !s.equals(text)) { + text = s; + revalidateBidi(this); + repaint(); + } +} + +/** + * @see java.lang.Object#toString() + */ +public String toString() { + return text; +} + +private int vDistanceBetween(TextFragmentBox box, int y) { + int top = box.getBaseline() - box.getLineRoot().getAscent(); + if (y < top) + return top - y; + return Math.max(0, y - (box.getBaseline() + box.getLineRoot().getDescent())); +} + +/** + * Gets the FlowUtilities instance to be used in measurement + * calculations. + * + * @return a FlowUtilities instance + * @since 3.4 + */ +protected FlowUtilities getFlowUtilities() { + return FlowUtilities.INSTANCE; +} +package FlowUtilities getFlowUtilities_package() { + return getFlowUtilities(); +} + +/** + * Gets the TextUtilities instance to be used in measurement + * calculations. + * + * @return a TextUtilities instance + * @since 3.4 + */ +protected TextUtilities getTextUtilities() { + return TextUtilities.INSTANCE; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/TextFragmentBox.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/TextFragmentBox.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.TextFragmentBox; + +import dwt.dwthelper.utils; +import dwtx.draw2d.text.ContentBox; +import dwtx.draw2d.text.TextFlow; +import dwtx.draw2d.text.FlowUtilities; +import tango.text.convert.Format; + +/** + * A Geometric object for representing a TextFragment region on a line of Text. + */ +public class TextFragmentBox + : ContentBox +{ + +/** + * The fragment's length in characters. + */ +public int length; + +/** + * The character offset at which this fragment begins. + */ +public int offset; + +private TextFlow textflow; +private bool truncated; + +/** + * Creates a new TextFragmentBox for the given text flow. + * @param textflow the text flow + */ +public this(TextFlow textflow) { + this.textflow = textflow; +} + +/** + * @see dwtx.draw2d.text.FlowBox#containsPoint(int, int) + */ +public bool containsPoint(int x, int y) { + return x >= getX() + && x < getX() + getWidth() + && y >= getBaseline() - getAscentWithBorder() + && y <= getBaseline() + getDescentWithBorder(); +} + +/** + * Returns the textflow's font's ascent. The ascent is the same for all fragments in a + * given TextFlow. + * @return the ascent + */ +public int getAscent() { + return textflow.getAscent(); +} + +int getAscentWithBorder() { + return textflow.getAscent() + FlowUtilities.getBorderAscent(textflow); +} + +/** + * Returns the textflow's font's descent. The descent is the same for all fragments in a + * given TextFlow. + * @return the descent + */ +public int getDescent() { + return textflow.getDescent(); +} + +int getDescentWithBorder() { + return textflow.getDescent() + FlowUtilities.getBorderDescent(textflow); +} + +int getOuterAscent() { + return textflow.getAscent() + FlowUtilities.getBorderAscentWithMargin(textflow); +} + +int getOuterDescent() { + return textflow.getDescent() + FlowUtilities.getBorderDescentWithMargin(textflow); +} + +final int getTextTop() { + return getBaseline() - getAscent(); +} + +/** + * Returns true if the bidi level is odd. Right to left fragments should be + * queried and rendered with the RLO control character inserted in front. + * @return true if right-to-left + * @since 3.1 + */ +public bool isRightToLeft() { + // -1 % 2 is -1 + return getBidiLevel() % 2 is 1; +} + +/** + * Returns true if the fragment should be rendered as truncated. + * @return true if the fragment is truncated + * @since 3.1 + */ +public bool isTruncated() { + return truncated; +} + +/** + * Marks the fragment as having been truncated. + * @param value true if the fragment is truncated + * @since 3.1 + */ +public void setTruncated(bool value) { + this.truncated = value; +} + +/** + * @see java.lang.Object#toString() + */ +public String toString() { + return Format("[{}, {}) = \"{}\"",offset, (offset + length), //$NON-NLS-1$ //$NON-NLS-2$ + textflow.getText().substring(offset, offset + length) ); //$NON-NLS-1$ +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/text/TextLayout.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/text/TextLayout.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.text.TextLayout; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.text.FlowFigureLayout; +import dwtx.draw2d.text.TextFlow; +import dwtx.draw2d.text.TextFragmentBox; + +/** + * @author hudsonr + * @since 2.1 + */ +public abstract class TextLayout : FlowFigureLayout { + +/** + * Creates a new TextLayout with the given TextFlow + * @param flow The TextFlow + */ +public this(TextFlow flow) { + super(flow); +} + +/** + * Reuses an existing TextFragmentBox, or creates a new one. + * @param i the index + * @param fragments the original list of fragments + * @return a TextFragmentBox + */ +protected TextFragmentBox getFragment(int i, List fragments) { + if (fragments.size() > i) + return cast(TextFragmentBox)fragments.get(i); + TextFragmentBox box = new TextFragmentBox(cast(TextFlow)getFlowFigure()); + fragments.add(box); + return box; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/widgets/ImageBorder.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/widgets/ImageBorder.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.widgets.ImageBorder; + +import dwt.dwthelper.utils; + + + +import dwt.graphics.Image; +import dwtx.draw2d.AbstractBorder; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Insets; +import dwtx.draw2d.geometry.Rectangle; + +/** + * @author Pratik Shah + */ +class ImageBorder + : AbstractBorder +{ + +/* + * @TODO:Pratik Need to test this class extensively + * @TODO Test inside compound borders + */ + +private Insets imgInsets; +private Image image; +private Dimension imageSize; + +public this(Image image) { + setImage(image); +} + +public Insets getInsets(IFigure figure) { + return imgInsets; +} + +public Image getImage() { + return image; +} + +/** + * @see dwtx.draw2d.AbstractBorder#getPreferredSize(dwtx.draw2d.IFigure) + */ +public Dimension getPreferredSize(IFigure f) { + return imageSize; +} + +public void paint(IFigure figure, Graphics graphics, Insets insets) { + if (image is null) + return; + Rectangle rect = getPaintRectangle(figure, insets); + int x = rect.x; + int y = rect.y + (rect.height - imageSize.height) / 2; + graphics.drawImage(getImage(), x, y); +} + +public void setImage(Image img) { + image = img; + imageSize = new Dimension(image); + imgInsets = new Insets(); + imgInsets.left = imageSize.width; +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/draw2d/widgets/MultiLineLabel.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/widgets/MultiLineLabel.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,200 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + *******************************************************************************/ +module dwtx.draw2d.widgets.MultiLineLabel; + +import dwt.dwthelper.utils; + + + +import dwt.DWT; +import dwt.accessibility.ACC; +import dwt.accessibility.AccessibleAdapter; +import dwt.accessibility.AccessibleControlAdapter; +import dwt.accessibility.AccessibleControlEvent; +import dwt.accessibility.AccessibleEvent; +import dwt.events.KeyAdapter; +import dwt.events.KeyEvent; +import dwt.graphics.Font; +import dwt.graphics.Image; +import dwt.widgets.Composite; +import dwtx.draw2d.Border; +import dwtx.draw2d.ColorConstants; +import dwtx.draw2d.FigureCanvas; +import dwtx.draw2d.FocusEvent; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.MarginBorder; +import dwtx.draw2d.Viewport; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.text.FlowPage; +import dwtx.draw2d.text.TextFlow; +import dwtx.draw2d.widgets.ImageBorder; + +/** + * A widget for displaying a multi-line string. The label will have a vertical or + * horizontal scrollbar when needed. Unlike the platform Label, this label is focusable + * and accessible to screen-readers. + * + * @author hudsonr + */ +public final class MultiLineLabel : FigureCanvas { + +private TextFlow textFlow; +static const Border MARGIN; +private Image image; + +static this(){ + MARGIN = new MarginBorder(2); +} + +class FocusableViewport : Viewport { + this() { + super(true); + setFocusTraversable(true); + setBorder(MARGIN); + } + + public void handleFocusGained(FocusEvent event) { + super.handleFocusGained(event); + repaint(); + } + + public void handleFocusLost(FocusEvent event) { + super.handleFocusLost(event); + repaint(); + } + + protected void paintBorder(Graphics graphics) { + super.paintBorder(graphics); + if (hasFocus()) { + graphics.setForegroundColor(ColorConstants.black); + graphics.setBackgroundColor(ColorConstants.white); + graphics.drawFocus(getBounds().getResized(-1, -1)); + } + } +} + +/** + * Constructs a new MultiLineLabel with the given parent. + * @param parent the parent + */ +public this(Composite parent) { + super(parent); + setViewport(new FocusableViewport()); + + FlowPage page = new FlowPage(); + textFlow = new TextFlow(); + page.add(textFlow); + + setContents(page); + getViewport().setContentsTracksWidth(true); + addAccessibility(); +} + +private void addAccessibility() { + getAccessible().addAccessibleControlListener(new class() AccessibleControlAdapter { + public void getRole(AccessibleControlEvent e) { + e.detail = ACC.ROLE_LABEL; + } + public void getState(AccessibleControlEvent e) { + e.detail = ACC.STATE_READONLY; + } + }); + getAccessible().addAccessibleListener(new class() AccessibleAdapter { + public void getName(AccessibleEvent e) { + e.result = getText(); + } + }); + addKeyListener(new class() KeyAdapter { + public void keyPressed(KeyEvent e) { + Point p = getViewport().getViewLocation(); + int dy = getFont().getFontData()[0].getHeight(); + int dx = dy * 3 / 2; + bool mirrored = (e.widget.getStyle() & DWT.MIRRORED) !is 0; + if (e.keyCode is DWT.ARROW_DOWN) { + scrollToY(p.y + dy / 2); + scrollToY(p.y + dy); + scrollToY(p.y + dy * 3 / 2); + scrollToY(p.y + dy * 2); + } else if (e.keyCode is DWT.ARROW_UP) { + scrollToY(p.y - dy / 2); + scrollToY(p.y - dy); + scrollToY(p.y - dy * 3 / 2); + scrollToY(p.y - dy * 2); + } else if ((!mirrored && e.keyCode is DWT.ARROW_RIGHT) + || (mirrored && e.keyCode is DWT.ARROW_LEFT)) { + scrollToX(p.x + dx); + scrollToX(p.x + dx * 2); + scrollToX(p.x + dx * 3); + } else if ((!mirrored && e.keyCode is DWT.ARROW_LEFT) + || (mirrored && e.keyCode is DWT.ARROW_RIGHT)) { + scrollToX(p.x - dx); + scrollToX(p.x - dx * 2); + scrollToX(p.x - dx * 3); + } + } + }); +} + +/** + * @see dwt.widgets.Control#setEnabled(bool) + */ +public void setEnabled(bool enabled) { + super.setEnabled(enabled); + textFlow.setEnabled(getEnabled()); +} + +/** + * @return the Image for this label, or null if there is none + * @see #setImage(Image) + */ +public Image getImage() { + return image; +} + +/** + * Returns the text in this label. + * @return the text + */ +public String getText() { + return textFlow.getText(); +} + +/** + * @see dwt.widgets.Canvas#setFont(dwt.graphics.Font) + */ +public void setFont(Font font) { + super.setFont(font); + textFlow.revalidate(); +} + +/** + * @param image The Image to be used for this label. It can be + * null. + */ +public void setImage(Image image) { + this.image = image; + if (image !is null) + setBorder(new ImageBorder(image)); + else + setBorder(null); +} + +/** + * Sets the text for this label. + * @param text the new text + */ +public void setText(String text) { + textFlow.setText(text); +} + +} diff -r b492ba44e44d -r 95307ad235d9 dwtx/dwtxhelper/mangoicu/UBreakIterator.d --- a/dwtx/dwtxhelper/mangoicu/UBreakIterator.d Sat Jul 26 14:39:08 2008 +0200 +++ b/dwtx/dwtxhelper/mangoicu/UBreakIterator.d Sun Aug 03 00:52:14 2008 +0200 @@ -366,25 +366,29 @@ ***********************************************************************/ - static UBreakIterator openWordIterator( ULocale locale, char[] str ){ + static UBreakIterator openWordIterator( ULocale locale, char[] str = null ){ UBreakIterator res; - res.ut.openUTF8(str); auto e = ICU.UErrorCode.OK; res.handle = ubrk_open( Type.Word, locale.name.ptr, null, 0, e); ICU.testError (e, "failed to open word iterator"); - ubrk_setUText( res.handle, & res.ut, e); - ICU.testError (e, "failed to set text in iterator"); + if( str ) { + res.ut.openUTF8(str); + ubrk_setUText( res.handle, & res.ut, e); + ICU.testError (e, "failed to set text in iterator"); + } return res; } - static UBreakIterator openLineIterator( ULocale locale, char[] str ){ + static UBreakIterator openLineIterator( ULocale locale, char[] str = null ){ UBreakIterator res; - res.ut.openUTF8(str); auto e = ICU.UErrorCode.OK; res.handle = ubrk_open( Type.Line, locale.name.ptr, null, 0, e); ICU.testError (e, "failed to open line iterator"); - ubrk_setUText( res.handle, & res.ut, e); - ICU.testError (e, "failed to set text in iterator"); + if( str ) { + res.ut.openUTF8(str); + ubrk_setUText( res.handle, & res.ut, e); + ICU.testError (e, "failed to set text in iterator"); + } return res; }