diff dwtx/draw2d/FigureCanvas.d @ 98:95307ad235d9

Added Draw2d code, still work in progress
author Frank Benoit <benoit@tionex.de>
date Sun, 03 Aug 2008 00:52:14 +0200
parents
children
line wrap: on
line diff
--- /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 <benoit@tionex.de>
+ *******************************************************************************/
+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}.
+ *
+ * <dl>
+ * <dt><b>Required Styles (when using certain constructors):</b></dt>
+ * <dd>V_SCROLL, H_SCROLL, NO_REDRAW_RESIZE</dd>
+ * <dt><b>Optional Styles:</b></dt>
+ * <dd>DOUBLE_BUFFERED, RIGHT_TO_LEFT, LEFT_TO_RIGHT, NO_BACKGROUND, BORDER</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles RIGHT_TO_LEFT, LEFT_TO_RIGHT may be specified.
+ * </p>
+ */
+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:
+ * <UL>
+ *   <LI>{@link DWT#NO_REDRAW_RESIZE}</LI>
+ *   <LI>{@link DWT#NO_BACKGROUND}</LI>
+ *   <LI>{@link DWT#V_SCROLL}</LI>
+ *   <LI>{@link DWT#H_SCROLL}</LI>
+ * </UL>
+ */
+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 <b>required</b> 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 <b>required</b> 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 <code>null</code>, 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 <code>hOffset</code>.
+ *
+ * @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 <code>vOffset</code>.
+ *
+ * @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 <code>new Viewport(true)</code>.
+ *
+ * @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);
+}
+
+}