view dwt/custom/CBanner.d @ 7:e831403a80a9

Add 'cast' to casts
author Frank Benoit <benoit@tionex.de>
date Wed, 27 Aug 2008 14:30:35 +0200
parents 380af2bdd8e5
children 5b53d338c709
line wrap: on
line source

/*******************************************************************************
 * 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:
 *     Jacob Carlborg <jacob.carlborg@gmail.com>
 *******************************************************************************/
module dwt.custom.CBanner;

import dwt.DWT;
import dwt.DWTException;
import dwt.graphics.Color;
import dwt.graphics.Cursor;
import dwt.graphics.GC;
import dwt.graphics.Point;
import dwt.graphics.RGB;
import dwt.graphics.Rectangle;
import dwt.widgets.Composite;
import dwt.widgets.Control;
import dwt.widgets.Event;
import dwt.widgets.Layout;
import dwt.widgets.Listener;

/**
 * Instances of this class implement a Composite that lays out its
 * children and allows programmatic control of the layout. It draws
 * a separator between the left and right children which can be dragged
 * to resize the right control.
 * CBanner is used in the workbench to layout the toolbar area and
 * perspective switching toolbar.
 * <p>
 * Note that although this class is a subclass of <code>Composite</code>,
 * it does not make sense to set a layout on it.
 * </p><p>
 * <dl>
 * <dt><b>Styles:</b></dt>
 * <dd>NONE</dd>
 * <dt><b>Events:</b></dt>
 * <dd>cast(None)</dd>
 * </dl>
 * <p>
 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
 * </p>
 * 
 * @since 3.0
 */

public class CBanner : Composite {

    Control left;
    Control right;
    Control bottom;

    bool simple = true;

    int[] curve = new int[0];
    int curveStart = 0;
    Rectangle curveRect = new Rectangle(0, 0, 0, 0);
    int curve_width = 5;
    int curve_indent = -2;

    int rightWidth = DWT.DEFAULT;
    int rightMinWidth = 0;
    int rightMinHeight = 0;
    Cursor resizeCursor;
    bool dragging = false;
    int rightDragDisplacement = 0;

    static const int OFFSCREEN = -200;
    static const int BORDER_BOTTOM = 2;
    static const int BORDER_TOP = 3;
    static const int BORDER_STRIPE = 1;
    static const int CURVE_TAIL = 200;
    static const int BEZIER_RIGHT = 30;
    static const int BEZIER_LEFT = 30;
    static const int MIN_LEFT = 10;
    static int BORDER1 = DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW;

    /**
     * Constructs a new instance of this class given its parent
     * and a style value describing its behavior and appearance.
     * <p>
     * The style value is either one of the style constants defined in
     * class <code>DWT</code> which is applicable to instances of this
     * class, or must be built by <em>bitwise OR</em>'ing together 
     * (that is, using the <code>int</code> "|" operator) two or more
     * of those <code>DWT</code> style constants. The class description
     * lists the style constants that are applicable to the class.
     * Style bits are also inherited from superclasses.
     * </p>
     *
     * @param parent a widget which will be the parent of the new instance (cannot be null)
     * @param style the style of widget to construct
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
     * </ul>
     * @exception DWTException <ul>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
     * </ul>
     *
     */
    public this (Composite parent, int style) {
        super(parent, checkStyle(style));
        super.setLayout(new CBannerLayout());
        resizeCursor = new Cursor(getDisplay(), DWT.CURSOR_SIZEWE);

        Listener listener = new class Listener {
            public void handleEvent (Event e) {
                switch (e.type) {
                    case DWT.Dispose:
                        onDispose();
                    break;
                    case DWT.MouseDown:
                        onMouseDown(e.x, e.y);
                    break;
                    case DWT.MouseExit:
                        onMouseExit();
                    break;
                    case DWT.MouseMove:
                        onMouseMove(e.x, e.y);
                    break;
                    case DWT.MouseUp:
                        onMouseUp();
                    break;
                    case DWT.Paint:
                        onPaint(e.gc);
                    break;
                    case DWT.Resize:
                        onResize();
                    break;
                }
            }
        };
        int[] events = new int[][DWT.Dispose , DWT.MouseDown , DWT.MouseExit , DWT.MouseMove , DWT.MouseUp , DWT.Paint , DWT.Resize];
        for (int i = 0; i < events.length; i++) {
            addListener(events[i], listener);
        }
    }

