/Users/richardallenbair/Documents/Source/Projects/nonsense/swingx/src/beaninfo/MultiSplitLayout_API.java
/*
 * $Id: MultiSplitLayout_API.html 1352 2006-08-22 22:52:00Z rbair $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package org.jdesktop.swingx;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import javax.swing.UIManager;


/**
 * The MultiSplitLayout layout manager recursively arranges its
 * components in row and column groups called "Splits".  Elements of
 * the layout are separated by gaps called "Dividers".  The overall
 * layout is defined with a simple tree model whose nodes are 
 * instances of MultiSplitLayout.Split, MultiSplitLayout.Divider, 
 * and MultiSplitLayout.Leaf. Named Leaf nodes represent the space 
 * allocated to a component that was added with a constraint that
 * matches the Leaf's name.  Extra space is distributed
 * among row/column siblings according to their 0.0 to 1.0 weight.
 * If no weights are specified then the last sibling always gets
 * all of the extra space, or space reduction.
 * 
 * <p>
 * Although MultiSplitLayout can be used with any Container, it's
 * the default layout manager for JXMultiSplitPane.  JXMultiSplitPane
 * supports interactively dragging the Dividers, accessibility, 
 * and other features associated with split panes.
 * 
 * <p>
 * All properties in this class are bound: when a properties value
 * is changed, all PropertyChangeListeners are fired.
 * 
 * @author Hans Muller
 * @see JXMultiSplitPane
 */

public class MultiSplitLayout extends JavaBean implements LayoutManager {
    /**
     * Create a MultiSplitLayout with a default model with a single
     * Leaf node named "default".  
     * 
     * #see setModel
     */
    public MultiSplitLayout();
    
    /**
     * Create a MultiSplitLayout with the specified model.
     * 
     * #see setModel
     */
    public MultiSplitLayout(Node model);

    /**
     * Return the root of the tree of Split, Leaf, and Divider nodes
     * that define this layout.  
     * 
     * @return the value of the model property
     * @see #setModel
     */
    public Node getModel();

    /**
     * Set the root of the tree of Split, Leaf, and Divider nodes
     * that define this layout.  The model can be a Split node
     * (the typical case) or a Leaf.  The default value of this
     * property is a Leaf named "default".
     * 
     * @param model the root of the tree of Split, Leaf, and Divider node
     * @throws IllegalArgumentException if model is a Divider or null
     * @see #getModel
     */
    public void setModel(Node model);

    /**
     * Returns the width of Dividers in Split rows, and the height of 
     * Dividers in Split columns.
     *
     * @return the value of the dividerSize property
     * @see #setDividerSize
     */
    public int getDividerSize();

    /**
     * Sets the width of Dividers in Split rows, and the height of 
     * Dividers in Split columns.  The default value of this property
     * is the same as for JSplitPane Dividers.
     *
     * @param dividerSize the size of dividers (pixels)
     * @throws IllegalArgumentException if dividerSize < 0
     * @see #getDividerSize
     */
    public void setDividerSize(int dividerSize);

    /**
     * @return the value of the floatingDividers property
     * @see #setFloatingDividers
     */
    public boolean getFloatingDividers();


    /**
     * If true, Leaf node bounds match the corresponding component's 
     * preferred size and Splits/Dividers are resized accordingly.  
     * If false then the Dividers define the bounds of the adjacent
     * Split and Leaf nodes.  Typically this property is set to false
     * after the (MultiSplitPane) user has dragged a Divider.
     * 
     * @see #getFloatingDividers
     */
    public void setFloatingDividers(boolean floatingDividers);


    /** 
     * Add a component to this MultiSplitLayout.  The
     * <code>name</code> should match the name property of the Leaf
     * node that represents the bounds of <code>child</code>.  After
     * layoutContainer() recomputes the bounds of all of the nodes in
     * the model, it will set this child's bounds to the bounds of the
     * Leaf node with <code>name</code>.  Note: if a component was already
     * added with the same name, this method does not remove it from 
     * its parent.  
     * 
     * @param name identifies the Leaf node that defines the child's bounds
     * @param child the component to be added
     * @see #removeLayoutComponent
     */
    public void addLayoutComponent(String name, Component child);

