view dwtx/jface/layout/AbstractColumnLayout.d @ 43:ea8ff534f622

Fix override and super aliases
author Frank Benoit <benoit@tionex.de>
date Fri, 11 Apr 2008 01:24:25 +0200
parents b6c35faf97c8
children 46a6e0e6ccd4
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2006, 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 (original file dwtx.ui.texteditor.templates.ColumnLayout)
 *     Tom Schindl <tom.schindl@bestsolution.at> - refactored to be widget independent (bug 171824)
 *                                               - fix for bug 178280, 184342, 184045
 * Port to the D programming language:
 *     Frank Benoit <benoit@tionex.de>
 *******************************************************************************/
module dwtx.jface.layout.AbstractColumnLayout;



import dwt.DWT;
import dwt.graphics.Point;
import dwt.graphics.Rectangle;
import dwt.widgets.Composite;
import dwt.widgets.Event;
import dwt.widgets.Layout;
import dwt.widgets.Listener;
import dwt.widgets.Scrollable;
import dwt.widgets.Widget;
import dwtx.core.runtime.Assert;
import dwtx.jface.util.Policy;
import dwtx.jface.viewers.ColumnLayoutData;
import dwtx.jface.viewers.ColumnPixelData;
import dwtx.jface.viewers.ColumnWeightData;
import dwtx.jface.viewers.TableLayout;

import dwt.dwthelper.utils;

/**
 * The AbstractColumnLayout is a {@link Layout} used to set the size of a table
 * in a consistent way even during a resize unlike a {@link TableLayout} which
 * only sets initial sizes.
 *
 * <p><b>You can only add the layout to a container whose
 * only child is the table/tree control you want the layouts applied to.</b>
 * </p>
 *
 * @since 3.3
 */
abstract class AbstractColumnLayout : Layout {
    /**
     * The number of extra pixels taken as horizontal trim by the table column.
     * To ensure there are N pixels available for the content of the column,
     * assign N+COLUMN_TRIM for the column width.
     *
     * @since 3.1
     */
    private static int COLUMN_TRIM;

    static const bool IS_GTK;

    static const String LAYOUT_DATA;

    static this(){
        COLUMN_TRIM = "carbon".equals(DWT.getPlatform()) ? 24 : 3; //$NON-NLS-1$
        IS_GTK = "gtk".equals(DWT.getPlatform());//$NON-NLS-1$
        LAYOUT_DATA = Policy.JFACE ~ ".LAYOUT_DATA"; //$NON-NLS-1$
    }

    private bool inupdateMode = false;

    private bool relayout = true;

    private Listener resizeListener;
    private void init_resizeListener(){
        resizeListener = new class Listener {

            public void handleEvent(Event event) {
                if( ! inupdateMode ) {
                    updateColumnData(event.widget);
                }
            }

        };
    }
    public this(){
        init_resizeListener();
    }

    /**
     * Adds a new column of data to this table layout.
     *
     * @param column
     *            the column
     *
     * @param data
     *            the column layout data
     */
    public void setColumnData(Widget column, ColumnLayoutData data) {

        if( column.getData(LAYOUT_DATA) is null ) {
            column.addListener(DWT.Resize, resizeListener);
        }

        column.setData(LAYOUT_DATA, data);
    }

    /**
     * Compute the size of the table or tree based on the ColumnLayoutData and
     * the width and height hint.
     *
     * @param scrollable
     *            the widget to compute
     * @param wHint
     *            the width hint
     * @param hHint
     *            the height hint
     * @return Point where x is the width and y is the height
     */
    private Point computeTableTreeSize(Scrollable scrollable, int wHint,
            int hHint) {
        Point result = scrollable.computeSize(wHint, hHint);

        int width = 0;
        int size = getColumnCount(scrollable);
        for (int i = 0; i < size; ++i) {
            ColumnLayoutData layoutData = getLayoutData(scrollable,i);
            if ( auto col = cast(ColumnPixelData)layoutData) {
                width += col.width;
                if (col.addTrim) {
                    width += COLUMN_TRIM;
                }
            } else if ( auto col = cast(ColumnWeightData)layoutData ) {
                width += col.minimumWidth;
            } else {
                Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
            }
        }
        if (width > result.x)
            result.x = width;

        return result;
    }