    static int[] bezier (int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3, int count) {
        // The parametric equations for a Bezier curve for x[t] and y[t] where  0 <= t <=1 are:
        // x[t] = x0+3(x1-x0)t+3(x0+x2-2x1)t^2+(x3-x0+3x1-3x2)t^3
        // y[t] = y0+3(y1-y0)t+3(y0+y2-2y1)t^2+(y3-y0+3y1-3y2)t^3
        double a0 = x0;
        double a1 = 3 * (x1 - x0);
        double a2 = 3 * (x0 + x2 - 2 * x1);
        double a3 = x3 - x0 + 3 * x1 - 3 * x2;
        double b0 = y0;
        double b1 = 3 * (y1 - y0);
        double b2 = 3 * (y0 + y2 - 2 * y1);
        double b3 = y3 - y0 + 3 * y1 - 3 * y2;

        int[] polygon = new int[2 * count + 2];
        for (int i = 0; i <= count; i++) {
            double t = cast(double) i / cast(double) count;
            polygon[2 * i] = cast(int) (a0 + a1 * t + a2 * t * t + a3 * t * t * t);
            polygon[2 * i + 1] = cast(int) (b0 + b1 * t + b2 * t * t + b3 * t * t * t);
        }
        return polygon;
    }

    static int checkStyle (int style) {
        return DWT.NONE;
    }

    /**
     * Returns the Control that appears on the bottom side of the banner.
     * 
     * @return the control that appears on the bottom side of the banner or null
     * 
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     * 
     * @since 3.0
     */
    public Control getBottom () {
        checkWidget();
        return bottom;
    }

    public Rectangle getClientArea () {
        return new Rectangle(0, 0, 0, 0);
    }

    /**
     * Returns the Control that appears on the left side of the banner.
     * 
     * @return the control that appears on the left side of the banner or null
     * 
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     * 
     * @since 3.0
     */
    public Control getLeft () {
        checkWidget();
        return left;
    }

    /**
     * Returns the Control that appears on the right side of the banner.
     * 
     * @return the control that appears on the right side of the banner or null
     * 
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     * 
     * @since 3.0
     */
    public Control getRight () {
        checkWidget();
        return right;
    }

    /**
     * Returns the minimum size of the control that appears on the right of the banner.
     * 
     * @return the minimum size of the control that appears on the right of the banner
     * 
     * @since 3.1
     */
    public Point getRightMinimumSize () {
        checkWidget();
        return new Point(rightMinWidth, rightMinHeight);
    }

    /**
     * Returns the width of the control that appears on the right of the banner.
     * 
     * @return the width of the control that appears on the right of the banner
     * 
     * @since 3.0
     */
    public int getRightWidth () {
        checkWidget();
        if (right is null)
            return 0;
        if (rightWidth is DWT.DEFAULT) {
            Point size = right.computeSize(DWT.DEFAULT, DWT.DEFAULT, false);
            return size.x;
        }
        return rightWidth;
    }

    /**
     * Returns <code>true</code> if the CBanner is rendered
     * with a simple, traditional shape.
     * 
     * @return <code>true</code> if the CBanner is rendered with a simple shape
     * 
     * @since 3.0
     */
    public bool getSimple () {
        checkWidget();
        return simple;
    }

    void onDispose () {
        if (resizeCursor !is null)
            resizeCursor.dispose();
        resizeCursor = null;
        left = null;
        right = null;
        bottom = null;
    }

    void onMouseDown (int x, int y) {
        if (curveRect.contains(x, y)) {
            dragging = true;
            rightDragDisplacement = curveStart - x + curve_width - curve_indent;
        }
    }

    void onMouseExit () {
        if (!dragging)
            setCursor(null);
    }