    /**
     * Removes the specified component from the layout.
     * 
     * @param child the component to be removed
     * @see #addLayoutComponent
     */
    public void removeLayoutComponent(Component child);

    public Dimension preferredLayoutSize(Container parent);

    public Dimension minimumLayoutSize(Container parent);

    /** 
     * The specified Node is either the wrong type or was configured
     * incorrectly.
     */
    public static class InvalidLayoutException extends RuntimeException {
        public InvalidLayoutException (String msg, Node node);
        /** 
         * @return the invalid Node.
         */
        public Node getNode();
    }

    /** 
     * Compute the bounds of all of the Split/Divider/Leaf Nodes in 
     * the layout model, and then set the bounds of each child component
     * with a matching Leaf Node.
     */
    public void layoutContainer(Container parent);

    /** 
     * Return the Divider whose bounds contain the specified
     * point, or null if there isn't one.
     * 
     * @param x x coordinate
     * @param y y coordinate
     * @return the Divider at x,y
     */
    public Divider dividerAt(int x, int y);

    /**
     * Return the Dividers whose bounds overlap the specified
     * Rectangle.
     * 
     * @param r target Rectangle
     * @return the Dividers that overlap r
     * @throws IllegalArgumentException if the Rectangle is null
     */
    public List<Divider> dividersThatOverlap(Rectangle r);

    /** 
     * Base class for the nodes that model a MultiSplitLayout.
     */
    public static abstract class Node {
        /** 
         * Returns the Split parent of this Node, or null.
         *
         * @return the value of the parent property.
         * @see #setParent
         */
        public Split getParent();

        /**
         * Set the value of this Node's parent property.  The default
         * value of this property is null.
         * 
         * @param parent a Split or null
         * @see #getParent
         */
        public void setParent(Split parent);
        
        /**
         * Returns the bounding Rectangle for this Node.
         * 
         * @return the value of the bounds property.
         * @see #setBounds
         */
        public Rectangle getBounds();

        /**
         * Set the bounding Rectangle for this node.  The value of 
         * bounds may not be null.  The default value of bounds
         * is equal to <code>new Rectangle(0,0,0,0)</code>.
         * 
         * @param bounds the new value of the bounds property
         * @throws IllegalArgumentException if bounds is null
         * @see #getBounds
         */
        public void setBounds(Rectangle bounds);

        /** 
         * Value between 0.0 and 1.0 used to compute how much space
         * to add to this sibling when the layout grows or how
         * much to reduce when the layout shrinks.
         * 
         * @return the value of the weight property
         * @see #setWeight
         */
        public double getWeight();

        /** 
         * The weight property is a between 0.0 and 1.0 used to
         * compute how much space to add to this sibling when the
         * layout grows or how much to reduce when the layout shrinks.
         * If rowLayout is true then this node's width grows
         * or shrinks by (extraSpace * weight).  If rowLayout is false,
         * then the node's height is changed.  The default value
         * of weight is 0.0.
         * 
         * @param weight a double between 0.0 and 1.0
         * @see #getWeight
         * @see MultiSplitLayout#layoutContainer
         * @throws IllegalArgumentException if weight is not between 0.0 and 1.0
         */
        public void setWeight(double weight);

        /** 
         * Return the Node that comes after this one in the parent's
         * list of children, or null.  If this node's parent is null,
         * or if it's the last child, then return null.
         * 
         * @return the Node that comes after this one in the parent's list of children.
         * @see #previousSibling
         * @see #getParent
         */
        public Node nextSibling();

        /** 
         * Return the Node that comes before this one in the parent's
         * list of children, or null.  If this node's parent is null,
         * or if it's the last child, then return null.
         * 
         * @return the Node that comes before this one in the parent's list of children.
         * @see #nextSibling
         * @see #getParent
         */
        public Node previousSibling();
    }

    /** 
     * Defines a vertical or horizontal subdivision into two or more
     * tiles.
     */
    public static class Split extends Node {
        /**
         * Returns true if the this Split's children are to be 
         * laid out in a row: all the same height, left edge
         * equal to the previous Node's right edge.  If false,
         * children are laid on in a column.
         * 
         * @return the value of the rowLayout property.
         * @see #setRowLayout
         */
        public boolean isRowLayout();

