view dwtx/draw2d/FlowLayout.d @ 192:c3583c6ec027

Added missing default cases for switch statements
author Frank Benoit <benoit@tionex.de>
date Mon, 03 Nov 2008 22:52:26 +0100
parents 95307ad235d9
children
line wrap: on
line source

/*******************************************************************************
 * 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 <benoit@tionex.de>
 *******************************************************************************/
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 <code>true</code> 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.
 * <P>
 * Possible values are :
 * <ul>
 *   <li>{@link #ALIGN_CENTER}
 *   <li>{@link #ALIGN_LEFTTOP}
 *   <li>{@link #ALIGN_RIGHTBOTTOM}
 * </ul>
 *
 * @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.
 * <P>
 * Possible values are :
 * <ul>
 *   <li>{@link #ALIGN_CENTER}
 *   <li>{@link #ALIGN_LEFTTOP}
 *   <li>{@link #ALIGN_RIGHTBOTTOM}
 * </ul>
 *
 * @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 <code>true</code> if the orientation of the layout is horizontal.
 *
 * @return <code>true</code> 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;
        default:
    }

    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;
                default:
            }
            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 <code>true</code> 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.
 * <P>
 * Possible values are :
 * <ul>
 *   <li>{@link #ALIGN_CENTER}
 *   <li>{@link #ALIGN_LEFTTOP}
 *   <li>{@link #ALIGN_RIGHTBOTTOM}
 * </ul>
 *
 * @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.
 * <P>
 * Possible values are :
 * <ul>
 *   <li>{@link #ALIGN_CENTER}
 *   <li>{@link #ALIGN_LEFTTOP}
 *   <li>{@link #ALIGN_RIGHTBOTTOM}
 * </ul>
 *
 * @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;
}

}