    void onMouseMove (int x, int y) {
        if (dragging) {
            Point size = getSize();
            if (!(0 < x && x < size.x))
                return;
            rightWidth = Math.max(0, size.x - x - rightDragDisplacement);
            if (rightMinWidth is DWT.DEFAULT) {
                Point minSize = right.computeSize(rightMinWidth, rightMinHeight);
                rightWidth = Math.max(minSize.x, rightWidth);
            }
            else {
                rightWidth = Math.max(rightMinWidth, rightWidth);
            }
            layout(false);
            return;
        }
        if (curveRect.contains(x, y)) {
            setCursor(resizeCursor);
        }
        else {
            setCursor(null);
        }
    }

    void onMouseUp () {
        dragging = false;
    }

    void onPaint (GC gc) {
        //   Useful for debugging paint problems
        //  {
        //  Point size = getSize(); 
        //  gc.setBackground(getDisplay().getSystemColor(DWT.COLOR_GREEN));
        //  gc.fillRectangle(-10, -10, size.x+20, size.y+20);
        //  }
        if (left is null && right is null)
            return;
        Point size = getSize();
        Color border1 = getDisplay().getSystemColor(BORDER1);
        if (bottom !is null) {
            int y = bottom.getBounds().y - BORDER_STRIPE - 1;
            gc.setForeground(border1);
            gc.drawLine(0, y, size.x, y);
        }
        if (left is null || right is null)
            return;
        int[] line1 = new int[curve.length + 6];
        int index = 0;
        int x = curveStart;
        line1[index++] = x + 1;
        line1[index++] = size.y - BORDER_STRIPE;
        for (int i = 0; i < curve.length / 2; i++) {
            line1[index++] = x + curve[2 * i];
            line1[index++] = curve[2 * i + 1];
        }
        line1[index++] = x + curve_width;
        line1[index++] = 0;
        line1[index++] = size.x;
        line1[index++] = 0;

        Color background = getBackground();

        if (getDisplay().getDepth() >= 15) {
            // Anti- aliasing
            int[] line2 = new int[line1.length];
            index = 0;
            for (int i = 0; i < line1.length / 2; i++) {
                line2[index] = line1[index++] - 1;
                line2[index] = line1[index++];
            }
            int[] line3 = new int[line1.length];
            index = 0;
            for (int i = 0; i < line1.length / 2; i++) {
                line3[index] = line1[index++] + 1;
                line3[index] = line1[index++];
            }
            RGB from = border1.getRGB();
            RGB to = background.getRGB();
            int red = from.red + 3 * (to.red - from.red) / 4;
            int green = from.green + 3 * (to.green - from.green) / 4;
            int blue = from.blue + 3 * (to.blue - from.blue) / 4;
            Color color = new Color(getDisplay(), red, green, blue);
            gc.setForeground(color);
            gc.drawPolyline(line2);
            gc.drawPolyline(line3);
            color.dispose();

            // draw tail fading to background
            int x1 = Math.max(0, curveStart - CURVE_TAIL);
            gc.setForeground(background);
            gc.setBackground(border1);
            gc.fillGradientRectangle(x1, size.y - BORDER_STRIPE, curveStart - x1 + 1, 1, false);
        }
        else {
            // draw solid tail
            int x1 = Math.max(0, curveStart - CURVE_TAIL);
            gc.setForeground(border1);
            gc.drawLine(x1, size.y - BORDER_STRIPE, curveStart + 1, size.y - BORDER_STRIPE);
        }

        // draw border
        gc.setForeground(border1);
        gc.drawPolyline(line1);
    }

    void onResize () {
        updateCurve(getSize().y);
    }

    /**
     * Set the control that appears on the bottom side of the banner.
     * The bottom control is optional.  Setting the bottom control to null will remove it from 
     * the banner - however, the creator of the control must dispose of the control.
     * 
     * @param control the control to be displayed on the bottom or null
     * 
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     *    <li>ERROR_INVALID_ARGUMENT - if the bottom control was not created as a child of the receiver</li>
     * </ul>
     * 
     * @since 3.0
     */
    public void setBottom (Control control) {
        checkWidget();
        if (control !is null && control.getParent() !is this) {
            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
        }
        if (bottom !is null && !bottom.isDisposed()) {
            Point size = bottom.getSize();
            bottom.setLocation(OFFSCREEN - size.x, OFFSCREEN - size.y);
        }
        bottom = control;
        layout(false);
    }

