/*
 * Copyright 1996 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package sun.awt;

import java.awt.*;

/**
 * Extends the FlowLayout class to support both vertical and horizontal
 * layout of components.  Orientation can be changed dynamically after
 * creation by calling either of the methods @method orientHorizontally or
 * @method orientVertically.  Separate values for alignment, vertical gap,
 * and horizontal gap can be specified for horizontal and vertical
 * orientation.
 *
 * @author Terry Cline
 */
public class OrientableFlowLayout extends FlowLayout {
    /**
     * The horizontal orientation constant.
     */
    public static final int HORIZONTAL = 0;

    /**
     * The vertical orientation constant.
     */
    public static final int VERTICAL   = 1;

    /**
     * The top vertical alignment constant.
     */
    public static final int TOP        = 0;

    /**
     * The bottom vertical alignment constant.
     */
    public static final int BOTTOM     = 2; // CENTER == 1

    int orientation;
    int vAlign;
    int vHGap;
    int vVGap;

    /**
     * Constructs a new flow layout with a horizontal orientation and
     * centered alignment.
     */
    public OrientableFlowLayout() {
        this(HORIZONTAL, CENTER, CENTER, 5, 5, 5, 5);
    }

    /**
     * Constructs a new flow layout with the specified orientation and
     * a centered alignment.
     *
     * @param orientation the orientation, one of HORIZONTAL or VERTICAL.
     */
    public OrientableFlowLayout(int orientation) {
        this(orientation, CENTER, CENTER, 5, 5, 5, 5);
    }

    /**
     * Constructs a new flow layout with the specified orientation and
     * alignment.
     *
     * @param orientation the orientation, one of HORIZONTAL or VERTICAL.
     * @param hAlign the horizontal alignment, one of LEFT, CENTER, or RIGHT.
     * @param vAlign the vertical alignment, one of TOP, CENTER, or BOTTOM.
     */
    public OrientableFlowLayout(int orientation, int hAlign, int vAlign) {
        this(orientation, hAlign, vAlign, 5, 5, 5, 5);
    }

    /**
     * Constructs a new flow layout with the specified orientation,
     * alignment, and gap values.
     *
     * @param orientation the orientation, one of HORIZONTAL or VERTICAL.
     * @param hAlign the horizontal alignment, one of LEFT, CENTER, or RIGHT.
     * @param vAlign the vertical alignment, one of TOP, CENTER, or BOTTOM.
     * @param hHGap the horizontal gap between components in HORIZONTAL.
     * @param hVGap the vertical gap between components in HORIZONTAL.
     * @param vHGap the horizontal gap between components in VERTICAL.
     * @param vVGap the vertical gap between components in VERTICAL.
     */
    public OrientableFlowLayout(int orientation, int hAlign, int vAlign, int hHGap, int hVGap, int vHGap, int vVGap) {
        super(hAlign, hHGap, hVGap);
        this.orientation = orientation;
        this.vAlign      = vAlign;
        this.vHGap       = vHGap;
        this.vVGap       = vVGap;
    }

    /**
     * Set the layout's current orientation to horizontal.
     */
    public synchronized void orientHorizontally() {
        orientation = HORIZONTAL;
    }

    /**
     * Set the layout's current orientation to vertical.
     */
    public synchronized void orientVertically() {
        orientation = VERTICAL;
    }

    /**
     * Returns the preferred dimensions for this layout given the
     * components in the specified target container.
     *
     * @param target the component which needs to be laid out.
     * @see Container
     * @see FlowLayout
     * @see #minimumLayoutSize
     */
    public Dimension preferredLayoutSize(Container target) {
        if (orientation == HORIZONTAL) {
            return super.preferredLayoutSize(target);
        }
        else {
            Dimension dim = new Dimension(0, 0);

            int n = target.countComponents();
            for (int i = 0; i < n; i++) {
                Component c = target.getComponent(i);
                if (c.isVisible()) {
                    Dimension cDim = c.preferredSize();
                    dim.width = Math.max(dim.width, cDim.width);
                    if (i > 0) {
                        dim.height += vVGap;
                    }
                    dim.height += cDim.height;
                }
            }

            Insets insets = target.insets();;
            dim.width  += insets.left + insets.right  + vHGap*2;
            dim.height += insets.top  + insets.bottom + vVGap*2;

            return dim;
        }
    }