    /**
     * Layout the scrollable based on the supplied width and area. Only increase
     * the size of the scrollable if increase is <code>true</code>.
     *
     * @param scrollable
     * @param width
     * @param area
     * @param increase
     */
    private void layoutTableTree(Scrollable scrollable, int width,
            Rectangle area, bool increase) {
        int size = getColumnCount(scrollable);
        int[] widths = new int[size];

        int[] weightIteration = new int[size];
        int numberOfWeightColumns = 0;

        int fixedWidth = 0;
        int minWeightWidth = 0;
        int totalWeight = 0;

        // First calc space occupied by fixed columns
        for (int i = 0; i < size; i++) {
            ColumnLayoutData col = getLayoutData(scrollable,i);
            if ( auto cpd = cast(ColumnPixelData)col ) {
                int pixels = cpd.width;
                if (cpd.addTrim) {
                    pixels += COLUMN_TRIM;
                }
                widths[i] = pixels;
                fixedWidth += pixels;
            } else if ( auto cw = cast(ColumnWeightData) col ) {
                weightIteration[numberOfWeightColumns] = i;
                numberOfWeightColumns++;
                totalWeight += cw.weight;
                minWeightWidth += cw.minimumWidth;
                widths[i] = cw.minimumWidth;
            } else {
                Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
            }
        }

        // Do we have columns that have a weight?
        int restIncludingMinWidths = width - fixedWidth;
        int rest = restIncludingMinWidths - minWeightWidth;
        if (numberOfWeightColumns > 0 && rest > 0) {

            // Modify the weights to reflect what each column already
            // has due to its minimum. Otherwise, columns with low
            // minimums get discriminated.
            int totalWantedPixels = 0;
            int[] wantedPixels = new int[numberOfWeightColumns];
            for (int i = 0; i < numberOfWeightColumns; i++) {
                ColumnWeightData cw = cast(ColumnWeightData) getLayoutData(scrollable,weightIteration[i]);
                wantedPixels[i] = totalWeight is 0 ? 0 : cw.weight
                        * restIncludingMinWidths / totalWeight;
                totalWantedPixels += wantedPixels[i];
            }

            // Now distribute the rest to the columns with weight.
            int totalDistributed = 0;
            for (int i = 0; i < numberOfWeightColumns; ++i) {
                int pixels = totalWantedPixels is 0 ? 0 : wantedPixels[i]
                        * rest / totalWantedPixels;
                totalDistributed += pixels;
                widths[weightIteration[i]] += pixels;
            }

            // Distribute any remaining pixels to columns with weight.
            int diff = rest - totalDistributed;
            for (int i = 0; diff > 0; i = ((i + 1) % numberOfWeightColumns)) {
                ++widths[weightIteration[i]];
                --diff;
            }
        }

        if (increase) {
            scrollable.setSize(area.width, area.height);
        }

        inupdateMode = true;
        setColumnWidths(scrollable, widths);
        scrollable.update();
        inupdateMode = false;

        if (!increase) {
            scrollable.setSize(area.width, area.height);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see dwt.widgets.Layout#computeSize(dwt.widgets.Composite,
     *      int, int, bool)
     */
    protected override Point computeSize(Composite composite, int wHint, int hHint,
            bool flushCache) {
        return computeTableTreeSize(getControl(composite), wHint, hHint);
    }

    /*
     * (non-Javadoc)
     *
     * @see dwt.widgets.Layout#layout(dwt.widgets.Composite,
     *      bool)
     */
    protected override void layout(Composite composite, bool flushCache) {
        Rectangle area = composite.getClientArea();
        Scrollable table = getControl(composite);
        int tableWidth = table.getSize().x;
        int trim = computeTrim(area, table, tableWidth);
        int width = Math.max(0, area.width - trim);

        if (width > 1)
            layoutTableTree(table, width, area, tableWidth < area.width);

        // For the first time we need to relayout because Scrollbars are not
        // calculate appropriately
        if (relayout) {
            relayout = false;
            composite.layout();
        }
    }

    /**
     * Compute the area required for trim.
     *
     * @param area
     * @param scrollable
     * @param currentWidth
     * @return int
     */
    private int computeTrim(Rectangle area, Scrollable scrollable,
            int currentWidth) {
        int trim;

        if (currentWidth > 1) {
            trim = currentWidth - scrollable.getClientArea().width;
        } else {
            // initially, the table has no extend and no client area - use the
            // border with
            // plus some padding as educated guess
            trim = 2 * scrollable.getBorderWidth() + 1;
        }

        return trim;
    }

    /**
     * Get the control being laid out.
     *
     * @param composite
     *            the composite with the layout
     * @return {@link Scrollable}
     */
    Scrollable getControl(Composite composite) {
        return cast(Scrollable) composite.getChildren()[0];
    }

    /**
     * Get the number of columns for the receiver.
     *
     * @return the number of columns
     */
    abstract int getColumnCount(Scrollable tableTree);

    /**
     * Set the widths of the columns.
     *
     * @param widths
     */
    abstract void setColumnWidths(Scrollable tableTree, int[] widths);

    abstract ColumnLayoutData getLayoutData(Scrollable tableTree, int columnIndex);

    abstract void updateColumnData(Widget column);
}