Index: src/org/jdesktop/swingx/MultiSplitLayout.java
===================================================================
--- src/org/jdesktop/swingx/MultiSplitLayout.java	(revision 0)
+++ src/org/jdesktop/swingx/MultiSplitLayout.java	(revision 0)
@@ -0,0 +1,1351 @@
+/*
+ * $Id: MultiSplitLayout.java,v 1.15 2005/10/26 14:29:54 hansmuller Exp $
+ *
+ * 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 MultiSplitPane.  MultiSplitPane
+ * 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 MultiSplitPane
+ */
+
+public class MultiSplitLayout implements LayoutManager {
+    private final Map<String, Component> childMap = new HashMap<String, Component>();
+    private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
+    private Node model;
+    private int dividerSize;
+    private boolean floatingDividers = true;
+
+    /**
+     * Create a MultiSplitLayout with a default model with a single
+     * Leaf node named "default".  
+     * 
+     * #see setModel
+     */
+    public MultiSplitLayout() { 
+	this(new Leaf("default"));
+    }
+    
+    /**
+     * Create a MultiSplitLayout with the specified model.
+     * 
+     * #see setModel
+     */
+    public MultiSplitLayout(Node model) {
+	this.model = model;
+        this.dividerSize = UIManager.getInt("SplitPane.dividerSize"); 
+	if (this.dividerSize == 0) {
+            this.dividerSize = 7;
+        }
+    }
+
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        if (listener != null) {
+	    pcs.addPropertyChangeListener(listener);
+        }
+    }
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        if (listener != null) {
+	    pcs.removePropertyChangeListener(listener);
+        }
+    }
+    public PropertyChangeListener[] getPropertyChangeListeners() {
+        return pcs.getPropertyChangeListeners();
+    }
+
+    private void firePCS(String propertyName, Object oldValue, Object newValue) {
+	if (!(oldValue != null && newValue != null && oldValue.equals(newValue))) {
+	    pcs.firePropertyChange(propertyName, oldValue, newValue);
+        }
+    }
+
+    /**
+     * 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() { return model; }
+
+    /**
+     * 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) {
+	if ((model == null) || (model instanceof Divider)) {
+	    throw new IllegalArgumentException("invalid model");
+	}
+	Node oldModel = model;
+	this.model = model;
+	firePCS("model", oldModel, 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() { return dividerSize; }
+
+    /**
+     * 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) {
+	if (dividerSize < 0) {
+	    throw new IllegalArgumentException("invalid dividerSize");
+	}
+	int oldDividerSize = this.dividerSize;
+	this.dividerSize = dividerSize;
+	firePCS("dividerSize", oldDividerSize, dividerSize);
+    }
+
+    /**
+     * @return the value of the floatingDividers property
+     * @see #setFloatingDividers
+     */
+    public boolean getFloatingDividers() { return floatingDividers; }
+
+
+    /**
+     * 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) {
+	boolean oldFloatingDividers = this.floatingDividers;
+	this.floatingDividers = floatingDividers;
+	firePCS("floatingDividers", oldFloatingDividers, 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) {
+	if (name == null) {
+	    throw new IllegalArgumentException("name not specified");
+	}
+	childMap.put(name, child);
+    }
+
+    /**
+     * Removes the specified component from the layout.
+     * 
+     * @param child the component to be removed
+     * @see #addLayoutComponent
+     */
+    public void removeLayoutComponent(Component child) {
+	String name = child.getName();
+	if (name != null) {
+	    childMap.remove(name);
+	}
+    }
+
+    private Component childForNode(Node node) {
+	if (node instanceof Leaf) {
+	    Leaf leaf = (Leaf)node;
+	    String name = leaf.getName();
+	    return (name != null) ? childMap.get(name) : null;
+	}
+	return null;
+    }
+
+
+    private Dimension preferredComponentSize(Node node) {
+	Component child = childForNode(node);
+	return (child != null) ? child.getPreferredSize() : new Dimension(0, 0);
+	
+    }
+
+    private Dimension minimumComponentSize(Node node) {
+	Component child = childForNode(node);
+	return (child != null) ? child.getMinimumSize() : new Dimension(0, 0);
+	
+    }
+
+    private Dimension preferredNodeSize(Node root) {
+	if (root instanceof Leaf) {
+	    return preferredComponentSize(root);
+	}
+	else if (root instanceof Divider) {
+	    int dividerSize = getDividerSize();
+	    return new Dimension(dividerSize, dividerSize);
+	}
+	else {
+	    Split split = (Split)root;
+	    List<Node> splitChildren = split.getChildren();
+	    int width = 0;
+	    int height = 0;
+	    if (split.isRowLayout()) { 
+		for(Node splitChild : splitChildren) {
+		    Dimension size = preferredNodeSize(splitChild);
+		    width += size.width;
+		    height = Math.max(height, size.height);
+		}
+	    }
+	    else {
+		for(Node splitChild : splitChildren) {
+		    Dimension size = preferredNodeSize(splitChild);
+		    width = Math.max(width, size.width);
+		    height += size.height;
+		}
+	    }
+	    return new Dimension(width, height);
+	}
+    }
+
+    private Dimension minimumNodeSize(Node root) {
+	if (root instanceof Leaf) {
+	    Component child = childForNode(root);
+	    return (child != null) ? child.getMinimumSize() : new Dimension(0, 0);
+	}
+	else if (root instanceof Divider) {
+	    int dividerSize = getDividerSize();
+	    return new Dimension(dividerSize, dividerSize);
+	}
+	else {
+	    Split split = (Split)root;
+	    List<Node> splitChildren = split.getChildren();
+	    int width = 0;
+	    int height = 0;
+	    if (split.isRowLayout()) { 
+		for(Node splitChild : splitChildren) {
+		    Dimension size = minimumNodeSize(splitChild);
+		    width += size.width;
+		    height = Math.max(height, size.height);
+		}
+	    }
+	    else {
+		for(Node splitChild : splitChildren) {
+		    Dimension size = minimumNodeSize(splitChild);
+		    width = Math.max(width, size.width);
+		    height += size.height;
+		}
+	    }
+	    return new Dimension(width, height);
+	}
+    }
+
+    private Dimension sizeWithInsets(Container parent, Dimension size) {
+	Insets insets = parent.getInsets();
+	int width = size.width + insets.left + insets.right;
+	int height = size.height + insets.top + insets.bottom;
+	return new Dimension(width, height);
+    }
+
+    public Dimension preferredLayoutSize(Container parent) {
+	Dimension size = preferredNodeSize(getModel());
+	return sizeWithInsets(parent, size);
+    }
+
+    public Dimension minimumLayoutSize(Container parent) {
+	Dimension size = minimumNodeSize(getModel());
+	return sizeWithInsets(parent, size);
+    }
+
+
+    private Rectangle boundsWithYandHeight(Rectangle bounds, double y, double height) {
+	Rectangle r = new Rectangle();
+	r.setBounds((int)(bounds.getX()), (int)y, (int)(bounds.getWidth()), (int)height);
+	return r;
+    }
+
+    private Rectangle boundsWithXandWidth(Rectangle bounds, double x, double width) {
+	Rectangle r = new Rectangle();
+	r.setBounds((int)x, (int)(bounds.getY()), (int)width, (int)(bounds.getHeight()));
+	return r;
+    }
+
+
+    private void minimizeSplitBounds(Split split, Rectangle bounds) {
+	Rectangle splitBounds = new Rectangle(bounds.x, bounds.y, 0, 0);
+	List<Node> splitChildren = split.getChildren();
+	Node lastChild = splitChildren.get(splitChildren.size() - 1);
+	Rectangle lastChildBounds = lastChild.getBounds();
+	if (split.isRowLayout()) {
+	    int lastChildMaxX = lastChildBounds.x + lastChildBounds.width;
+	    splitBounds.add(lastChildMaxX, bounds.y + bounds.height);
+	}
+	else {
+	    int lastChildMaxY = lastChildBounds.y + lastChildBounds.height;
+	    splitBounds.add(bounds.x + bounds.width, lastChildMaxY);
+	}
+	split.setBounds(splitBounds);
+    }
+
+
+    private void layoutShrink(Split split, Rectangle bounds) {
+	Rectangle splitBounds = split.getBounds();
+	ListIterator<Node> splitChildren = split.getChildren().listIterator();
+	Node lastWeightedChild = split.lastWeightedChild();
+
+	if (split.isRowLayout()) {
+	    int totalWidth = 0;          // sum of the children's widths
+	    int minWeightedWidth = 0;    // sum of the weighted childrens' min widths
+	    int totalWeightedWidth = 0;  // sum of the weighted childrens' widths
+	    for(Node splitChild : split.getChildren()) {
+		int nodeWidth = splitChild.getBounds().width;
+		int nodeMinWidth = Math.min(nodeWidth, minimumNodeSize(splitChild).width);
+		totalWidth += nodeWidth;
+		if (splitChild.getWeight() > 0.0) {
+		    minWeightedWidth += nodeMinWidth;
+		    totalWeightedWidth += nodeWidth;
+		}
+	    }
+
+	    double x = bounds.getX();
+	    double extraWidth = splitBounds.getWidth() - bounds.getWidth();
+	    double availableWidth = extraWidth;
+	    boolean onlyShrinkWeightedComponents = 
+		(totalWeightedWidth - minWeightedWidth) > extraWidth;
+
+	    while(splitChildren.hasNext()) {
+		Node splitChild = splitChildren.next();
+		Rectangle splitChildBounds = splitChild.getBounds();
+		double minSplitChildWidth = minimumNodeSize(splitChild).getWidth();
+		double splitChildWeight = (onlyShrinkWeightedComponents)
+		    ? splitChild.getWeight()
+		    : (splitChildBounds.getWidth() / (double)totalWidth);
+
+		if (!splitChildren.hasNext()) {
+		    double newWidth =  Math.max(minSplitChildWidth, bounds.getMaxX() - x); 
+		    Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth);
+		    layout2(splitChild, newSplitChildBounds);
+		}
+		else if ((availableWidth > 0.0) && (splitChildWeight > 0.0)) {
+		    double allocatedWidth = Math.rint(splitChildWeight * extraWidth);
+		    double oldWidth = splitChildBounds.getWidth();
+		    double newWidth = Math.max(minSplitChildWidth, oldWidth - allocatedWidth);
+		    Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth);
+		    layout2(splitChild, newSplitChildBounds);
+		    availableWidth -= (oldWidth - splitChild.getBounds().getWidth());
+		}
+		else {
+		    double existingWidth = splitChildBounds.getWidth();
+		    Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, existingWidth);
+		    layout2(splitChild, newSplitChildBounds);
+		}
+		x = splitChild.getBounds().getMaxX();
+	    }
+	}
+
+	else {
+	    int totalHeight = 0;          // sum of the children's heights
+	    int minWeightedHeight = 0;    // sum of the weighted childrens' min heights
+	    int totalWeightedHeight = 0;  // sum of the weighted childrens' heights
+	    for(Node splitChild : split.getChildren()) {
+		int nodeHeight = splitChild.getBounds().height;
+		int nodeMinHeight = Math.min(nodeHeight, minimumNodeSize(splitChild).height);
+		totalHeight += nodeHeight;
+		if (splitChild.getWeight() > 0.0) {
+		    minWeightedHeight += nodeMinHeight;
+		    totalWeightedHeight += nodeHeight;
+		}
+	    }
+
+	    double y = bounds.getY();
+	    double extraHeight = splitBounds.getHeight() - bounds.getHeight();
+	    double availableHeight = extraHeight;
+	    boolean onlyShrinkWeightedComponents = 
+		(totalWeightedHeight - minWeightedHeight) > extraHeight;
+
+	    while(splitChildren.hasNext()) {
+		Node splitChild = splitChildren.next();
+		Rectangle splitChildBounds = splitChild.getBounds();
+		double minSplitChildHeight = minimumNodeSize(splitChild).getHeight();
+		double splitChildWeight = (onlyShrinkWeightedComponents)
+		    ? splitChild.getWeight()
+		    : (splitChildBounds.getHeight() / (double)totalHeight);
+
+		if (!splitChildren.hasNext()) {
+		    double oldHeight = splitChildBounds.getHeight();
+		    double newHeight =  Math.max(minSplitChildHeight, bounds.getMaxY() - y); 
+		    Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight);
+		    layout2(splitChild, newSplitChildBounds);
+		    availableHeight -= (oldHeight - splitChild.getBounds().getHeight());
+		}
+		else if ((availableHeight > 0.0) && (splitChildWeight > 0.0)) {
+		    double allocatedHeight = Math.rint(splitChildWeight * extraHeight);
+		    double oldHeight = splitChildBounds.getHeight();
+		    double newHeight = Math.max(minSplitChildHeight, oldHeight - allocatedHeight);
+		    Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight);
+		    layout2(splitChild, newSplitChildBounds);
+		    availableHeight -= (oldHeight - splitChild.getBounds().getHeight());
+		}
+		else {
+		    double existingHeight = splitChildBounds.getHeight();
+		    Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, existingHeight);
+		    layout2(splitChild, newSplitChildBounds);
+		}
+		y = splitChild.getBounds().getMaxY();
+	    }
+	}
+
+	/* The bounds of the Split node root are set to be 
+	 * big enough to contain all of its children. Since 
+	 * Leaf children can't be reduced below their 
+	 * (corresponding java.awt.Component) minimum sizes, 
+	 * the size of the Split's bounds maybe be larger than
+	 * the bounds we were asked to fit within.
+	 */
+	minimizeSplitBounds(split, bounds);
+    }
+
+
+    private void layoutGrow(Split split, Rectangle bounds) {
+	Rectangle splitBounds = split.getBounds();
+	ListIterator<Node> splitChildren = split.getChildren().listIterator();
+	Node lastWeightedChild = split.lastWeightedChild();
+
+	/* Layout the Split's child Nodes' along the X axis.  The bounds 
+	 * of each child will have the same y coordinate and height as the 
+	 * layoutGrow() bounds argument.  Extra width is allocated to the 
+	 * to each child with a non-zero weight:
+	 *     newWidth = currentWidth + (extraWidth * splitChild.getWeight())
+	 * Any extraWidth "left over" (that's availableWidth in the loop
+	 * below) is given to the last child.  Note that Dividers always
+	 * have a weight of zero, and they're never the last child.
+	 */
+	if (split.isRowLayout()) {
+	    double x = bounds.getX();
+	    double extraWidth = bounds.getWidth() - splitBounds.getWidth();
+	    double availableWidth = extraWidth;
+
+	    while(splitChildren.hasNext()) {
+		Node splitChild = splitChildren.next();
+		Rectangle splitChildBounds = splitChild.getBounds();
+		double splitChildWeight = splitChild.getWeight();
+
+		if (!splitChildren.hasNext()) {  
+		    double newWidth = bounds.getMaxX() - x; 
+		    Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth);
+		    layout2(splitChild, newSplitChildBounds);
+		}
+		else if ((availableWidth > 0.0) && (splitChildWeight > 0.0)) {
+		    double allocatedWidth = (splitChild.equals(lastWeightedChild)) 
+			? availableWidth
+			: Math.rint(splitChildWeight * extraWidth);
+		    double newWidth = splitChildBounds.getWidth() + allocatedWidth;
+		    Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth);
+		    layout2(splitChild, newSplitChildBounds);
+		    availableWidth -= allocatedWidth;
+		}
+		else {
+		    double existingWidth = splitChildBounds.getWidth();
+		    Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, existingWidth);
+		    layout2(splitChild, newSplitChildBounds);
+		}
+		x = splitChild.getBounds().getMaxX();
+	    }
+	}
+
+	/* Layout the Split's child Nodes' along the Y axis.  The bounds 
+	 * of each child will have the same x coordinate and width as the 
+	 * layoutGrow() bounds argument.  Extra height is allocated to the 
+	 * to each child with a non-zero weight:
+	 *     newHeight = currentHeight + (extraHeight * splitChild.getWeight())
+	 * Any extraHeight "left over" (that's availableHeight in the loop
+	 * below) is given to the last child.  Note that Dividers always
+	 * have a weight of zero, and they're never the last child.
+	 */
+	else {
+	    double y = bounds.getY();
+	    double extraHeight = bounds.getMaxY() - splitBounds.getHeight();
+	    double availableHeight = extraHeight;
+
+	    while(splitChildren.hasNext()) {
+		Node splitChild = splitChildren.next();
+		Rectangle splitChildBounds = splitChild.getBounds();
+		double splitChildWeight = splitChild.getWeight();
+		
+		if (!splitChildren.hasNext()) {
+		    double newHeight = bounds.getMaxY() - y; 
+		    Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight);
+		    layout2(splitChild, newSplitChildBounds);
+		}
+		else if ((availableHeight > 0.0) && (splitChildWeight > 0.0)) {
+		    double allocatedHeight = (splitChild.equals(lastWeightedChild)) 
+			? availableHeight
+			: Math.rint(splitChildWeight * extraHeight);
+		    double newHeight = splitChildBounds.getHeight() + allocatedHeight;
+		    Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight);
+		    layout2(splitChild, newSplitChildBounds);
+		    availableHeight -= allocatedHeight;
+		}
+		else {
+		    double existingHeight = splitChildBounds.getHeight();
+		    Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, existingHeight);
+		    layout2(splitChild, newSplitChildBounds);
+		}
+		y = splitChild.getBounds().getMaxY();
+	    }
+	}
+    }
+
+
+    /* Second pass of the layout algorithm: branch to layoutGrow/Shrink
+     * as needed.
+     */
+   private void layout2(Node root, Rectangle bounds) {
+	if (root instanceof Leaf) {
+	    Component child = childForNode(root);
+	    if (child != null) {
+		child.setBounds(bounds);
+	    }
+	    root.setBounds(bounds);
+	}
+	else if (root instanceof Divider) {
+	    root.setBounds(bounds);
+	}
+	else if (root instanceof Split) {
+	    Split split = (Split)root;
+	    boolean grow = split.isRowLayout() 
+		? (split.getBounds().width <= bounds.width)
+		: (split.getBounds().height <= bounds.height);
+	    if (grow) {
+		layoutGrow(split, bounds);
+		root.setBounds(bounds);
+	    }
+	    else {
+		layoutShrink(split, bounds);
+                // split.setBounds() called in layoutShrink()
+	    }
+	}
+    }
+
+
+    /* First pass of the layout algorithm.
+     * 
+     * If the Dividers are "floating" then set the bounds of each
+     * node to accomodate the preferred size of all of the 
+     * Leaf's java.awt.Components.  Otherwise, just set the bounds
+     * of each Leaf/Split node so that it's to the left of (for
+     * Split.isRowLayout() Split children) or directly above
+     * the Divider that follows.
+     * 
+     * This pass sets the bounds of each Node in the layout model.  It
+     * does not resize any of the parent Container's
+     * (java.awt.Component) children.  That's done in the second pass,
+     * see layoutGrow() and layoutShrink().
+     */
+    private void layout1(Node root, Rectangle bounds) {
+	if (root instanceof Leaf) {
+	    root.setBounds(bounds);
+	}
+	else if (root instanceof Split) {
+	    Split split = (Split)root;
+	    Iterator<Node> splitChildren = split.getChildren().iterator();
+	    Rectangle childBounds = null;
+	    int dividerSize = getDividerSize();
+	    
+	    /* Layout the Split's child Nodes' along the X axis.  The bounds 
+	     * of each child will have the same y coordinate and height as the 
+	     * layout1() bounds argument.  
+	     * 
+	     * Note: the column layout code - that's the "else" clause below
+	     * this if, is identical to the X axis (rowLayout) code below.
+	     */
+	    if (split.isRowLayout()) {
+		double x = bounds.getX();
+		while(splitChildren.hasNext()) {
+		    Node splitChild = splitChildren.next();
+		    Divider dividerChild = 
+			(splitChildren.hasNext()) ? (Divider)(splitChildren.next()) : null;
+
+		    double childWidth = 0.0;
+		    if (getFloatingDividers()) {
+			childWidth = preferredNodeSize(splitChild).getWidth();
+		    }
+		    else {
+			if (dividerChild != null) {
+			    childWidth = dividerChild.getBounds().getX() - x;
+			}
+			else {
+			    childWidth = split.getBounds().getMaxX() - x;
+			}
+		    }
+		    childBounds = boundsWithXandWidth(bounds, x, childWidth);
+		    layout1(splitChild, childBounds);
+
+		    if (getFloatingDividers() && (dividerChild != null)) {
+			double dividerX = childBounds.getMaxX();
+			Rectangle dividerBounds = boundsWithXandWidth(bounds, dividerX, dividerSize);
+			dividerChild.setBounds(dividerBounds);
+		    }
+		    if (dividerChild != null) {
+			x = dividerChild.getBounds().getMaxX();
+		    }
+		}
+	    }
+
+	    /* Layout the Split's child Nodes' along the Y axis.  The bounds 
+	     * of each child will have the same x coordinate and width as the 
+	     * layout1() bounds argument.  The algorithm is identical to what's
+	     * explained above, for the X axis case.
+	     */
+	    else {
+		double y = bounds.getY();
+		while(splitChildren.hasNext()) {
+		    Node splitChild = splitChildren.next();
+		    Divider dividerChild = 
+			(splitChildren.hasNext()) ? (Divider)(splitChildren.next()) : null;
+
+		    double childHeight = 0.0;
+		    if (getFloatingDividers()) {
+			childHeight = preferredNodeSize(splitChild).getHeight();
+		    }
+		    else {
+			if (dividerChild != null) {
+			    childHeight = dividerChild.getBounds().getY() - y;
+			}
+			else {
+			    childHeight = split.getBounds().getMaxY() - y;
+			}
+		    }
+		    childBounds = boundsWithYandHeight(bounds, y, childHeight);
+		    layout1(splitChild, childBounds);
+
+		    if (getFloatingDividers() && (dividerChild != null)) {
+			double dividerY = childBounds.getMaxY();
+			Rectangle dividerBounds = boundsWithYandHeight(bounds, dividerY, dividerSize);
+			dividerChild.setBounds(dividerBounds);
+		    }
+		    if (dividerChild != null) {
+			y = dividerChild.getBounds().getMaxY();
+		    }
+		}
+	    }
+	    /* The bounds of the Split node root are set to be just
+	     * big enough to contain all of its children, but only
+	     * along the axis it's allocating space on.  That's 
+	     * X for rows, Y for columns.  The second pass of the 
+	     * layout algorithm - see layoutShrink()/layoutGrow() 
+	     * allocates extra space.
+	     */
+	    minimizeSplitBounds(split, bounds);
+	}
+    }
+
+    /** 
+     * The specified Node is either the wrong type or was configured
+     * incorrectly.
+     */
+    public static class InvalidLayoutException extends RuntimeException {
+	private final Node node;
+	public InvalidLayoutException (String msg, Node node) {
+	    super(msg);
+	    this.node = node;
+	}
+	/** 
+	 * @return the invalid Node.
+	 */
+	public Node getNode() { return node; }
+    }
+
+    private void throwInvalidLayout(String msg, Node node) {
+	throw new InvalidLayoutException(msg, node);
+    }
+
+    private void checkLayout(Node root) {
+	if (root instanceof Split) {
+	    Split split = (Split)root;
+	    if (split.getChildren().size() <= 2) {
+		throwInvalidLayout("Split must have > 2 children", root);
+	    }
+	    Iterator<Node> splitChildren = split.getChildren().iterator();
+	    double weight = 0.0;
+	    while(splitChildren.hasNext()) {
+		Node splitChild = splitChildren.next();
+		if (splitChild instanceof Divider) {
+		    throwInvalidLayout("expected a Split or Leaf Node", splitChild);
+		}
+		if (splitChildren.hasNext()) {
+		    Node dividerChild = splitChildren.next();
+		    if (!(dividerChild instanceof Divider)) {
+			throwInvalidLayout("expected a Divider Node", dividerChild);
+		    }
+		}
+		weight += splitChild.getWeight();
+		checkLayout(splitChild);
+	    }
+	    if (weight > 1.0) {
+		throwInvalidLayout("Split children's total weight > 1.0", root);
+	    }
+	}
+    }
+
+    /** 
+     * 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) {
+	checkLayout(getModel());
+	Insets insets = parent.getInsets();
+	Dimension size = parent.getSize();
+	int width = size.width - (insets.left + insets.right);
+	int height = size.height - (insets.top + insets.bottom);
+	Rectangle bounds = new Rectangle(insets.left, insets.top, width, height);
+	layout1(getModel(), bounds); 
+	layout2(getModel(), bounds); 
+    }
+
+
+    private Divider dividerAt(Node root, int x, int y) {
+	if (root instanceof Divider) {
+            Divider divider = (Divider)root;
+	    return (divider.getBounds().contains(x, y)) ? divider : null;
+	}
+	else if (root instanceof Split) {
+	    Split split = (Split)root;
+	    for(Node child : split.getChildren()) {
+		if (child.getBounds().contains(x, y)) {
+		    return dividerAt(child, x, y);
+		}
+	    }
+	}
+	return null;
+    }
+
+    /** 
+     * 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 dividerAt(getModel(), x, y);
+    }
+
+    private boolean nodeOverlapsRectangle(Node node, Rectangle r2) {
+	Rectangle r1 = node.getBounds();
+	return 
+	    (r1.x <= (r2.x + r2.width)) && ((r1.x + r1.width) >= r2.x) &&
+	    (r1.y <= (r2.y + r2.height)) && ((r1.y + r1.height) >= r2.y);
+    }
+
+    private List<Divider> dividersThatOverlap(Node root, Rectangle r) {
+	if (nodeOverlapsRectangle(root, r) && (root instanceof Split)) {
+	    List<Divider> dividers = new ArrayList<Divider>();
+	    for(Node child : ((Split)root).getChildren()) {
+		if (child instanceof Divider) {
+		    if (nodeOverlapsRectangle(child, r)) {
+			dividers.add((Divider)child);
+		    }
+		}
+		else if (child instanceof Split) {
+		    dividers.addAll(dividersThatOverlap(child, r));
+		}
+	    }
+            return dividers;
+	}
+	else {
+	    return Collections.emptyList();
+	}
+    }
+
+    /**
+     * 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) {
+	if (r == null) {
+	    throw new IllegalArgumentException("null Rectangle");
+	}
+	return dividersThatOverlap(getModel(), r);
+    }
+
+
+    /** 
+     * Base class for the nodes that model a MultiSplitLayout.
+     */
+    public static abstract class Node {
+	private Split parent = null;  
+	private Rectangle bounds = new Rectangle();
+	private double weight = 0.0;
+
+	/** 
+	 * Returns the Split parent of this Node, or null.
+	 *
+	 * @return the value of the parent property.
+	 * @see #setParent
+	 */
+	public Split getParent() { return parent; }
+
+	/**
+	 * 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) {
+	    this.parent = parent;
+	}
+	
+	/**
+	 * Returns the bounding Rectangle for this Node.
+	 * 
+	 * @return the value of the bounds property.
+	 * @see #setBounds
+	 */
+	public Rectangle getBounds() { 
+	    return new Rectangle(this.bounds);
+	}
+
+	/**
+	 * 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) {
+	    if (bounds == null) {
+		throw new IllegalArgumentException("null bounds");
+	    }
+	    this.bounds = new 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() { return weight; }
+
+	/** 
+	 * 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) {
+	    if ((weight < 0.0)|| (weight > 1.0)) {
+		throw new IllegalArgumentException("invalid weight");
+	    }
+	    this.weight = weight;
+	}
+
+	private Node siblingAtOffset(int offset) {
+	    Split parent = getParent();
+	    if (parent == null) { return null; }
+	    List<Node> siblings = parent.getChildren();
+	    int index = siblings.indexOf(this);
+	    if (index == -1) { return null; }
+	    index += offset;
+	    return ((index > -1) && (index < siblings.size())) ? siblings.get(index) : null;
+	}
+	    
+	/** 
+	 * 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 siblingAtOffset(+1);
+	}
+
+	/** 
+	 * 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() {
+	    return siblingAtOffset(-1);
+	}
+    }
+
+    /** 
+     * Defines a vertical or horizontal subdivision into two or more
+     * tiles.
+     */
+    public static class Split extends Node {
+	private List<Node> children = Collections.emptyList();
+	private boolean rowLayout = true;
+
+	/**
+	 * 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() { return rowLayout; }
+
+	/**
+	 * 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) {
+	    this.rowLayout = 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() { 
+	    return new ArrayList<Node>(children);
+	}
+
+	/**
+	 * 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) {
+	    if (children == null) {
+		throw new IllegalArgumentException("children must be a non-null List");
+	    }
+	    for(Node child : this.children) {
+		child.setParent(null);
+	    }
+	    this.children = new ArrayList<Node>(children);
+	    for(Node child : this.children) {
+		child.setParent(this);
+	    }
+	}
+
+	/**
+	 * 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() {
+	    List<Node> children = getChildren();
+	    Node weightedChild = null;
+	    for(Node child : children) {
+		if (child.getWeight() > 0.0) {
+		    weightedChild = child;
+		}
+	    }
+	    return weightedChild;
+	}
+
+	public String toString() {
+	    int nChildren = getChildren().size();
+	    StringBuffer sb = new StringBuffer("MultiSplitLayout.Split");
+	    sb.append(isRowLayout() ? " ROW [" : " COLUMN [");
+	    sb.append(nChildren + ((nChildren == 1) ? " child" : " children"));
+	    sb.append("] ");
+	    sb.append(getBounds());
+	    return sb.toString();
+	}
+    }
+
+
+    /**
+     * Models a java.awt Component child.
+     */
+    public static class Leaf extends Node {
+	private String name = "";
+
+	/**
+	 * 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) {
+	    if (name == null) {
+		throw new IllegalArgumentException("name is null");
+	    }
+	    this.name = name;
+	}
+
+	/**
+	 * Return the Leaf's name.
+	 * 
+	 * @return the value of the name property.
+	 * @see #setName
+	 */
+	public String getName() { return name; }
+
+	/**
+	 * 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) {
+	    if (name == null) {
+		throw new IllegalArgumentException("name is null");
+	    }
+	    this.name = name;
+	}
+
+	public String toString() {
+	    StringBuffer sb = new StringBuffer("MultiSplitLayout.Leaf");
+	    sb.append(" \"");
+	    sb.append(getName());
+	    sb.append("\""); 
+	    sb.append(" weight=");
+	    sb.append(getWeight());
+	    sb.append(" ");
+	    sb.append(getBounds());
+	    return sb.toString();
+	}
+    }
+
+
+    /** 
+     * 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() {
+	    Split parent = getParent();
+	    return (parent != null) ? parent.isRowLayout() : false;
+	}
+
+	/** 
+	 * Dividers can't have a weight, they don't grow or shrink.
+	 * @throws UnsupportedOperationException
+	 */
+	public void setWeight(double weight) {
+	    throw new UnsupportedOperationException();
+	}
+
+	public String toString() {
+	    return "MultiSplitLayout.Divider " + getBounds().toString();
+	}
+    }
+
+
+    private static void throwParseException(StreamTokenizer st, String msg) throws Exception {
+	throw new Exception("MultiSplitLayout.parseModel Error: " + msg);
+    }
+
+    private static void parseAttribute(String name, StreamTokenizer st, Node node) throws Exception {
+	if ((st.nextToken() != '=')) {
+	    throwParseException(st, "expected '=' after " + name);
+	}
+	if (name.equalsIgnoreCase("WEIGHT")) {
+	    if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
+		node.setWeight(st.nval);
+	    }
+	    else { 
+		throwParseException(st, "invalid weight");
+	    }
+	}
+	else if (name.equalsIgnoreCase("NAME")) {
+	    if (st.nextToken() == StreamTokenizer.TT_WORD) {
+		if (node instanceof Leaf) {
+		    ((Leaf)node).setName(st.sval);
+		}
+		else {
+		    throwParseException(st, "can't specify name for " + node);
+		}
+	    }
+	    else {
+		throwParseException(st, "invalid name");
+	    }
+	}
+	else {
+	    throwParseException(st, "unrecognized attribute \"" + name + "\"");
+	}
+    }
+
+    private static void addSplitChild(Split parent, Node child) {
+	List<Node> children = new ArrayList<Node>(parent.getChildren());
+	if (children.size() == 0) {
+	    children.add(child);
+	}
+	else {
+	    children.add(new Divider());
+	    children.add(child);
+	}
+	parent.setChildren(children);
+    }
+
+    private static void parseLeaf(StreamTokenizer st, Split parent) throws Exception {
+	Leaf leaf = new Leaf();
+	int token;
+	while ((token = st.nextToken()) != StreamTokenizer.TT_EOF) {
+	    if (token == ')') {
+		break;
+	    }
+	    if (token == StreamTokenizer.TT_WORD) {
+		parseAttribute(st.sval, st, leaf);
+	    }
+	    else {
+		throwParseException(st, "Bad Leaf: " + leaf);
+	    }
+	}
+	addSplitChild(parent, leaf);
+    }
+
+    private static void parseSplit(StreamTokenizer st, Split parent) throws Exception {
+	int token;
+	while ((token = st.nextToken()) != StreamTokenizer.TT_EOF) {
+	    if (token == ')') {
+		break;
+	    }
+	    else if (token == StreamTokenizer.TT_WORD) {
+		if (st.sval.equalsIgnoreCase("WEIGHT")) {
+		    parseAttribute(st.sval, st, parent);
+		}
+		else {
+		    addSplitChild(parent, new Leaf(st.sval));
+		}
+	    }
+	    else if (token == '(') {
+		if ((token = st.nextToken()) != StreamTokenizer.TT_WORD) {
+		    throwParseException(st, "invalid node type");
+		}
+		String nodeType = st.sval.toUpperCase();
+		if (nodeType.equals("LEAF")) {
+		    parseLeaf(st, parent);
+		}
+		else if (nodeType.equals("ROW") || nodeType.equals("COLUMN")) {
+		    Split split = new Split();
+		    split.setRowLayout(nodeType.equals("ROW"));
+		    addSplitChild(parent, split);
+		    parseSplit(st, split);
+		}
+		else {
+		    throwParseException(st, "unrecognized node type '" + nodeType + "'");
+		}
+	    }
+	}
+    }
+
+    private static Node parseModel (Reader r) {
+	StreamTokenizer st = new StreamTokenizer(r);
+	try {
+	    Split root = new Split();
+	    parseSplit(st, root);
+	    return root.getChildren().get(0);
+	}
+	catch (Exception e) {
+	    System.err.println(e);
+	}
+	finally {
+	    try { r.close(); } catch (IOException ignore) {}
+	}
+	return null;
+    }
+
+    /**
+     * 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) {
+	return parseModel(new StringReader(s));
+    }
+
+
+    private static void printModel(String indent, Node root) {
+	if (root instanceof Split) {
+	    Split split = (Split)root;
+	    System.out.println(indent + split);
+	    for(Node child : split.getChildren()) {
+		printModel(indent + "  ", child);
+	    }
+	}
+	else {
+	    System.out.println(indent + root);
+	}
+    }
+
+    /** 
+     * Print the tree with enough detail for simple debugging.
+     */
+    public static void printModel(Node root) {
+	printModel("", root);
+    }
+}
Index: src/org/jdesktop/swingx/MultiSplitPane.java
===================================================================
--- src/org/jdesktop/swingx/MultiSplitPane.java	(revision 0)
+++ src/org/jdesktop/swingx/MultiSplitPane.java	(revision 0)
@@ -0,0 +1,396 @@
+/*
+ * $Id: MultiSplitPane.java,v 1.15 2005/10/26 14:29:54 hansmuller Exp $
+ *
+ * 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.Color;
+import java.awt.Cursor;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.JPanel;
+import javax.swing.event.MouseInputAdapter;
+import org.jdesktop.swingx.MultiSplitLayout.Divider;
+import org.jdesktop.swingx.MultiSplitLayout.Node;
+
+/**
+ *
+ * <p>
+ * All properties in this class are bound: when a properties value
+ * is changed, all PropertyChangeListeners are fired.
+ * 
+ * @author Hans Muller
+ */
+public class MultiSplitPane extends JPanel {
+    private AccessibleContext accessibleContext = null;
+    private boolean continuousLayout = true;
+    private DividerPainter dividerPainter = new DefaultDividerPainter();
+
+    /**
+     * Creates a MultiSplitPane with it's LayoutManager set to 
+     * to an empty MultiSplitLayout.
+     */
+    public MultiSplitPane() {
+	super(new MultiSplitLayout());
+	InputHandler inputHandler = new InputHandler();
+	addMouseListener(inputHandler);
+	addMouseMotionListener(inputHandler);
+	addKeyListener(inputHandler);
+	setFocusable(true);
+    }
+
+    /** 
+     * A convenience method that returns the layout manager cast 
+     * to MutliSplitLayout.
+     * 
+     * @return this MultiSplitPane's layout manager
+     * @see java.awt.Container#getLayout
+     * @see #setModel
+     */
+    public final MultiSplitLayout getMultiSplitLayout() {
+	return (MultiSplitLayout)getLayout();
+    }
+
+    /** 
+     * A convenience method that sets the MultiSplitLayout model.
+     * Equivalent to <code>getMultiSplitLayout.setModel(model)</code>
+     * 
+     * @param model the root of the MultiSplitLayout model
+     * @see #getMultiSplitLayout
+     * @see MultiSplitLayout#setModel
+     */
+    public final void setModel(Node model) {
+	getMultiSplitLayout().setModel(model);
+    }
+
+    /** 
+     * A convenience method that sets the MultiSplitLayout dividerSize
+     * property. Equivalent to 
+     * <code>getMultiSplitLayout().setDividerSize(newDividerSize)</code>.
+     * 
+     * @param dividerSize the value of the dividerSize property
+     * @see #getMultiSplitLayout
+     * @see MultiSplitLayout#setDividerSize
+     */
+    public final void setDividerSize(int dividerSize) {
+	getMultiSplitLayout().setDividerSize(dividerSize);
+    }
+
+    /**
+     * Sets the value of the <code>continuousLayout</code> property.
+     * If true, then the layout is revalidated continuously while
+     * a divider is being moved.  The default value of this property
+     * is true.
+     *
+     * @param continuousLayout value of the continuousLayout property
+     * @see #isContinuousLayout
+     */
+    public void setContinuousLayout(boolean continuousLayout) {
+        boolean oldContinuousLayout = continuousLayout;
+        this.continuousLayout = continuousLayout;
+        firePropertyChange("continuousLayout", oldContinuousLayout, continuousLayout);
+    }
+
+    /**
+     * Returns true if dragging a divider only updates
+     * the layout when the drag gesture ends (typically, when the 
+     * mouse button is released).
+     *
+     * @return the value of the <code>continuousLayout</code> property
+     * @see #setContinuousLayout
+     */
+    public boolean isContinuousLayout() {
+        return continuousLayout;
+    }
+
+    /** 
+     * Returns the Divider that's currently being moved, typically
+     * because the user is dragging it, or null.
+     * 
+     * @return the Divider that's being moved or null.
+     */
+    public Divider activeDivider() {
+	return dragDivider;
+    }
+
+    /**
+     * Draws a single Divider.  Typically used to specialize the
+     * way the active Divider is painted.  
+     * 
+     * @see #getDividerPainter
+     * @see #setDividerPainter
+     */
+    public static abstract class DividerPainter {
+	/**
+	 * Paint a single Divider.       
+	 * 
+	 * @param g the Graphics object to paint with
+	 * @param divider the Divider to paint
+	 */
+	public abstract void paint(Graphics g, Divider divider);
+    }
+
+    private class DefaultDividerPainter extends DividerPainter {
+	public void paint(Graphics g, Divider divider) {
+	    if ((divider == activeDivider()) && !isContinuousLayout()) {
+		Graphics2D g2d = (Graphics2D)g;
+		g2d.setColor(Color.black);
+		g2d.fill(divider.getBounds());
+	    }
+	}
+    }
+
+    /** 
+     * The DividerPainter that's used to paint Dividers on this MultiSplitPane.
+     * This property may be null.
+     * 
+     * @return the value of the dividerPainter Property
+     * @see #setDividerPainter
+     */
+    public DividerPainter getDividerPainter() {
+	return dividerPainter;
+    }
+
+    /** 
+     * Sets the DividerPainter that's used to paint Dividers on this 
+     * MultiSplitPane.  The default DividerPainter only draws
+     * the activeDivider (if there is one) and then, only if 
+     * continuousLayout is false.  The value of this property is 
+     * used by the paintChildren method: Dividers are painted after
+     * the MultiSplitPane's children have been rendered so that 
+     * the activeDivider can appear "on top of" the children.
+     * 
+     * @param dividerPainter the value of the dividerPainter property, can be null
+     * @see #paintChildren
+     * @see #activeDivider
+     */
+    public void setDividerPainter(DividerPainter dividerPainter) {
+	this.dividerPainter = dividerPainter;
+    }
+
+    /**
+     * Uses the DividerPainter (if any) to paint each Divider that
+     * overlaps the clip Rectangle.  This is done after the call to
+     * <code>super.paintChildren()</code> so that Dividers can be 
+     * rendered "on top of" the children.
+     * <p>
+     * {@inheritDoc}
+     */
+    protected void paintChildren(Graphics g) {
+	super.paintChildren(g);
+	DividerPainter dp = getDividerPainter();
+	Rectangle clipR = g.getClipBounds();
+	if ((dp != null) && (clipR != null)) {
+            Graphics dpg = g.create();
+            try {
+		MultiSplitLayout msl = getMultiSplitLayout();
+		for(Divider divider : msl.dividersThatOverlap(clipR)) {
+		    dp.paint(dpg, divider);
+		}
+            }
+            finally {
+		dpg.dispose();
+            }
+	}
+    }
+
+    private boolean dragUnderway = false;
+    private MultiSplitLayout.Divider dragDivider = null;
+    private Rectangle initialDividerBounds = null;
+    private boolean oldFloatingDividers = true;
+    private int dragOffsetX = 0;
+    private int dragOffsetY = 0;
+    private int dragMin = -1;
+    private int dragMax = -1;
+    
+    private void startDrag(int mx, int my) {
+	requestFocusInWindow();
+	MultiSplitLayout msl = getMultiSplitLayout();
+	MultiSplitLayout.Divider divider = msl.dividerAt(mx, my);
+	if (divider != null) {
+	    MultiSplitLayout.Node prevNode = divider.previousSibling();
+	    MultiSplitLayout.Node nextNode = divider.nextSibling();
+	    if ((prevNode == null) || (nextNode == null)) {
+		dragUnderway = false;
+	    }
+	    else {
+		initialDividerBounds = divider.getBounds();
+		dragOffsetX = mx - initialDividerBounds.x;
+		dragOffsetY = my - initialDividerBounds.y;
+		dragDivider  = divider;
+		Rectangle prevNodeBounds = prevNode.getBounds();
+		Rectangle nextNodeBounds = nextNode.getBounds();
+		if (dragDivider.isVertical()) {
+		    dragMin = prevNodeBounds.x;
+		    dragMax = nextNodeBounds.x + nextNodeBounds.width;
+		    dragMax -= dragDivider.getBounds().width;
+		}
+		else {
+		    dragMin = prevNodeBounds.y;
+		    dragMax = nextNodeBounds.y + nextNodeBounds.height;
+		    dragMax -= dragDivider.getBounds().height;
+		}
+		oldFloatingDividers = getMultiSplitLayout().getFloatingDividers();
+		getMultiSplitLayout().setFloatingDividers(false);
+		dragUnderway = true;
+	    }
+	}
+	else {
+	    dragUnderway = false;
+	}
+    }
+
+    private void repaintDragLimits() {
+	Rectangle damageR = dragDivider.getBounds();
+	if (dragDivider.isVertical()) {
+	    damageR.x = dragMin;
+	    damageR.width = dragMax - dragMin;
+	}
+	else {
+	    damageR.y = dragMin;
+	    damageR.height = dragMax - dragMin;
+	}
+	repaint(damageR);
+    }
+
+    private void updateDrag(int mx, int my) {
+	if (!dragUnderway) {
+	    return;
+	}
+	Rectangle oldBounds = dragDivider.getBounds();
+	Rectangle bounds = new Rectangle(oldBounds);
+	if (dragDivider.isVertical()) {
+	    bounds.x = mx - dragOffsetX;
+	    bounds.x = Math.max(bounds.x, dragMin);
+	    bounds.x = Math.min(bounds.x, dragMax);
+	}
+	else {
+	    bounds.y = my - dragOffsetY;
+	    bounds.y = Math.max(bounds.y, dragMin);
+	    bounds.y = Math.min(bounds.y, dragMax);
+	}
+	dragDivider.setBounds(bounds);
+	if (isContinuousLayout()) {
+	    revalidate();
+	    repaintDragLimits();
+	}
+	else {
+	    repaint(oldBounds.union(bounds));
+	}
+    }
+
+    private void clearDragState() {
+	dragDivider = null;
+	initialDividerBounds = null;
+	oldFloatingDividers = true;
+	dragOffsetX = dragOffsetY = 0;
+	dragMin = dragMax = -1;
+	dragUnderway = false;
+    }
+
+    private void finishDrag(int x, int y) {
+	if (dragUnderway) {
+	    clearDragState();
+	    if (!isContinuousLayout()) {
+		revalidate();
+		repaint();
+	    }
+	}
+    }
+    
+    private void cancelDrag() {       
+	if (dragUnderway) {
+	    dragDivider.setBounds(initialDividerBounds);
+	    getMultiSplitLayout().setFloatingDividers(oldFloatingDividers);
+	    setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+	    repaint();
+	    revalidate();
+	    clearDragState();
+	}
+    }
+
+    private void updateCursor(int x, int y, boolean show) {
+	if (dragUnderway) {
+	    return;
+	}
+	int cursorID = Cursor.DEFAULT_CURSOR;
+	if (show) {
+	    MultiSplitLayout.Divider divider = getMultiSplitLayout().dividerAt(x, y);
+	    if (divider != null) {
+		cursorID  = (divider.isVertical()) ? 
+		    Cursor.E_RESIZE_CURSOR : 
+		    Cursor.N_RESIZE_CURSOR;
+	    }
+	}
+	setCursor(Cursor.getPredefinedCursor(cursorID));
+    }
+
+
+    private class InputHandler extends MouseInputAdapter implements KeyListener {
+
+	public void mouseEntered(MouseEvent e) {
+	    updateCursor(e.getX(), e.getY(), true);
+	}
+
+	public void mouseMoved(MouseEvent e) {
+	    updateCursor(e.getX(), e.getY(), true);
+	}
+
+	public void mouseExited(MouseEvent e) {
+	    updateCursor(e.getX(), e.getY(), false);
+	}
+
+	public void mousePressed(MouseEvent e) {
+	    startDrag(e.getX(), e.getY());
+	}
+	public void mouseReleased(MouseEvent e) {
+	    finishDrag(e.getX(), e.getY());
+	}
+	public void mouseDragged(MouseEvent e) {
+	    updateDrag(e.getX(), e.getY());	    
+	}
+        public void keyPressed(KeyEvent e) { 
+	    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+		cancelDrag();
+	    }
+	}
+        public void keyReleased(KeyEvent e) { }
+	public void keyTyped(KeyEvent e) { }
+    }
+
+    public AccessibleContext getAccessibleContext() {
+        if( accessibleContext == null ) {
+            accessibleContext = new AccessibleMultiSplitPane();
+        }
+        return accessibleContext;
+    }
+    
+    protected class AccessibleMultiSplitPane extends AccessibleJPanel {
+        public AccessibleRole getAccessibleRole() {
+            return AccessibleRole.SPLIT_PANE;
+        }
+    }
+}
Index: src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java	(revision 2186)
+++ src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java	(working copy)
@@ -84,7 +84,7 @@
 
     public SelectionListDialog() {
         super(tr("Current Selection"), "selectionlist", tr("Open a selection list window."),
-                Shortcut.registerShortcut("subwindow:selection", tr("Toggle: {0}", tr("Current Selection")), KeyEvent.VK_T, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150);
+                Shortcut.registerShortcut("subwindow:selection", tr("Toggle: {0}", tr("Current Selection")), KeyEvent.VK_T, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150, true);
 
         selectionHistory = new LinkedList<Collection<? extends OsmPrimitive>>();
         popupMenu = new JPopupMenu();
Index: src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java	(revision 2186)
+++ src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java	(working copy)
@@ -33,6 +33,7 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.JosmAction;
 import org.openstreetmap.josm.actions.HelpAction.Helpful;
+import org.openstreetmap.josm.gui.dialogs.DialogsPanel.Action;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Shortcut;
@@ -43,43 +44,13 @@
  *
  */
 public class ToggleDialog extends JPanel implements Helpful {
-//    private static final Logger logger = Logger.getLogger(ToggleDialog.class.getName());
-
-    /**
-     * The action to toggle the visibility state of this toggle dialog.
-     * 
-     * Emits {@see PropertyChangeEvent}s for the property <tt>selected</tt>:
-     * <ul>
-     *   <li>true, if the dialog is currently visible</li>
-     *   <li>false, if the dialog is currently invisible</li>
-     * </ul>
-     *
-     */
-    public final class ToggleDialogAction extends JosmAction {
-        private ToggleDialogAction(String name, String iconName, String tooltip, Shortcut shortcut, String prefname) {
-            super(name, iconName, tooltip, shortcut, false);
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            toggleVisibility();
-        }
-
-        public void toggleVisibility() {
-            if (isShowing) {
-                hideDialog();
-            } else {
-                showDialog();
-            }
-        }
-    }
-
-    /**
-     * The action to toggle this dialog.
-     */
+    /** The action to toggle this dialog */
     private ToggleDialogAction toggleAction;
     private String preferencePrefix;
 
-    private JPanel parent;
+    /** DialogsPanel that manages all ToggleDialogs */
+    private DialogsPanel dialogsPanel;
+    
     private  TitleBar titleBar;
     private String title;
 
@@ -100,12 +71,21 @@
 
     /** the preferred height if the toggle dialog is expanded */
     private int preferredHeight;
+    
     /** the label in the title bar which shows whether the toggle dialog is expanded or collapsed */
     private JLabel lblMinimized;
+    
     /** the JDialog displaying the toggle dialog as undocked dialog */
     private JDialog detachedDialog;
 
     /**
+     * Constructor 
+     * (see below)
+     */
+    public ToggleDialog(String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight) {
+        this(name, iconName, tooltip, shortcut, preferredHeight, false);
+    }
+    /**
      * Constructor
      * 
      * @param name  the name of the dialog
@@ -113,23 +93,12 @@
      * @param tooltip  the tool tip
      * @param shortcut  the shortcut
      * @param preferredHeight the preferred height for the dialog
+     * @param defShow if the dialog should be shown by default, if there is no preference
      */
-    public ToggleDialog(String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight) {
+    public ToggleDialog(String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight, boolean defShow) {
         super(new BorderLayout());
         this.preferencePrefix = iconName;
-        init(name, iconName, tooltip, shortcut, preferredHeight);
-    }
 
-    /**
-     * Initializes the toggle dialog
-     * 
-     * @param name
-     * @param iconName
-     * @param tooltip
-     * @param shortcut
-     * @param preferredHeight
-     */
-    private void init(String name, String iconName, String tooltip, Shortcut shortcut, final int preferredHeight) {
         /** Use the full width of the parent element */
         setPreferredSize(new Dimension(0, preferredHeight));
         /** Override any minimum sizes of child elements so the user can resize freely */
@@ -146,121 +115,76 @@
         titleBar = new TitleBar(name, iconName);
         add(titleBar, BorderLayout.NORTH);
 
-        setVisible(false);
         setBorder(BorderFactory.createEtchedBorder());
-
+        
+        isShowing = Main.pref.getBoolean(preferencePrefix+".visible", defShow);
         isDocked = Main.pref.getBoolean(preferencePrefix+".docked", true);
         isCollapsed = Main.pref.getBoolean(preferencePrefix+".minimized", false);
     }
 
     /**
-     * Sets the visibility of all components in this toggle dialog, except the title bar
+     * The action to toggle the visibility state of this toggle dialog.
      * 
-     * @param visible true, if the components should be visible; false otherwise
+     * Emits {@see PropertyChangeEvent}s for the property <tt>selected</tt>:
+     * <ul>
+     *   <li>true, if the dialog is currently visible</li>
+     *   <li>false, if the dialog is currently invisible</li>
+     * </ul>
+     *
      */
-    protected void setContentVisible(boolean visible) {
-        Component comps[] = getComponents();
-        for(int i=0; i<comps.length; i++) {
-            if(comps[i] != titleBar) {
-                comps[i].setVisible(visible);
+    public final class ToggleDialogAction extends JosmAction {
+        private ToggleDialogAction(String name, String iconName, String tooltip, Shortcut shortcut, String prefname) {
+            super(name, iconName, tooltip, shortcut, false);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            if (isShowing) {
+                hideDialog();
+                dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
+            } else {
+                showDialog();
+                expand();
+                dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, ToggleDialog.this);
             }
         }
     }
 
     /**
-     * Toggles between collapsed and expanded state
-     * 
+     * Shows the dialog
      */
-    protected void toggleExpandedState() {
-        if (isCollapsed) {
-            expand();
+    public void showDialog() {
+        setIsShowing(true);
+        if (!isDocked) {
+            detach();
         } else {
-            collapse();
+            dock();
+            this.setVisible(true);
         }
+        // toggling the selected value in order to enforce PropertyChangeEvents
+        setIsShowing(true);
+        toggleAction.putValue("selected", false);
+        toggleAction.putValue("selected", true);
     }
-
+    
     /**
-     * Collapses the toggle dialog to the title bar only
-     * 
+     * Hides the dialog
      */
-    protected void collapse() {
-        setContentVisible(false);
-        isCollapsed = true;
-        Main.pref.put(preferencePrefix+".minimized", true);
-        setPreferredSize(new Dimension(0,20));
-        setMaximumSize(new Dimension(Integer.MAX_VALUE,20));
-        lblMinimized.setIcon(ImageProvider.get("misc", "minimized"));
-        refreshToggleDialogsView();
+    public void hideDialog() {
+        closeDetachedDialog();
+        this.setVisible(false);
+        setIsShowing(false);
+        toggleAction.putValue("selected", false);
     }
 
     /**
-     * Expands the toggle dialog
-     */
-    protected void expand() {
-        setContentVisible(true);
-        isCollapsed = false;
-        Main.pref.put(preferencePrefix+".minimized", false);
-        setPreferredSize(new Dimension(0,preferredHeight));
-        setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
-        lblMinimized.setIcon(ImageProvider.get("misc", "normal"));
-        refreshToggleDialogsView();
-    }
-
-    /**
-     * Replies the index of this toggle dialog in the view of
-     * toggle dialog.
-     * 
-     * @return
-     */
-    protected int getDialogPosition() {
-        if (parent == null) return -1;
-        for (int i=0; i< parent.getComponentCount(); i++) {
-            String name = parent.getComponent(i).getName();
-            if (name != null && name.equals(this.getName()))
-                return i;
-        }
-        return -1;
-    }
-
-    /**
      * Displays the toggle dialog in the toggle dialog view on the right
      * of the main map window.
      * 
      */
     protected void dock() {
         detachedDialog = null;
-        if (parent == null) return;
-
-        // check whether the toggle dialog view contains a placeholder
-        // for this toggle dialog. If so, replace it with this dialog.
-        //
-        int idx = getDialogPosition();
-        if (idx > -1) {
-            parent.remove(idx);
-            if (idx >= parent.getComponentCount()) {
-                parent.add(ToggleDialog.this);
-            } else {
-                parent.add(ToggleDialog.this,idx);
-            }
-        } else {
-            parent.add(ToggleDialog.this);
-        }
-        parent.validate();
-
-        if(Main.pref.getBoolean(preferencePrefix+".visible")) {
-            setVisible(true);
-        } else {
-            setVisible(false);
-        }
         titleBar.setVisible(true);
-        isCollapsed = Main.pref.getBoolean(preferencePrefix+".minimized", false);
-        if (isCollapsed) {
-            collapse();
-        } else {
-            expand();
-        }
-        isDocked = true;
-        Main.pref.put(preferencePrefix+".docked", isDocked);
+        setIsDocked(true);
     }
 
     /**
@@ -269,85 +193,52 @@
      */
     protected void detach() {
         setContentVisible(true);
-        setVisible(true);
-        // replace the toggle dialog by an invisible place holder. Makes sure
-        // we can place the toggle dialog where it was when it becomes docked
-        // again.
-        //
-        if (parent != null) {
-            int idx = getDialogPosition();
-            if (idx > -1) {
-                JPanel placeHolder = new JPanel();
-                placeHolder.setName(this.getName());
-                placeHolder.setVisible(false);
-                parent.add(placeHolder,idx);
-            }
-            parent.remove(ToggleDialog.this);
-        }
-        
-
+        this.setVisible(true);        
         titleBar.setVisible(false);
         detachedDialog = new DetachedDialog();
         detachedDialog.setVisible(true);
-        refreshToggleDialogsView();
-        isDocked = false;
-        Main.pref.put(preferencePrefix+".docked", isDocked);
+        setIsDocked(false);
     }
 
     /**
-     * Hides the dialog
+     * Collapses the toggle dialog to the title bar only
+     * 
      */
-    public void hideDialog() {
-        closeDetachedDialog();
-        setVisible(false);
-        isShowing = false;
-        Main.pref.put(preferencePrefix+".visible", false);
-        refreshToggleDialogsView();
-        toggleAction.putValue("selected", false);
+    public void collapse() {
+        setContentVisible(false);
+        setIsCollapsed(true);
+        setPreferredSize(new Dimension(0,20));
+        setMaximumSize(new Dimension(Integer.MAX_VALUE,20));
+        setMinimumSize(new Dimension(Integer.MAX_VALUE,20));
+        lblMinimized.setIcon(ImageProvider.get("misc", "minimized"));
     }
 
     /**
-     * Replies true if this dialog is showing either as docked or as detached dialog
+     * Expands the toggle dialog
      */
-    public boolean isDialogShowing() {
-        return this.isShowing;
+    protected void expand() {
+        setContentVisible(true);
+        setIsCollapsed(false);
+        setPreferredSize(new Dimension(0,preferredHeight));
+        setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
+        lblMinimized.setIcon(ImageProvider.get("misc", "normal"));
     }
 
     /**
-     * Shows the dialog
+     * Sets the visibility of all components in this toggle dialog, except the title bar
+     * 
+     * @param visible true, if the components should be visible; false otherwise
      */
-    public void showDialog() {
-        if (!isDocked) {
-            detach();
-        } else {
-            dock();
-            if (!isCollapsed) {
-                expand();
-                setVisible(true);
-                refreshToggleDialogsView();
-            } else {
-                setVisible(true);
-                refreshToggleDialogsView();
+    protected void setContentVisible(boolean visible) {
+        Component comps[] = getComponents();
+        for(int i=0; i<comps.length; i++) {
+            if(comps[i] != titleBar) {
+                comps[i].setVisible(visible);
             }
         }
-        isShowing = true;
-        // toggling the selected value in order to enforce PropertyChangeEvents
-        toggleAction.putValue("selected", false);
-        toggleAction.putValue("selected", true);
-        Main.pref.put(preferencePrefix+".visible", true);
     }
 
     /**
-     * Refreshes the layout of the parent toggle dialog view
-     * 
-     */
-    protected void refreshToggleDialogsView() {
-        if(parent != null){
-            parent.validate();
-        }
-    }
-
-    /**
      * Closes the the detached dialog if this toggle dialog is currently displayed
      * in a detached dialog.
      * 
@@ -360,58 +251,8 @@
         }
     }
 
-    public String helpTopic() {
-        String help = getClass().getName();
-        help = help.substring(help.lastIndexOf('.')+1, help.length()-6);
-        return "Dialog/"+help;
-    }
 
     /**
-     * Replies the action to toggle the visible state of this toggle dialog
-     * 
-     * @return the action to toggle the visible state of this toggle dialog
-     */
-    public AbstractAction getToggleAction() {
-        return toggleAction;
-    }
-
-    /**
-     * Replies the prefix for the preference settings of this dialog.
-     * 
-     * @return the prefix for the preference settings of this dialog.
-     */
-    public String getPreferencePrefix() {
-        return preferencePrefix;
-    }
-
-    /**
-     * Sets the parent displaying all toggle dialogs
-     * 
-     * @param parent the parent
-     */
-    public void setParent(JPanel parent) {
-        this.parent = parent;
-    }
-
-    /**
-     * Replies the name of this toggle dialog
-     *
-     */
-    @Override
-    public String getName() {
-        return "toggleDialog." + preferencePrefix;
-    }
-
-    /**
-     * Sets the title
-     * 
-     * @param title the title
-     */
-    public void setTitle(String title) {
-        titleBar.setTitle(title);
-    }
-
-    /**
      * The title bar displayed in docked mode
      * 
      */
@@ -449,7 +290,14 @@
                     new MouseAdapter() {
                         @Override
                         public void mouseClicked(MouseEvent e) {
-                            toggleExpandedState();
+//                            toggleExpandedState();
+                            if (isCollapsed) {
+                                expand();
+                                dialogsPanel.reconstruct(Action.COLLAPSED_TO_DEFAULT, ToggleDialog.this);
+                            } else {
+                                collapse();
+                                dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
+                            }
                         }
                     }
             );
@@ -462,6 +310,7 @@
                     new ActionListener(){
                         public void actionPerformed(ActionEvent e) {
                             detach();
+                            dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
                         }
                     }
             );
@@ -475,6 +324,7 @@
                     new ActionListener(){
                         public void actionPerformed(ActionEvent e) {
                             hideDialog();
+                            dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
                         }
                     }
             );
@@ -506,6 +356,8 @@
                     getContentPane().removeAll();
                     dispose();
                     dock();
+                    expand();
+                    dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, ToggleDialog.this);
                 }
             });
             String bounds = Main.pref.get(preferencePrefix+".bounds",null);
@@ -541,4 +393,90 @@
     protected Dimension getDefaultDetachedSize() {
         return new Dimension(Main.map.DEF_TOGGLE_DLG_WIDTH, preferredHeight);
     }
+
+    /**
+     * Replies the action to toggle the visible state of this toggle dialog
+     * 
+     * @return the action to toggle the visible state of this toggle dialog
+     */
+    public AbstractAction getToggleAction() {
+        return toggleAction;
+    }
+
+    /**
+     * Replies the prefix for the preference settings of this dialog.
+     * 
+     * @return the prefix for the preference settings of this dialog.
+     */
+    public String getPreferencePrefix() {
+        return preferencePrefix;
+    }
+
+    /**
+     * Sets the dialogsPanel managing all toggle dialogs
+     */
+    public void setDialogsPanel(DialogsPanel dialogsPanel) {
+        this.dialogsPanel = dialogsPanel;
+    }
+
+    /**
+     * Replies the name of this toggle dialog
+     */
+    @Override
+    public String getName() {
+        return "toggleDialog." + preferencePrefix;
+    }
+
+    /**
+     * Sets the title
+     */
+    public void setTitle(String title) {
+        titleBar.setTitle(title);
+    }
+    
+    private void setIsShowing(boolean val) {
+        isShowing = val;
+        Main.pref.put(preferencePrefix+".visible", val);
+    }
+
+    private void setIsDocked(boolean val) {
+        isDocked = val;
+        Main.pref.put(preferencePrefix+".docked", val);
+    }
+
+    private void setIsCollapsed(boolean val) {
+        isCollapsed = val;
+        Main.pref.put(preferencePrefix+".minimized", val);
+    }
+
+    public int getPreferredHeight() {
+        return preferredHeight;
+    }
+
+    /**
+     * Replies true if this dialog is showing either as docked or as detached dialog
+     */
+    public boolean isDialogShowing() {
+        return isShowing;
+    }
+
+    /**
+     * Replies true if this dialog is docked and expanded
+     */
+    public boolean isDialogInDefaultView() {
+        return isShowing && isDocked && (! isCollapsed);
+    }
+
+    /**
+     * Replies true if this dialog is docked and collapsed
+     */
+    public boolean isDialogInCollapsedView() {
+        return isShowing && isDocked && isCollapsed;
+    }
+
+    public String helpTopic() {
+        String help = getClass().getName();
+        help = help.substring(help.lastIndexOf('.')+1, help.length()-6);
+        return "Dialog/"+help;
+    }
 }
Index: src/org/openstreetmap/josm/gui/dialogs/DialogsPanel.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/DialogsPanel.java	(revision 0)
+++ src/org/openstreetmap/josm/gui/dialogs/DialogsPanel.java	(revision 0)
@@ -0,0 +1,287 @@
+// License: GPL. See LICENSE file for details.
+
+package org.openstreetmap.josm.gui.dialogs;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Dimension;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BoxLayout;
+import javax.swing.JPanel;
+
+import org.jdesktop.swingx.MultiSplitLayout;
+import org.jdesktop.swingx.MultiSplitLayout.Node;
+import org.jdesktop.swingx.MultiSplitLayout.Leaf;
+import org.jdesktop.swingx.MultiSplitLayout.Divider;
+import org.jdesktop.swingx.MultiSplitLayout.Split;
+import org.jdesktop.swingx.MultiSplitPane;
+
+import org.openstreetmap.josm.Main;
+
+public class DialogsPanel extends JPanel {
+    protected List<ToggleDialog> allDialogs = new ArrayList<ToggleDialog>();
+    protected MultiSplitPane mSpltPane = new MultiSplitPane();
+    final protected int DIVIDER_SIZE = 5;
+    
+    /**
+     * Panels that are added to the multisplitpane.
+     */
+    private List<JPanel> panels = new ArrayList<JPanel>();
+
+    private boolean initialized = false;    
+    public void initialize(List<ToggleDialog> allDialogs) {
+        if (initialized) {
+            throw new IllegalStateException();
+        }
+        initialized = true;
+        this.allDialogs = allDialogs;
+        
+        for (Integer i=0; i < allDialogs.size(); ++i) {
+            final ToggleDialog dlg = allDialogs.get(i);
+            dlg.setDialogsPanel(this);            
+            dlg.setVisible(false);
+        }
+        for (int i=0; i < allDialogs.size() + 1; ++i) {
+            final JPanel p = new JPanel() {
+                /** 
+                 * Honoured by the MultiSplitPaneLayout when the
+                 * entire Window is resized.
+                 */
+                public Dimension getMinimumSize() {
+                    return new Dimension(0, 40);    
+                }
+            };
+            p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
+            p.setVisible(false);
+
+            mSpltPane.add(p, "L"+i);
+            panels.add(p);
+        }
+
+        for (Integer i=0; i < allDialogs.size(); ++i) {
+            final ToggleDialog dlg = allDialogs.get(i);
+            if (dlg.isDialogShowing()) {
+                dlg.showDialog();
+                if (dlg.isDialogInCollapsedView()) {
+                    dlg.collapse();
+                }            
+            } else {
+                dlg.hideDialog();
+            }
+        }
+        this.add(mSpltPane);
+        reconstruct(Action.ELEMENT_SHRINKS, null);
+    }
+
+    /**
+     * What action was performed to trigger the reconstruction
+     */
+    public enum Action { 
+        INVISIBLE_TO_DEFAULT, 
+        COLLAPSED_TO_DEFAULT, 
+    /*  INVISIBLE_TO_COLLAPSED,    does not happen */
+        ELEMENT_SHRINKS         /* else. (Remaining elements have more space.) */
+    };
+    /**
+     * Reconstruct the view, if the configurations of dialogs has changed.
+     * @param action what happened, so the reconstruction is necessary
+     * @param triggeredBy the dialog that caused the reconstruction
+     */
+    public void reconstruct(Action action, ToggleDialog triggeredBy) {
+
+        final int N = allDialogs.size();
+         
+        /**
+         * reset the panels
+         */       
+        for (int i=0; i < N; ++i) {
+            final JPanel p = panels.get(i);
+            p.removeAll();
+            p.setVisible(false);
+        }
+
+        /**
+         * Add the elements to their respective panel.
+         *
+         * Each panel contains one dialog in default view and zero or more
+         * collapsed dialogs on top of it. The last panel is an exception
+         * as it can have collapsed dialogs at the bottom as well.
+         * If there are no dialogs in default view, show the collapsed ones
+         * in the last panel anyway.
+         */
+        int k = N-1;                // index of the current Panel (start with last one)
+        JPanel p = panels.get(k);   // current Panel
+        k = -1;                     // indicates that the current Panel index is N-1, but no default-view-Dialog was added to this Panel yet.
+        for (int i=N-1; i >= 0 ; --i) {
+            final ToggleDialog dlg = allDialogs.get(i);
+            if (dlg.isDialogInDefaultView()) {
+                if (k == -1) {
+                    k = N-1;
+                } else {
+                    --k;
+                    p = panels.get(k);
+                }
+                p.add(dlg, 0);
+                p.setVisible(true);
+            }
+            else if (dlg.isDialogInCollapsedView()) {
+                p.add(dlg, 0);
+                p.setVisible(true);
+            }   
+        }
+        
+        if (k == -1) {
+            k = N-1;
+        }
+        final int numPanels = N - k;
+
+        /**
+         * Determine the panel geometry
+         */
+        if (action == Action.ELEMENT_SHRINKS) {
+            for (int i=0; i<N; ++i) {
+                final ToggleDialog dlg = allDialogs.get(i);
+                if (dlg.isDialogInDefaultView()) {
+                    final int ph = dlg.getPreferredHeight();
+                    final int ah = dlg.getSize().height;
+                    dlg.setPreferredSize(new Dimension(Integer.MAX_VALUE, (ah < 20 ? ph : ah)));
+                }
+            } 
+        } else {
+            if (triggeredBy == null) {
+                throw new IllegalArgumentException();
+            }
+            
+            int sumP = 0;   // sum of preferred heights of dialogs in default view (without the triggering dialog)
+            int sumA = 0;   // sum of actual heights of dialogs in default view (without the triggering dialog)
+            int sumC = 0;   // sum of heights of all collapsed dialogs (triggering dialog is never collapsed)
+
+            for (int i=0; i<N; ++i) {
+                final ToggleDialog dlg = allDialogs.get(i);
+                if (dlg.isDialogInDefaultView()) {
+                    if (dlg != triggeredBy) {
+                        final int ph = dlg.getPreferredHeight();
+                        final int ah = dlg.getSize().height;
+                        sumP += ph;
+                        sumA += ah;
+                    }
+                }
+                else if (dlg.isDialogInCollapsedView()) {
+                    sumC += dlg.getSize().height;
+                }
+            } 
+
+            /** total Height */
+            final int H = mSpltPane.getMultiSplitLayout().getModel().getBounds().getSize().height;
+
+            /** space, that is available for dialogs in default view (after the reconfiguration) */            
+            final int s2 = H - (numPanels - 1) * DIVIDER_SIZE - sumC;
+            
+            final int hp_trig = triggeredBy.getPreferredHeight();
+            if (hp_trig <= 0) throw new IllegalStateException(); // Must be positive
+            
+            /** The new dialog gets a fair share */
+            final int hn_trig = hp_trig * s2 / (hp_trig + sumP);
+            triggeredBy.setPreferredSize(new Dimension(Integer.MAX_VALUE, hn_trig)); 
+            
+            /** This is remainig for the other default view dialogs */
+            final int R = s2 - hn_trig;
+            
+            /**
+             * Take space only from dialogs that are relatively large
+             */
+            int D_m = 0;        // additional space needed by the small dialogs
+            int D_p = 0;        // available space from the large dialogs
+            for (int i=0; i<N; ++i) {
+                final ToggleDialog dlg = allDialogs.get(i);
+                if (dlg.isDialogInDefaultView() && dlg != triggeredBy) {
+                    final int ha = dlg.getSize().height;                              // current
+                    final int h0 = ha * R / sumA;                                     // proportional shrinking
+                    final int he = dlg.getPreferredHeight() * s2 / (sumP + hp_trig);  // fair share
+                    if (h0 < he) {                  // dialog is relatively small
+                        int hn = Math.min(ha, he);  // shrink less, but do not grow
+                        D_m += hn - h0;
+                    } else {                        // dialog is relatively large
+                        D_p += h0 - he;
+                    }
+                }
+            }
+            /** adjust, without changing the sum */
+            for (int i=0; i<N; ++i) {
+                final ToggleDialog dlg = allDialogs.get(i);
+                if (dlg.isDialogInDefaultView() && dlg != triggeredBy) {
+                    final int ha = dlg.getSize().height;
+                    final int h0 = ha * R / sumA;
+                    final int he = dlg.getPreferredHeight() * s2 / (sumP + hp_trig);
+                    if (h0 < he) {
+                        int hn = Math.min(ha, he);
+                        dlg.setPreferredSize(new Dimension(Integer.MAX_VALUE, hn));
+                    } else {
+                        int d;
+                        try {
+                            d = (h0-he) * D_m / D_p;
+                        } catch (ArithmeticException e) { /* D_p may be zero - nothing wrong with that. */
+                            d = 0;
+                        };
+                        dlg.setPreferredSize(new Dimension(Integer.MAX_VALUE, h0 - d));
+                    }
+                }
+            }
+        }
+
+        /**
+         * create Layout
+         */
+        final List<Node> ch = new ArrayList<Node>();
+        
+        for (int i = k; i <= N-1; ++i) {
+            if (i != k) {
+                ch.add(new Divider());
+            }
+            Leaf l = new Leaf("L"+i);
+            l.setWeight(1.0 / numPanels);
+            ch.add(l);
+        }
+
+        if (numPanels == 1) {
+            Node model = ch.get(0);
+            mSpltPane.getMultiSplitLayout().setModel(model);
+        } else {
+            Split model = new Split();
+            model.setRowLayout(false);
+            model.setChildren(ch);
+            mSpltPane.getMultiSplitLayout().setModel(model);
+        }
+            
+        mSpltPane.getMultiSplitLayout().setDividerSize(DIVIDER_SIZE);
+        mSpltPane.getMultiSplitLayout().setFloatingDividers(true);
+        mSpltPane.revalidate();        
+    }
+    
+    public void destroy() {
+        for (ToggleDialog t : allDialogs) {
+            t.closeDetachedDialog();
+        }
+    }
+
+    /**
+     * Replies the instance of a toggle dialog of type <code>type</code> managed by this
+     * map frame
+     *
+     * @param <T>
+     * @param type the class of the toggle dialog, i.e. UserListDialog.class
+     * @return the instance of a toggle dialog of type <code>type</code> managed by this
+     * map frame; null, if no such dialog exists
+     *
+     */
+    public <T> T getToggleDialog(Class<T> type) {
+        for (ToggleDialog td : allDialogs) {
+            if (type.isInstance(td))
+                return type.cast(td);
+        }
+        return null;
+    }
+}
Index: src/org/openstreetmap/josm/gui/dialogs/CommandStackDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/CommandStackDialog.java	(revision 2186)
+++ src/org/openstreetmap/josm/gui/dialogs/CommandStackDialog.java	(working copy)
@@ -28,7 +28,7 @@
 
     public CommandStackDialog(final MapFrame mapFrame) {
         super(tr("Command Stack"), "commandstack", tr("Open a list of all commands (undo buffer)."),
-                Shortcut.registerShortcut("subwindow:commandstack", tr("Toggle: {0}", tr("Command Stack")), KeyEvent.VK_O, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 100);
+                Shortcut.registerShortcut("subwindow:commandstack", tr("Toggle: {0}", tr("Command Stack")), KeyEvent.VK_O, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 100, true);
         Main.main.undoRedo.listenerCommands.add(this);
 
         tree.setRootVisible(false);
Index: src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java	(revision 2186)
+++ src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java	(working copy)
@@ -143,7 +143,7 @@
      */
     protected LayerListDialog(MapFrame mapFrame) {
         super(tr("Layers"), "layerlist", tr("Open a list of all loaded layers."),
-                Shortcut.registerShortcut("subwindow:layers", tr("Toggle: {0}", tr("Layers")), KeyEvent.VK_L, Shortcut.GROUP_LAYER), 100);
+                Shortcut.registerShortcut("subwindow:layers", tr("Toggle: {0}", tr("Layers")), KeyEvent.VK_L, Shortcut.GROUP_LAYER), 100, true);
 
         // create the models
         //
Index: src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java	(revision 2186)
+++ src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java	(working copy)
@@ -453,7 +453,7 @@
     public PropertiesDialog(MapFrame mapFrame) {
         super(tr("Properties/Memberships"), "propertiesdialog", tr("Properties for selected objects."),
                 Shortcut.registerShortcut("subwindow:properties", tr("Toggle: {0}", tr("Properties/Memberships")), KeyEvent.VK_P,
-                        Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150);
+                        Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150, true);
 
         // setting up the properties table
         propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
Index: src/org/openstreetmap/josm/gui/MapFrame.java
===================================================================
--- src/org/openstreetmap/josm/gui/MapFrame.java	(revision 2186)
+++ src/org/openstreetmap/josm/gui/MapFrame.java	(working copy)
@@ -7,6 +7,7 @@
 import java.awt.Container;
 import java.awt.Dimension;
 import java.util.ArrayList;
+import java.util.List;
 
 import javax.swing.AbstractButton;
 import javax.swing.Action;
@@ -28,6 +29,7 @@
 import org.openstreetmap.josm.actions.mapmode.ZoomAction;
 import org.openstreetmap.josm.gui.dialogs.CommandStackDialog;
 import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
+import org.openstreetmap.josm.gui.dialogs.DialogsPanel;
 import org.openstreetmap.josm.gui.dialogs.FilterDialog;
 import org.openstreetmap.josm.gui.dialogs.HistoryDialog;
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
@@ -74,8 +76,8 @@
      * The panel list of all toggle dialog icons. To add new toggle dialog actions, use addToggleDialog
      * instead of adding directly to this list.
      */
-    private JPanel toggleDialogs = new JPanel();
-    private ArrayList<ToggleDialog> allDialogs = new ArrayList<ToggleDialog>();
+    private List<ToggleDialog> allDialogs = new ArrayList<ToggleDialog>();
+    private DialogsPanel dialogsPanel = new DialogsPanel();
 
     public final ButtonGroup toolGroup = new ButtonGroup();
     
@@ -106,7 +108,7 @@
         toolGroup.setSelected(((AbstractButton)toolBarActions.getComponent(0)).getModel(), true);
 
         JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true,
-                             mapView, toggleDialogs); 
+                             mapView, dialogsPanel); 
 
         /**
          * All additional space goes to the mapView
@@ -129,10 +131,10 @@
         
         add(splitPane, BorderLayout.CENTER);
 
-        toggleDialogs.setLayout(new BoxLayout(toggleDialogs, BoxLayout.Y_AXIS));
-        toggleDialogs.setPreferredSize(new Dimension(Main.pref.getInteger("toggleDialogs.width",DEF_TOGGLE_DLG_WIDTH), 0));
+        dialogsPanel.setLayout(new BoxLayout(dialogsPanel, BoxLayout.Y_AXIS));
+        dialogsPanel.setPreferredSize(new Dimension(Main.pref.getInteger("toggleDialogs.width",DEF_TOGGLE_DLG_WIDTH), 0));
                                                                         
-        toggleDialogs.setMinimumSize(new Dimension(24, 0));
+        dialogsPanel.setMinimumSize(new Dimension(24, 0));
         mapView.setMinimumSize(new Dimension(10,0));
 
         toolBarToggle.setFloatable(false);
@@ -172,9 +174,7 @@
      * Delegates the call to all Destroyables within this component (e.g. MapModes)
      */
     public void destroy() {
-        for (ToggleDialog t : allDialogs) {
-            t.closeDetachedDialog();
-        }
+        dialogsPanel.destroy();
         for (int i = 0; i < toolBarActions.getComponentCount(); ++i)
             if (toolBarActions.getComponent(i) instanceof Destroyable) {
                 ((Destroyable)toolBarActions).destroy();
@@ -202,18 +202,8 @@
     /**
      * Open all ToggleDialogs that have their preferences property set. Close all others.
      */
-    public void setVisibleDialogs() {
-        toggleDialogs.removeAll();
-        for (ToggleDialog dialog: allDialogs) {
-            dialog.setVisible(false);
-            toggleDialogs.add(dialog);
-            dialog.setParent(toggleDialogs);
-            if (Main.pref.getBoolean(dialog.getPreferencePrefix()+".visible")) {
-                dialog.showDialog();
-            } else {
-                dialog.hideDialog();
-            }
-        }
+    public void initializeDialogsPane() {
+        dialogsPanel.initialize(allDialogs);
     }
 
     /**
@@ -243,8 +233,6 @@
         }
     }
 
-
-
     /**
      * Change the operating map mode for the view. Will call unregister on the
      * old MapMode and register on the new one.
@@ -298,17 +286,13 @@
      *
      */
     public <T> T getToggleDialog(Class<T> type) {
-        for (ToggleDialog td : allDialogs) {
-            if (type.isInstance(td))
-                return type.cast(td);
-        }
-        return null;
+        return dialogsPanel.getToggleDialog(type);
     }
 
     /**
      * Returns the current width of the (possibly resized) toggle dialog area
      */
     public int getToggleDlgWidth() {
-        return toggleDialogs.getWidth();
+        return dialogsPanel.getWidth();
     }
 }
Index: src/org/openstreetmap/josm/Main.java
===================================================================
--- src/org/openstreetmap/josm/Main.java	(revision 2186)
+++ src/org/openstreetmap/josm/Main.java	(working copy)
@@ -219,7 +219,7 @@
             setMapFrame(mapFrame);
             mapFrame.selectMapMode((MapMode)mapFrame.getDefaultButtonAction());
             mapFrame.setVisible(true);
-            mapFrame.setVisibleDialogs();
+            mapFrame.initializeDialogsPane();
             // bootstrapping problem: make sure the layer list dialog is going to
             // listen to change events of the very first layer
             //
Index: src/org/openstreetmap/josm/data/Preferences.java
===================================================================
--- src/org/openstreetmap/josm/data/Preferences.java	(revision 2186)
+++ src/org/openstreetmap/josm/data/Preferences.java	(working copy)
@@ -441,10 +441,6 @@
 
     public final void resetToDefault(){
         properties.clear();
-        put("layerlist.visible", true);
-        put("propertiesdialog.visible", true);
-        put("selectionlist.visible", true);
-        put("commandstack.visible", true);
         if (System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") == -1) {
             put("laf", "javax.swing.plaf.metal.MetalLookAndFeel");
         } else {