    /**
     * Sets the layout which is associated with the receiver to be
     * the argument which may be null.
     * <p>
     * Note: No Layout can be set on this Control because it already
     * manages the size and position of its children.
     * </p>
     *
     * @param layout the receiver's new layout or null
     *
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public void setLayout (Layout layout) {
        checkWidget();
        return;
    }

    /**
     * Set the control that appears on the left side of the banner.
     * The left control is optional.  Setting the left control to null will remove it from 
     * the banner - however, the creator of the control must dispose of the control.
     * 
     * @param control the control to be displayed on the left or null
     * 
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     *    <li>ERROR_INVALID_ARGUMENT - if the left control was not created as a child of the receiver</li>
     * </ul>
     * 
     * @since 3.0
     */
    public void setLeft (Control control) {
        checkWidget();
        if (control !is null && control.getParent() !is this) {
            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
        }
        if (left !is null && !left.isDisposed()) {
            Point size = left.getSize();
            left.setLocation(OFFSCREEN - size.x, OFFSCREEN - size.y);
        }
        left = control;
        layout(false);
    }

    /**
     * Set the control that appears on the right side of the banner.
     * The right control is optional.  Setting the right control to null will remove it from 
     * the banner - however, the creator of the control must dispose of the control.
     * 
     * @param control the control to be displayed on the right or null
     * 
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     *    <li>ERROR_INVALID_ARGUMENT - if the right control was not created as a child of the receiver</li>
     * </ul>
     * 
     * @since 3.0
     */
    public void setRight (Control control) {
        checkWidget();
        if (control !is null && control.getParent() !is this) {
            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
        }
        if (right !is null && !right.isDisposed()) {
            Point size = right.getSize();
            right.setLocation(OFFSCREEN - size.x, OFFSCREEN - size.y);
        }
        right = control;
        layout(false);
    }

    /**
     * Set the minimum height of the control that appears on the right side of the banner.
     * 
     * @param size the minimum size of the control on the right
     * 
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     *    <li>ERROR_INVALID_ARGUMENT - if the size is null or the values of size are less than DWT.DEFAULT</li>
     * </ul>
     * 
     * @since 3.1
     */
    public void setRightMinimumSize (Point size) {
        checkWidget();
        if (size is null || size.x < DWT.DEFAULT || size.y < DWT.DEFAULT)
            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
        rightMinWidth = size.x;
        rightMinHeight = size.y;
        layout(false);
    }

    /**
     * Set the width of the control that appears on the right side of the banner.
     * 
     * @param width the width of the control on the right
     * 
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     *    <li>ERROR_INVALID_ARGUMENT - if width is less than DWT.DEFAULT</li>
     * </ul>
     * 
     * @since 3.0
     */
    public void setRightWidth (int width) {
        checkWidget();
        if (width < DWT.DEFAULT)
            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
        rightWidth = width;
        layout(false);
    }

    /**
     * Sets the shape that the CBanner will use to render itself.  
     * 
     * @param simple <code>true</code> if the CBanner should render itself in a simple, traditional style
     * 
     * @exception DWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     *
     * @since 3.0
     */
    public void setSimple (bool simple) {
        checkWidget();
        if (this.simple !is simple) {
            this.simple = simple;
            if (simple) {
                curve_width = 5;
                curve_indent = -2;
            }
            else {
                curve_width = 50;
                curve_indent = 5;
            }
            updateCurve(getSize().y);
            layout(false);
            redraw();
        }
    }

    void updateCurve (int height) {
        int h = height - BORDER_STRIPE;
        if (simple) {
            curve = new int[][0 , h , 1 , h , 2 , h - 1 , 3 , h - 2 , 3 , 2 , 4 , 1 , 5 , 0];
        }
        else {
            curve = bezier(0, h + 1, BEZIER_LEFT, h + 1, curve_width - BEZIER_RIGHT, 0, curve_width, 0, curve_width);
        }
    }
}