        /**
         * Set the rowLayout property.  If true, all of this Split's
         * children are to be laid out in a row: all the same height,
         * each node's left edge equal to the previous Node's right
         * edge.  If false, children are laid on in a column.  Default
         * value is true.
         * 
         * @param rowLayout true for horizontal row layout, false for column
         * @see #isRowLayout
         */
        public void setRowLayout(boolean rowLayout);

        /** 
         * Returns this Split node's children.  The returned value
         * is not a reference to the Split's internal list of children
         * 
         * @return the value of the children property.
         * @see #setChildren
         */
        public List<Node> getChildren();

        /**
         * Set's the children property of this Split node.  The parent
         * of each new child is set to this Split node, and the parent
         * of each old child (if any) is set to null.  This method
         * defensively copies the incoming List.  Default value is
         * an empty List.
         * 
         * @param children List of children
         * @see #getChildren
         * @throws IllegalArgumentException if children is null
         */
        public void setChildren(List<Node> children);

        /**
         * Convenience method that returns the last child whose weight
         * is > 0.0.
         * 
         * @return the last child whose weight is > 0.0.
         * @see #getChildren
         * @see Node#getWeight
         */
        public final Node lastWeightedChild();
    }


    /**
     * Models a java.awt Component child.
     */
    public static class Leaf extends Node {
        /**
         * Create a Leaf node.  The default value of name is "". 
         */
        public Leaf();

        /**
         * Create a Leaf node with the specified name.  Name can not
         * be null.
         * 
         * @param name value of the Leaf's name property
         * @throws IllegalArgumentException if name is null
         */
        public Leaf(String name);

        /**
         * Return the Leaf's name.
         * 
         * @return the value of the name property.
         * @see #setName
         */
        public String getName();

        /**
         * Set the value of the name property.  Name may not be null.
         * 
         * @param name value of the name property
         * @throws IllegalArgumentException if name is null
         */
        public void setName(String name);
    }


    /** 
     * Models a single vertical/horiztonal divider.
     */
    public static class Divider extends Node {
        /**
         * Convenience method, returns true if the Divider's parent
         * is a Split row (a Split with isRowLayout() true), false
         * otherwise. In other words if this Divider's major axis
         * is vertical, return true.
         * 
         * @return true if this Divider is part of a Split row.
         */
        public final boolean isVertical();

        /** 
         * Dividers can't have a weight, they don't grow or shrink.
         * @throws UnsupportedOperationException
         */
        public void setWeight(double weight);
    }

    /**
     * A convenience method that converts a string to a 
     * MultiSplitLayout model (a tree of Nodes) using a 
     * a simple syntax.  Nodes are represented by 
     * parenthetical expressions whose first token 
     * is one of ROW/COLUMN/LEAF.  ROW and COLUMN specify
     * horizontal and vertical Split nodes respectively, 
     * LEAF specifies a Leaf node.  A Leaf's name and 
     * weight can be specified with attributes, 
     * name=<i>myLeafName</i> weight=<i>myLeafWeight</i>.
     * Similarly, a Split's weight can be specified with
     * weight=<i>mySplitWeight</i>.
     * 
     * <p> For example, the following expression generates
     * a horizontal Split node with three children:
     * the Leafs named left and right, and a Divider in 
     * between:
     * <pre>
     * (ROW (LEAF name=left) (LEAF name=right weight=1.0))
     * </pre>
     * 
     * <p> Dividers should not be included in the string, 
     * they're added automatcially as needed.  Because 
     * Leaf nodes often only need to specify a name, one
     * can specify a Leaf by just providing the name.
     * The previous example can be written like this:
     * <pre>
     * (ROW left (LEAF name=right weight=1.0))
     * </pre>
     * 
     * <p>Here's a more complex example.  One row with
     * three elements, the first and last of which are columns
     * with two leaves each:
     * <pre>
     * (ROW (COLUMN weight=0.5 left.top left.bottom) 
     *      (LEAF name=middle)
     *      (COLUMN weight=0.5 right.top right.bottom))
     * </pre>
     * 
     * 
     * <p> This syntax is not intended for archiving or 
     * configuration files. It's just a convenience for
     * examples and tests.
     * 
     * @return the Node root of a tree based on s.
     */
    public static Node parseModel(String s);

    /** 
     * Print the tree with enough detail for simple debugging.
     */
    public static void printModel(Node root);
}