    /**
     * Returns the minimum dimensions needed to layout the components
     * contained in the specified target container.
     *
     * @param target the component which needs to be laid out.
     * @see #preferredLayoutSize.
     */
    public Dimension minimumLayoutSize(Container target) {
        if (orientation == HORIZONTAL) {
            return super.minimumLayoutSize(target);
        }
        else {
            Dimension dim = new Dimension(0, 0);

            int n = target.countComponents();
            for (int i = 0; i < n; i++) {
                Component c = target.getComponent(i);
                if (c.isVisible()) {
                    Dimension cDim = c.minimumSize();
                    dim.width = Math.max(dim.width, cDim.width);
                    if (i > 0) {
                        dim.height += vVGap;
                    }
                    dim.height += cDim.height;
                }
            }

            Insets insets = target.insets();
            dim.width  += insets.left + insets.right  + vHGap*2;
            dim.height += insets.top  + insets.bottom + vVGap*2;

            return dim;
        }
    }

    /**
     * Lays out the container.  This method will reshape the
     * components in the target to satisfy the constraints of the
     * layout.
     *
     * @param target the specified component being laid out.
     * @see Container.
     */
    public void layoutContainer(Container target) {
        if (orientation == HORIZONTAL) {
            super.layoutContainer(target);
        }
        else {
            Insets insets = target.insets();
            Dimension targetDim = target.size();
            int maxHeight = targetDim.height - (insets.top + insets.bottom + vVGap*2);
            int x = insets.left + vHGap;
            int y = 0;
            int colWidth = 0;
            int start = 0;

            int n = target.countComponents();
            for (int i = 0; i < n; i++) {
                Component c = target.getComponent(i);
                if (c.isVisible()) {
                    Dimension cDim = c.preferredSize();
                    c.resize(cDim.width, cDim.height);

                    if ((y == 0) || ((y + cDim.height) <= maxHeight)) {
                        if (y > 0) {
                            y += vVGap;
                        }
                        y += cDim.height;
                        colWidth = Math.max(colWidth, cDim.width);
                    }
                    else {
                        moveComponents(target,
                                       x,
                                       insets.top + vVGap,
                                       colWidth,
                                       maxHeight - y,
                                       start,
                                       i);
                        x += vHGap + colWidth;
                        y = cDim.width;
                        colWidth = cDim.width;
                        start = i;
                    }
                }
            }

            moveComponents(target,
                           x,
                           insets.top + vVGap,
                           colWidth,
                           maxHeight - y,
                           start,
                           n);
        }
    }

    /**
     * Aligns the components vertically if there is any slack.
     *
     * @param target the container whose components need to be moved.
     * @param x the x coordinate.
     * @param y the y coordinate.
     * @param width the width available.
     * @param height the height available.
     * @param colStart the beginning of the column.
     * @param colEnd the end of the column.
     */
    private void moveComponents(Container target, int x, int y, int width, int height, int colStart, int colEnd) {
        switch (vAlign) {
        case TOP:
            break;
        case CENTER:
            y += height/2;
            break;
        case BOTTOM:
            y += height;
        }

        for (int i = colStart; i < colEnd; i++) {
            Component c = target.getComponent(i);
            Dimension cDim = c.size();
            if (c.isVisible()) {
                c.move(x + (width - cDim.width)/2, y);
                y += vVGap + cDim.height;
            }
        }
    }

    /**
     * Returns the String representation of this layout's values.
     */
    public String toString() {
        String str = "";
        switch (orientation) {
        case HORIZONTAL:
            str = "orientation=horizontal, ";
            break;
        case VERTICAL:
            str = "orientation=vertical, ";
            break;
        }

        return getClass().getName() + "[" + str + super.toString() + "]";
    }
}
