Index: trunk/src/org/openstreetmap/josm/actions/AboutAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/AboutAction.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/actions/AboutAction.java	(revision 6340)
@@ -20,4 +20,5 @@
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.widgets.JosmTextArea;
+import org.openstreetmap.josm.gui.widgets.UrlLabel;
 import org.openstreetmap.josm.plugins.PluginHandler;
 import org.openstreetmap.josm.tools.BugReportExceptionHandler;
@@ -25,5 +26,4 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Shortcut;
-import org.openstreetmap.josm.tools.UrlLabel;
 import org.openstreetmap.josm.tools.Utils;
 
Index: trunk/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/actions/ImageryAdjustAction.java	(revision 6340)
@@ -26,8 +26,8 @@
 import org.openstreetmap.josm.data.imagery.OffsetBookmark;
 import org.openstreetmap.josm.gui.ExtendedDialog;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.layer.ImageryLayer;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
 
Index: trunk/src/org/openstreetmap/josm/actions/MapRectifierWMSmenuAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/MapRectifierWMSmenuAction.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/actions/MapRectifierWMSmenuAction.java	(revision 6340)
@@ -25,7 +25,7 @@
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.Shortcut;
-import org.openstreetmap.josm.tools.UrlLabel;
 import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
+import org.openstreetmap.josm.gui.widgets.UrlLabel;
 
 public class MapRectifierWMSmenuAction extends JosmAction {
Index: trunk/src/org/openstreetmap/josm/corrector/TagCorrector.java
===================================================================
--- trunk/src/org/openstreetmap/josm/corrector/TagCorrector.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/corrector/TagCorrector.java	(revision 6340)
@@ -29,5 +29,5 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.DefaultNameFormatter;
-import org.openstreetmap.josm.gui.JMultilineLabel;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
Index: trunk/src/org/openstreetmap/josm/gui/BookmarkList.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/BookmarkList.java	(revision 6339)
+++ 	(revision )
@@ -1,239 +1,0 @@
-// License: GPL. Copyright 2007 by Immanuel Scholz and others
-package org.openstreetmap.josm.gui;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.Component;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.swing.DefaultListModel;
-import javax.swing.ImageIcon;
-import javax.swing.JLabel;
-import javax.swing.JList;
-import javax.swing.JOptionPane;
-import javax.swing.ListCellRenderer;
-import javax.swing.UIManager;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.Utils;
-
-/**
- * List class that read and save its content from the bookmark file.
- * @author imi
- */
-public class BookmarkList extends JList {
-
-    /**
-     * Class holding one bookmarkentry.
-     * @author imi
-     */
-    public static class Bookmark implements Comparable<Bookmark> {
-        private String name;
-        private Bounds area;
-
-        public Bookmark(Collection<String> list) throws NumberFormatException, IllegalArgumentException {
-            List<String> array = new ArrayList<String>(list);
-            if(array.size() < 5)
-                throw new IllegalArgumentException(tr("Wrong number of arguments for bookmark"));
-            name = array.get(0);
-            area = new Bounds(Double.parseDouble(array.get(1)), Double.parseDouble(array.get(2)),
-                              Double.parseDouble(array.get(3)), Double.parseDouble(array.get(4)));
-        }
-
-        public Bookmark() {
-            area = null;
-            name = null;
-        }
-
-        public Bookmark(Bounds area) {
-            this.area = area;
-        }
-
-        @Override public String toString() {
-            return name;
-        }
-
-        @Override
-        public int compareTo(Bookmark b) {
-            return name.toLowerCase().compareTo(b.name.toLowerCase());
-        }
-
-        public Bounds getArea() {
-            return area;
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        public void setName(String name) {
-            this.name = name;
-        }
-
-        public void setArea(Bounds area) {
-            this.area = area;
-        }
-    }
-
-    /**
-     * Create a bookmark list as well as the Buttons add and remove.
-     */
-    public BookmarkList() {
-        setModel(new DefaultListModel());
-        load();
-        setVisibleRowCount(7);
-        setCellRenderer(new BookmarkCellRenderer());
-    }
-
-    /**
-     * Loads the bookmarks from file.
-     */
-    public void load() {
-        DefaultListModel model = (DefaultListModel)getModel();
-        model.removeAllElements();
-        Collection<Collection<String>> args = Main.pref.getArray("bookmarks", null);
-        if(args != null) {
-            LinkedList<Bookmark> bookmarks = new LinkedList<Bookmark>();
-            for(Collection<String> entry : args) {
-                try {
-                    bookmarks.add(new Bookmark(entry));
-                }
-                catch (Exception e) {
-                    Main.error(tr("Error reading bookmark entry: %s", e.getMessage()));
-                }
-            }
-            Collections.sort(bookmarks);
-            for (Bookmark b : bookmarks) {
-                model.addElement(b);
-            }
-        }
-        else if(!Main.applet) { /* FIXME: remove else clause after spring 2011, but fix windows installer before */
-            File bookmarkFile = new File(Main.pref.getPreferencesDir(),"bookmarks");
-            try {
-                LinkedList<Bookmark> bookmarks = new LinkedList<Bookmark>();
-                if (bookmarkFile.exists()) {
-                    Main.info("Try loading obsolete bookmarks file");
-                    BufferedReader in = new BufferedReader(new InputStreamReader(
-                            new FileInputStream(bookmarkFile), "utf-8"));
-
-                    for (String line = in.readLine(); line != null; line = in.readLine()) {
-                        Matcher m = Pattern.compile("^(.+)[,\u001e](-?\\d+.\\d+)[,\u001e](-?\\d+.\\d+)[,\u001e](-?\\d+.\\d+)[,\u001e](-?\\d+.\\d+)$").matcher(line);
-                        if (!m.matches() || m.groupCount() != 5) {
-                            Main.error(tr("Unexpected line ''{0}'' in bookmark file ''{1}''",line, bookmarkFile.toString()));
-                            continue;
-                        }
-                        Bookmark b = new Bookmark();
-                        b.setName(m.group(1));
-                        double[] values= new double[4];
-                        for (int i = 0; i < 4; ++i) {
-                            try {
-                                values[i] = Double.parseDouble(m.group(i+2));
-                            } catch (NumberFormatException e) {
-                                Main.error(tr("Illegal double value ''{0}'' on line ''{1}'' in bookmark file ''{2}''",m.group(i+2),line, bookmarkFile.toString()));
-                                continue;
-                            }
-                        }
-                        b.setArea(new Bounds(values));
-                        bookmarks.add(b);
-                    }
-                    Utils.close(in);
-                    Collections.sort(bookmarks);
-                    for (Bookmark b : bookmarks) {
-                        model.addElement(b);
-                    }
-                    save();
-                    Main.info("Removing obsolete bookmarks file");
-                    bookmarkFile.delete();
-                }
-            } catch (IOException e) {
-                e.printStackTrace();
-                JOptionPane.showMessageDialog(
-                        Main.parent,
-                        tr("<html>Could not read bookmarks from<br>''{0}''<br>Error was: {1}</html>",
-                                bookmarkFile.toString(),
-                                e.getMessage()
-                        ),
-                        tr("Error"),
-                        JOptionPane.ERROR_MESSAGE
-                );
-            }
-        }
-    }
-
-    /**
-     * Save all bookmarks to the preferences file
-     */
-    public void save() {
-        LinkedList<Collection<String>> coll = new LinkedList<Collection<String>>();
-        for (Object o : ((DefaultListModel)getModel()).toArray()) {
-            String[] array = new String[5];
-            Bookmark b = (Bookmark)o;
-            array[0] = b.getName();
-            Bounds area = b.getArea();
-            array[1] = String.valueOf(area.getMinLat());
-            array[2] = String.valueOf(area.getMinLon());
-            array[3] = String.valueOf(area.getMaxLat());
-            array[4] = String.valueOf(area.getMaxLon());
-            coll.add(Arrays.asList(array));
-        }
-        Main.pref.putArray("bookmarks", coll);
-    }
-
-    static class BookmarkCellRenderer extends JLabel implements ListCellRenderer {
-
-        private ImageIcon icon;
-
-        public BookmarkCellRenderer() {
-            setOpaque(true);
-            icon = ImageProvider.get("dialogs", "bookmark");
-            setIcon(icon);
-        }
-
-        protected void renderColor(boolean selected) {
-            if (selected) {
-                setBackground(UIManager.getColor("List.selectionBackground"));
-                setForeground(UIManager.getColor("List.selectionForeground"));
-            } else {
-                setBackground(UIManager.getColor("List.background"));
-                setForeground(UIManager.getColor("List.foreground"));
-            }
-        }
-
-        protected String buildToolTipText(Bookmark b) {
-            Bounds area = b.getArea();
-            StringBuffer sb = new StringBuffer();
-            sb.append("<html>min[latitude,longitude]=<strong>[")
-            .append(area.getMinLat()).append(",").append(area.getMinLon()).append("]</strong>")
-            .append("<br>")
-            .append("max[latitude,longitude]=<strong>[")
-            .append(area.getMaxLat()).append(",").append(area.getMaxLon()).append("]</strong>")
-            .append("</html>");
-            return sb.toString();
-
-        }
-        @Override
-        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
-                boolean cellHasFocus) {
-
-            Bookmark b = (Bookmark) value;
-            renderColor(isSelected);
-            setText(b.getName());
-            setToolTipText(buildToolTipText(b));
-            return this;
-        }
-    }
-}
Index: trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java	(revision 6340)
@@ -35,4 +35,5 @@
 import org.openstreetmap.josm.gui.help.HelpBrowser;
 import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
Index: trunk/src/org/openstreetmap/josm/gui/JMultilineLabel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/JMultilineLabel.java	(revision 6339)
+++ 	(revision )
@@ -1,85 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-
-package org.openstreetmap.josm.gui;
-
-import java.awt.Dimension;
-import java.awt.Rectangle;
-
-import javax.swing.JLabel;
-import javax.swing.plaf.basic.BasicHTML;
-import javax.swing.text.View;
-
-/**
- * Creates a normal label that will wrap its contents if there less width than
- * required to print it in one line. Additionally the maximum width of the text
- * can be set using <code>setMaxWidth</code>.
- *
- * Note that this won't work if JMultilineLabel is put into a JScrollBox or
- * similar as the bounds will never change. Instead scrollbars will be displayed.
- */
-public class JMultilineLabel extends JLabel {
-    private int maxWidth = Integer.MAX_VALUE;
-    private Rectangle oldbounds = null;
-    private Dimension oldPreferred = null;
-
-    /**
-     * Constructs a normal label but adds HTML tags if not already done so.
-     * Supports both newline characters (<code>\n</code>) as well as the HTML
-     * <code>&lt;br&gt;</code> to insert new lines.
-     *
-     * Use setMaxWidth to limit the width of the label.
-     * @param text
-     */
-    public JMultilineLabel(String text)
-    {
-        super();
-        text = text.trim().replaceAll("\n", "<br>");
-        if(!text.startsWith("<html>")) {
-            text = "<html>" + text + "</html>";
-        }
-        super.setText(text);
-    }
-
-    /**
-     * Set the maximum width. Use this method instead of setMaximumSize because
-     * this saves a little bit of overhead and is actually taken into account.
-     *
-     * @param width
-     */
-    public void setMaxWidth(int width) {
-        this.maxWidth = width;
-    }
-
-    /**
-     * Tries to determine a suitable height for the given contents and return
-     * that dimension.
-     */
-    @Override
-    public Dimension getPreferredSize() {
-        // Without this check it will result in an infinite loop calling
-        // getPreferredSize. Remember the old bounds and only recalculate if
-        // the size actually changed.
-        if (this.getBounds().equals(oldbounds) && oldPreferred != null) {
-            return oldPreferred;
-        }
-        oldbounds = this.getBounds();
-
-        Dimension superPreferred = super.getPreferredSize();
-        // Make it not larger than required
-        int width = Math.min(superPreferred.width, maxWidth);
-
-        // Calculate suitable width and height
-        final View v = (View) super.getClientProperty(BasicHTML.propertyKey);
-
-        if (v == null) {
-            return superPreferred;
-        }
-
-        v.setSize(width, 0);
-        int w = (int) Math.ceil(v.getPreferredSpan(View.X_AXIS));
-        int h = (int) Math.ceil(v.getPreferredSpan(View.Y_AXIS));
-
-        oldPreferred = new Dimension(w, h);
-        return oldPreferred;
-    }
-}
Index: trunk/src/org/openstreetmap/josm/gui/MultiSplitLayout.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MultiSplitLayout.java	(revision 6339)
+++ 	(revision )
@@ -1,1319 +1,0 @@
-/*
- * $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.openstreetmap.josm.gui;
-
-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.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;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.tools.Utils;
-
-/**
- * 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 - SwingX
- * @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
-     */
-    @Override
-    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
-     */
-    @Override
-    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);
-    }
-
-    @Override
-    public Dimension preferredLayoutSize(Container parent) {
-        Dimension size = preferredNodeSize(getModel());
-        return sizeWithInsets(parent, size);
-    }
-
-    @Override
-    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();
-
-        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() / 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() / 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 + 0.000000001) { /* add some epsilon to a double check */
-                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.
-     */
-    @Override
-    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.
-         *
-         * This method isn't called getParent(), in order to avoid problems
-         * with recursive object creation when using XmlDecoder.
-         *
-         * @return the value of the parent property.
-         * @see #parent_set
-         */
-        public Split parent_get() { return parent; }
-
-        /**
-         * Set the value of this Node's parent property.  The default
-         * value of this property is null.
-         *
-         * This method isn't called setParent(), in order to avoid problems
-         * with recursive object creation when using XmlEncoder.
-         *
-         * @param parent a Split or null
-         * @see #parent_get
-         */
-        public void parent_set(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 = parent_get();
-            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 #parent_get
-         */
-        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 #parent_get
-         */
-        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.parent_set(null);
-            }
-            this.children = new ArrayList<Node>(children);
-            for(Node child : this.children) {
-                child.parent_set(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;
-        }
-
-        @Override
-        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;
-        }
-
-        @Override
-        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 = parent_get();
-            return (parent != null) ? parent.isRowLayout() : false;
-        }
-
-        /**
-         * Dividers can't have a weight, they don't grow or shrink.
-         * @throws UnsupportedOperationException
-         */
-        @Override
-        public void setWeight(double weight) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        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.isEmpty()) {
-            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) {
-            Main.error(e);
-        }
-        finally {
-            Utils.close(r);
-        }
-        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));
-    }
-}
Index: trunk/src/org/openstreetmap/josm/gui/MultiSplitPane.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MultiSplitPane.java	(revision 6339)
+++ 	(revision )
@@ -1,408 +1,0 @@
-/*
- * $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.openstreetmap.josm.gui;
-
-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.openstreetmap.josm.gui.MultiSplitLayout.Divider;
-import org.openstreetmap.josm.gui.MultiSplitLayout.Node;
-
-/**
- *
- * <p>
- * All properties in this class are bound: when a properties value
- * is changed, all PropertyChangeListeners are fired.
- *
- * @author Hans Muller - SwingX
- */
-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 {
-        @Override
-        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}
-     */
-    @Override
-    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 {
-
-        @Override
-        public void mouseEntered(MouseEvent e) {
-            updateCursor(e.getX(), e.getY(), true);
-        }
-
-        @Override
-        public void mouseMoved(MouseEvent e) {
-            updateCursor(e.getX(), e.getY(), true);
-        }
-
-        @Override
-        public void mouseExited(MouseEvent e) {
-            updateCursor(e.getX(), e.getY(), false);
-        }
-
-        @Override
-        public void mousePressed(MouseEvent e) {
-            startDrag(e.getX(), e.getY());
-        }
-        @Override
-        public void mouseReleased(MouseEvent e) {
-            finishDrag(e.getX(), e.getY());
-        }
-        @Override
-        public void mouseDragged(MouseEvent e) {
-            updateDrag(e.getX(), e.getY());
-        }
-        @Override
-        public void keyPressed(KeyEvent e) {
-            if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
-                cancelDrag();
-            }
-        }
-        @Override
-        public void keyReleased(KeyEvent e) { }
-        @Override
-        public void keyTyped(KeyEvent e) { }
-    }
-
-    @Override
-    public AccessibleContext getAccessibleContext() {
-        if( accessibleContext == null ) {
-            accessibleContext = new AccessibleMultiSplitPane();
-        }
-        return accessibleContext;
-    }
-
-    protected class AccessibleMultiSplitPane extends AccessibleJPanel {
-        @Override
-        public AccessibleRole getAccessibleRole() {
-            return AccessibleRole.SPLIT_PANE;
-        }
-    }
-}
Index: trunk/src/org/openstreetmap/josm/gui/Notification.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/Notification.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/Notification.java	(revision 6340)
@@ -9,4 +9,5 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 
 /**
Index: trunk/src/org/openstreetmap/josm/gui/actionsupport/AlignImageryPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/actionsupport/AlignImageryPanel.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/actionsupport/AlignImageryPanel.java	(revision 6340)
@@ -19,7 +19,7 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.widgets.UrlLabel;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.UrlLabel;
 
 /**
Index: trunk/src/org/openstreetmap/josm/gui/conflict/tags/RelationMemberConflictResolver.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/conflict/tags/RelationMemberConflictResolver.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/conflict/tags/RelationMemberConflictResolver.java	(revision 6340)
@@ -32,7 +32,7 @@
 import org.openstreetmap.josm.command.Command;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.tools.ImageProvider;
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/DialogsPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/DialogsPanel.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/DialogsPanel.java	(revision 6340)
@@ -10,9 +10,9 @@
 import javax.swing.JSplitPane;
 
-import org.openstreetmap.josm.gui.MultiSplitLayout.Divider;
-import org.openstreetmap.josm.gui.MultiSplitLayout.Leaf;
-import org.openstreetmap.josm.gui.MultiSplitLayout.Node;
-import org.openstreetmap.josm.gui.MultiSplitLayout.Split;
-import org.openstreetmap.josm.gui.MultiSplitPane;
+import org.openstreetmap.josm.gui.widgets.MultiSplitPane;
+import org.openstreetmap.josm.gui.widgets.MultiSplitLayout.Divider;
+import org.openstreetmap.josm.gui.widgets.MultiSplitLayout.Leaf;
+import org.openstreetmap.josm.gui.widgets.MultiSplitLayout.Node;
+import org.openstreetmap.josm.gui.widgets.MultiSplitLayout.Split;
 import org.openstreetmap.josm.tools.Destroyable;
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentPanel.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentPanel.java	(revision 6340)
@@ -40,5 +40,4 @@
 import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.gui.MapView.EditLayerChangeListener;
@@ -48,4 +47,5 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
 import org.openstreetmap.josm.tools.BugReportExceptionHandler;
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/AdvancedChangesetQueryPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/AdvancedChangesetQueryPanel.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/AdvancedChangesetQueryPanel.java	(revision 6340)
@@ -29,9 +29,9 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.JosmUserIdentityManager;
 import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
 import org.openstreetmap.josm.gui.widgets.BoundingBoxSelectionPanel;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
 import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/BasicChangesetQueryPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/BasicChangesetQueryPanel.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/query/BasicChangesetQueryPanel.java	(revision 6340)
@@ -21,7 +21,7 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.JosmUserIdentityManager;
 import org.openstreetmap.josm.gui.widgets.HtmlPanel;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.io.ChangesetQuery;
 
Index: trunk/src/org/openstreetmap/josm/gui/download/BookmarkList.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/BookmarkList.java	(revision 6340)
+++ trunk/src/org/openstreetmap/josm/gui/download/BookmarkList.java	(revision 6340)
@@ -0,0 +1,242 @@
+// License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.gui.download;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.swing.DefaultListModel;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.ListCellRenderer;
+import javax.swing.UIManager;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * List class that read and save its content from the bookmark file.
+ * @author imi
+ */
+public class BookmarkList extends JList {
+
+    /**
+     * Class holding one bookmarkentry.
+     * @author imi
+     */
+    public static class Bookmark implements Comparable<Bookmark> {
+        private String name;
+        private Bounds area;
+
+        public Bookmark(Collection<String> list) throws NumberFormatException, IllegalArgumentException {
+            List<String> array = new ArrayList<String>(list);
+            if(array.size() < 5)
+                throw new IllegalArgumentException(tr("Wrong number of arguments for bookmark"));
+            name = array.get(0);
+            area = new Bounds(Double.parseDouble(array.get(1)), Double.parseDouble(array.get(2)),
+                              Double.parseDouble(array.get(3)), Double.parseDouble(array.get(4)));
+        }
+
+        /**
+         * Constructs a new {@code Bookmark}.
+         */
+        public Bookmark() {
+            area = null;
+            name = null;
+        }
+
+        public Bookmark(Bounds area) {
+            this.area = area;
+        }
+
+        @Override public String toString() {
+            return name;
+        }
+
+        @Override
+        public int compareTo(Bookmark b) {
+            return name.toLowerCase().compareTo(b.name.toLowerCase());
+        }
+
+        public Bounds getArea() {
+            return area;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public void setArea(Bounds area) {
+            this.area = area;
+        }
+    }
+
+    /**
+     * Create a bookmark list as well as the Buttons add and remove.
+     */
+    public BookmarkList() {
+        setModel(new DefaultListModel());
+        load();
+        setVisibleRowCount(7);
+        setCellRenderer(new BookmarkCellRenderer());
+    }
+
+    /**
+     * Loads the bookmarks from file.
+     */
+    public void load() {
+        DefaultListModel model = (DefaultListModel)getModel();
+        model.removeAllElements();
+        Collection<Collection<String>> args = Main.pref.getArray("bookmarks", null);
+        if(args != null) {
+            LinkedList<Bookmark> bookmarks = new LinkedList<Bookmark>();
+            for(Collection<String> entry : args) {
+                try {
+                    bookmarks.add(new Bookmark(entry));
+                }
+                catch (Exception e) {
+                    Main.error(tr("Error reading bookmark entry: %s", e.getMessage()));
+                }
+            }
+            Collections.sort(bookmarks);
+            for (Bookmark b : bookmarks) {
+                model.addElement(b);
+            }
+        }
+        else if(!Main.applet) { /* FIXME: remove else clause after spring 2011, but fix windows installer before */
+            File bookmarkFile = new File(Main.pref.getPreferencesDir(),"bookmarks");
+            try {
+                LinkedList<Bookmark> bookmarks = new LinkedList<Bookmark>();
+                if (bookmarkFile.exists()) {
+                    Main.info("Try loading obsolete bookmarks file");
+                    BufferedReader in = new BufferedReader(new InputStreamReader(
+                            new FileInputStream(bookmarkFile), "utf-8"));
+
+                    for (String line = in.readLine(); line != null; line = in.readLine()) {
+                        Matcher m = Pattern.compile("^(.+)[,\u001e](-?\\d+.\\d+)[,\u001e](-?\\d+.\\d+)[,\u001e](-?\\d+.\\d+)[,\u001e](-?\\d+.\\d+)$").matcher(line);
+                        if (!m.matches() || m.groupCount() != 5) {
+                            Main.error(tr("Unexpected line ''{0}'' in bookmark file ''{1}''",line, bookmarkFile.toString()));
+                            continue;
+                        }
+                        Bookmark b = new Bookmark();
+                        b.setName(m.group(1));
+                        double[] values= new double[4];
+                        for (int i = 0; i < 4; ++i) {
+                            try {
+                                values[i] = Double.parseDouble(m.group(i+2));
+                            } catch (NumberFormatException e) {
+                                Main.error(tr("Illegal double value ''{0}'' on line ''{1}'' in bookmark file ''{2}''",m.group(i+2),line, bookmarkFile.toString()));
+                                continue;
+                            }
+                        }
+                        b.setArea(new Bounds(values));
+                        bookmarks.add(b);
+                    }
+                    Utils.close(in);
+                    Collections.sort(bookmarks);
+                    for (Bookmark b : bookmarks) {
+                        model.addElement(b);
+                    }
+                    save();
+                    Main.info("Removing obsolete bookmarks file");
+                    bookmarkFile.delete();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+                JOptionPane.showMessageDialog(
+                        Main.parent,
+                        tr("<html>Could not read bookmarks from<br>''{0}''<br>Error was: {1}</html>",
+                                bookmarkFile.toString(),
+                                e.getMessage()
+                        ),
+                        tr("Error"),
+                        JOptionPane.ERROR_MESSAGE
+                );
+            }
+        }
+    }
+
+    /**
+     * Save all bookmarks to the preferences file
+     */
+    public void save() {
+        LinkedList<Collection<String>> coll = new LinkedList<Collection<String>>();
+        for (Object o : ((DefaultListModel)getModel()).toArray()) {
+            String[] array = new String[5];
+            Bookmark b = (Bookmark)o;
+            array[0] = b.getName();
+            Bounds area = b.getArea();
+            array[1] = String.valueOf(area.getMinLat());
+            array[2] = String.valueOf(area.getMinLon());
+            array[3] = String.valueOf(area.getMaxLat());
+            array[4] = String.valueOf(area.getMaxLon());
+            coll.add(Arrays.asList(array));
+        }
+        Main.pref.putArray("bookmarks", coll);
+    }
+
+    static class BookmarkCellRenderer extends JLabel implements ListCellRenderer {
+
+        private ImageIcon icon;
+
+        public BookmarkCellRenderer() {
+            setOpaque(true);
+            icon = ImageProvider.get("dialogs", "bookmark");
+            setIcon(icon);
+        }
+
+        protected void renderColor(boolean selected) {
+            if (selected) {
+                setBackground(UIManager.getColor("List.selectionBackground"));
+                setForeground(UIManager.getColor("List.selectionForeground"));
+            } else {
+                setBackground(UIManager.getColor("List.background"));
+                setForeground(UIManager.getColor("List.foreground"));
+            }
+        }
+
+        protected String buildToolTipText(Bookmark b) {
+            Bounds area = b.getArea();
+            StringBuffer sb = new StringBuffer();
+            sb.append("<html>min[latitude,longitude]=<strong>[")
+            .append(area.getMinLat()).append(",").append(area.getMinLon()).append("]</strong>")
+            .append("<br>")
+            .append("max[latitude,longitude]=<strong>[")
+            .append(area.getMaxLat()).append(",").append(area.getMaxLon()).append("]</strong>")
+            .append("</html>");
+            return sb.toString();
+
+        }
+        @Override
+        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
+                boolean cellHasFocus) {
+
+            Bookmark b = (Bookmark) value;
+            renderColor(isSelected);
+            setText(b.getName());
+            setToolTipText(buildToolTipText(b));
+            return this;
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/download/BookmarkSelection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/BookmarkSelection.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/download/BookmarkSelection.java	(revision 6340)
@@ -23,7 +23,6 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.gui.BookmarkList;
-import org.openstreetmap.josm.gui.BookmarkList.Bookmark;
-import org.openstreetmap.josm.gui.JMultilineLabel;
+import org.openstreetmap.josm.gui.download.BookmarkList.Bookmark;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.JosmTextArea;
 import org.openstreetmap.josm.tools.ImageProvider;
Index: trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/history/VersionInfoPanel.java	(revision 6340)
@@ -22,9 +22,9 @@
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.JosmUserIdentityManager;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
+import org.openstreetmap.josm.gui.widgets.UrlLabel;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
-import org.openstreetmap.josm.tools.UrlLabel;
 
 /**
Index: trunk/src/org/openstreetmap/josm/gui/io/ChangesetManagementPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/ChangesetManagementPanel.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/io/ChangesetManagementPanel.java	(revision 6340)
@@ -26,5 +26,5 @@
 import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.data.osm.ChangesetCache;
-import org.openstreetmap.josm.gui.JMultilineLabel;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.JosmComboBox;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
Index: trunk/src/org/openstreetmap/josm/gui/io/CredentialDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/CredentialDialog.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/io/CredentialDialog.java	(revision 6340)
@@ -30,9 +30,9 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.SideButton;
 import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
 import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.preferences.server.ProxyPreferencesPanel;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.JosmPasswordField;
 import org.openstreetmap.josm.io.OsmApi;
Index: trunk/src/org/openstreetmap/josm/gui/io/UploadStrategySelectionPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/UploadStrategySelectionPanel.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/io/UploadStrategySelectionPanel.java	(revision 6340)
@@ -30,7 +30,7 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
 
Index: trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java	(revision 6340)
@@ -39,7 +39,7 @@
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
 import org.openstreetmap.josm.gui.MenuScroller;
+import org.openstreetmap.josm.gui.widgets.UrlLabel;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.UrlLabel;
 
 public abstract class ImageryLayer extends Layer {
Index: trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerAction.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerAction.java	(revision 6340)
@@ -25,8 +25,8 @@
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.widgets.UrlLabel;
 import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.UrlLabel;
 
 public class ConvertToDataLayerAction extends AbstractAction {
Index: trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticAuthorizationUI.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticAuthorizationUI.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticAuthorizationUI.java	(revision 6340)
@@ -31,5 +31,4 @@
 import org.openstreetmap.josm.data.oauth.OAuthToken;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.SideButton;
@@ -38,4 +37,5 @@
 import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
 import org.openstreetmap.josm.gui.widgets.HtmlPanel;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.JosmPasswordField;
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
Index: trunk/src/org/openstreetmap/josm/gui/oauth/SemiAutomaticAuthorizationUI.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/SemiAutomaticAuthorizationUI.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/SemiAutomaticAuthorizationUI.java	(revision 6340)
@@ -24,8 +24,8 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.oauth.OAuthToken;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.SideButton;
 import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
 import org.openstreetmap.josm.gui.widgets.HtmlPanel;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.OpenBrowser;
Index: trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginUpdatePolicyPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginUpdatePolicyPanel.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginUpdatePolicyPanel.java	(revision 6340)
@@ -19,5 +19,5 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.JMultilineLabel;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
 import org.openstreetmap.josm.plugins.PluginHandler;
Index: trunk/src/org/openstreetmap/josm/gui/preferences/server/OAuthAuthenticationPreferencesPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/server/OAuthAuthenticationPreferencesPanel.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/server/OAuthAuthenticationPreferencesPanel.java	(revision 6340)
@@ -26,5 +26,4 @@
 import org.openstreetmap.josm.data.oauth.OAuthParameters;
 import org.openstreetmap.josm.data.oauth.OAuthToken;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.SideButton;
 import org.openstreetmap.josm.gui.oauth.AdvancedOAuthPropertiesPanel;
@@ -33,4 +32,5 @@
 import org.openstreetmap.josm.io.auth.CredentialsManager;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
 
Index: trunk/src/org/openstreetmap/josm/gui/preferences/server/ProxyPreferencesPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/server/ProxyPreferencesPanel.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/server/ProxyPreferencesPanel.java	(revision 6340)
@@ -25,6 +25,6 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.JosmPasswordField;
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
Index: trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPresetItems.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPresetItems.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPresetItems.java	(revision 6340)
@@ -54,7 +54,7 @@
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.gui.widgets.QuadStateCheckBox;
+import org.openstreetmap.josm.gui.widgets.UrlLabel;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.UrlLabel;
 import org.openstreetmap.josm.tools.Utils;
 import org.xml.sax.SAXException;
Index: trunk/src/org/openstreetmap/josm/gui/widgets/BoundingBoxSelectionPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/BoundingBoxSelectionPanel.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/BoundingBoxSelectionPanel.java	(revision 6340)
@@ -19,5 +19,4 @@
 import org.openstreetmap.josm.data.coor.CoordinateFormat;
 import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.OsmUrlToBounds;
Index: trunk/src/org/openstreetmap/josm/gui/widgets/JMultilineLabel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/JMultilineLabel.java	(revision 6340)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/JMultilineLabel.java	(revision 6340)
@@ -0,0 +1,85 @@
+// License: GPL. For details, see LICENSE file.
+
+package org.openstreetmap.josm.gui.widgets;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+
+import javax.swing.JLabel;
+import javax.swing.plaf.basic.BasicHTML;
+import javax.swing.text.View;
+
+/**
+ * Creates a normal label that will wrap its contents if there less width than
+ * required to print it in one line. Additionally the maximum width of the text
+ * can be set using <code>setMaxWidth</code>.
+ *
+ * Note that this won't work if JMultilineLabel is put into a JScrollBox or
+ * similar as the bounds will never change. Instead scrollbars will be displayed.
+ */
+public class JMultilineLabel extends JLabel {
+    private int maxWidth = Integer.MAX_VALUE;
+    private Rectangle oldbounds = null;
+    private Dimension oldPreferred = null;
+
+    /**
+     * Constructs a normal label but adds HTML tags if not already done so.
+     * Supports both newline characters (<code>\n</code>) as well as the HTML
+     * <code>&lt;br&gt;</code> to insert new lines.
+     *
+     * Use setMaxWidth to limit the width of the label.
+     * @param text
+     */
+    public JMultilineLabel(String text)
+    {
+        super();
+        text = text.trim().replaceAll("\n", "<br>");
+        if(!text.startsWith("<html>")) {
+            text = "<html>" + text + "</html>";
+        }
+        super.setText(text);
+    }
+
+    /**
+     * Set the maximum width. Use this method instead of setMaximumSize because
+     * this saves a little bit of overhead and is actually taken into account.
+     *
+     * @param width
+     */
+    public void setMaxWidth(int width) {
+        this.maxWidth = width;
+    }
+
+    /**
+     * Tries to determine a suitable height for the given contents and return
+     * that dimension.
+     */
+    @Override
+    public Dimension getPreferredSize() {
+        // Without this check it will result in an infinite loop calling
+        // getPreferredSize. Remember the old bounds and only recalculate if
+        // the size actually changed.
+        if (this.getBounds().equals(oldbounds) && oldPreferred != null) {
+            return oldPreferred;
+        }
+        oldbounds = this.getBounds();
+
+        Dimension superPreferred = super.getPreferredSize();
+        // Make it not larger than required
+        int width = Math.min(superPreferred.width, maxWidth);
+
+        // Calculate suitable width and height
+        final View v = (View) super.getClientProperty(BasicHTML.propertyKey);
+
+        if (v == null) {
+            return superPreferred;
+        }
+
+        v.setSize(width, 0);
+        int w = (int) Math.ceil(v.getPreferredSpan(View.X_AXIS));
+        int h = (int) Math.ceil(v.getPreferredSpan(View.Y_AXIS));
+
+        oldPreferred = new Dimension(w, h);
+        return oldPreferred;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/widgets/MultiSplitLayout.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/MultiSplitLayout.java	(revision 6340)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/MultiSplitLayout.java	(revision 6340)
@@ -0,0 +1,1319 @@
+/*
+ * $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.openstreetmap.josm.gui.widgets;
+
+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.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;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * 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 - SwingX
+ * @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
+     */
+    @Override
+    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
+     */
+    @Override
+    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);
+    }
+
+    @Override
+    public Dimension preferredLayoutSize(Container parent) {
+        Dimension size = preferredNodeSize(getModel());
+        return sizeWithInsets(parent, size);
+    }
+
+    @Override
+    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();
+
+        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() / 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() / 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 + 0.000000001) { /* add some epsilon to a double check */
+                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.
+     */
+    @Override
+    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.
+         *
+         * This method isn't called getParent(), in order to avoid problems
+         * with recursive object creation when using XmlDecoder.
+         *
+         * @return the value of the parent property.
+         * @see #parent_set
+         */
+        public Split parent_get() { return parent; }
+
+        /**
+         * Set the value of this Node's parent property.  The default
+         * value of this property is null.
+         *
+         * This method isn't called setParent(), in order to avoid problems
+         * with recursive object creation when using XmlEncoder.
+         *
+         * @param parent a Split or null
+         * @see #parent_get
+         */
+        public void parent_set(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 = parent_get();
+            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 #parent_get
+         */
+        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 #parent_get
+         */
+        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.parent_set(null);
+            }
+            this.children = new ArrayList<Node>(children);
+            for(Node child : this.children) {
+                child.parent_set(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;
+        }
+
+        @Override
+        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;
+        }
+
+        @Override
+        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 = parent_get();
+            return (parent != null) ? parent.isRowLayout() : false;
+        }
+
+        /**
+         * Dividers can't have a weight, they don't grow or shrink.
+         * @throws UnsupportedOperationException
+         */
+        @Override
+        public void setWeight(double weight) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        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.isEmpty()) {
+            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) {
+            Main.error(e);
+        }
+        finally {
+            Utils.close(r);
+        }
+        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));
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/widgets/MultiSplitPane.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/MultiSplitPane.java	(revision 6340)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/MultiSplitPane.java	(revision 6340)
@@ -0,0 +1,409 @@
+/*
+ * $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.openstreetmap.josm.gui.widgets;
+
+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.openstreetmap.josm.gui.widgets.MultiSplitLayout.Divider;
+import org.openstreetmap.josm.gui.widgets.MultiSplitLayout.Node;
+
+/**
+ *
+ * <p>
+ * All properties in this class are bound: when a properties value
+ * is changed, all PropertyChangeListeners are fired.
+ *
+ * @author Hans Muller - SwingX
+ */
+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 {
+        @Override
+        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}
+     */
+    @Override
+    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 {
+
+        @Override
+        public void mouseEntered(MouseEvent e) {
+            updateCursor(e.getX(), e.getY(), true);
+        }
+
+        @Override
+        public void mouseMoved(MouseEvent e) {
+            updateCursor(e.getX(), e.getY(), true);
+        }
+
+        @Override
+        public void mouseExited(MouseEvent e) {
+            updateCursor(e.getX(), e.getY(), false);
+        }
+
+        @Override
+        public void mousePressed(MouseEvent e) {
+            startDrag(e.getX(), e.getY());
+        }
+        @Override
+        public void mouseReleased(MouseEvent e) {
+            finishDrag(e.getX(), e.getY());
+        }
+        @Override
+        public void mouseDragged(MouseEvent e) {
+            updateDrag(e.getX(), e.getY());
+        }
+        @Override
+        public void keyPressed(KeyEvent e) {
+            if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+                cancelDrag();
+            }
+        }
+        @Override
+        public void keyReleased(KeyEvent e) { }
+        @Override
+        public void keyTyped(KeyEvent e) { }
+    }
+
+    @Override
+    public AccessibleContext getAccessibleContext() {
+        if( accessibleContext == null ) {
+            accessibleContext = new AccessibleMultiSplitPane();
+        }
+        return accessibleContext;
+    }
+
+    protected class AccessibleMultiSplitPane extends AccessibleJPanel {
+        @Override
+        public AccessibleRole getAccessibleRole() {
+            return AccessibleRole.SPLIT_PANE;
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/widgets/UrlLabel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/UrlLabel.java	(revision 6340)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/UrlLabel.java	(revision 6340)
@@ -0,0 +1,107 @@
+// License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.gui.widgets;
+
+import java.awt.Cursor;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+import javax.swing.JLabel;
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.tools.OpenBrowser;
+import org.openstreetmap.josm.tools.Utils;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+/**
+ * Label that contains a clickable link.
+ * @author Imi
+ * 5050: Simplifications by Zverikk included by akks
+ */
+public class UrlLabel extends JLabel implements MouseListener {
+
+    private String url = "";
+    private String description = "";
+
+    /**
+     * Constructs a new {@code UrlLabel}.
+     */
+    public UrlLabel() {
+        addMouseListener(this);
+        setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+    }
+
+    public UrlLabel(String url) {
+        this (url, url, 0);
+    }
+
+    public UrlLabel(String url, int fontPlus) {
+        this (url, url, fontPlus);
+    }
+
+    public UrlLabel(String url, String description) {
+        this (url, url, 0);
+    }
+
+    public UrlLabel(String url, String description, int fontPlus) {
+        this();
+        setUrl(url);
+        setDescription(description);
+        if (fontPlus!=0) {
+            setFont(getFont().deriveFont(0, getFont().getSize()+fontPlus));
+        }
+        refresh();
+    }
+
+    protected void refresh() {
+        if (url != null) {
+            setText("<html><a href=\""+url+"\">"+description+"</a></html>");
+            setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+            setToolTipText(String.format("<html>%s<br/>%s</html>", url, tr("Right click = copy to clipboard")));
+        } else {
+            setText("<html>" + description + "</html>");
+            setCursor(null);
+            setToolTipText(null);
+        }
+    }
+
+    /**
+     * Sets the URL to be visited if the user clicks on this URL label. If null, the
+     * label turns into a normal label without hyperlink.
+     *
+     * @param url the url. Can be null.
+     */
+    public void setUrl(String url) {
+        this.url = url;
+        refresh();
+    }
+
+    /**
+     * Sets the text part of the URL label. Defaults to the empty string if description is null.
+     *
+     * @param description the description
+     */
+    public void setDescription(String description) {
+        this.description = description == null? "" : description;
+        this.description = this.description.replace("&", "&amp;").replace(">", "&gt;").replace("<", "&lt;");
+        refresh();
+    }
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+        if( SwingUtilities.isLeftMouseButton(e) ) {
+            OpenBrowser.displayUrl(url);
+        } else if( SwingUtilities.isRightMouseButton(e) ) {
+            Utils.copyToClipboard(url);
+        }
+    }
+    @Override
+    public void mousePressed(MouseEvent e) {    }
+    @Override
+    public void mouseEntered(MouseEvent e) {    }
+    @Override
+    public void mouseExited(MouseEvent e) {    }
+    @Override
+    public void mouseReleased(MouseEvent e) {    }
+
+}
Index: trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java	(revision 6340)
@@ -53,5 +53,4 @@
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
-import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.download.DownloadSelection;
 import org.openstreetmap.josm.gui.help.HelpUtil;
@@ -59,4 +58,5 @@
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.JosmTextArea;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
Index: trunk/src/org/openstreetmap/josm/tools/BugReportExceptionHandler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/BugReportExceptionHandler.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/tools/BugReportExceptionHandler.java	(revision 6340)
@@ -24,6 +24,7 @@
 import org.openstreetmap.josm.actions.ShowStatusReportAction;
 import org.openstreetmap.josm.gui.ExtendedDialog;
-import org.openstreetmap.josm.gui.JMultilineLabel;
+import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.JosmTextArea;
+import org.openstreetmap.josm.gui.widgets.UrlLabel;
 import org.openstreetmap.josm.plugins.PluginHandler;
 
Index: trunk/src/org/openstreetmap/josm/tools/TextTagParser.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/TextTagParser.java	(revision 6339)
+++ trunk/src/org/openstreetmap/josm/tools/TextTagParser.java	(revision 6340)
@@ -20,4 +20,5 @@
 import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.gui.widgets.UrlLabel;
 import org.openstreetmap.josm.io.XmlWriter;
 import org.openstreetmap.josm.tools.LanguageInfo.LocaleType;
Index: trunk/src/org/openstreetmap/josm/tools/UrlLabel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/UrlLabel.java	(revision 6339)
+++ 	(revision )
@@ -1,102 +1,0 @@
-// License: GPL. Copyright 2007 by Immanuel Scholz and others
-package org.openstreetmap.josm.tools;
-
-import java.awt.Cursor;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import javax.swing.JLabel;
-import javax.swing.SwingUtilities;
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-/**
- * Label that contains a clickable link.
- * @author Imi
- * 5050: Simplifications by Zverikk included by akks
- */
-public class UrlLabel extends JLabel implements MouseListener {
-
-    private String url = "";
-    private String description = "";
-
-    /**
-     * Constructs a new {@code UrlLabel}.
-     */
-    public UrlLabel() {
-        addMouseListener(this);
-        setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-    }
-
-    public UrlLabel(String url) {
-        this (url, url, 0);
-    }
-
-    public UrlLabel(String url, int fontPlus) {
-        this (url, url, fontPlus);
-    }
-
-    public UrlLabel(String url, String description) {
-        this (url, url, 0);
-    }
-
-    public UrlLabel(String url, String description, int fontPlus) {
-        this();
-        setUrl(url);
-        setDescription(description);
-        if (fontPlus!=0) {
-            setFont(getFont().deriveFont(0, getFont().getSize()+fontPlus));
-        }
-        refresh();
-    }
-
-    protected void refresh() {
-        if (url != null) {
-            setText("<html><a href=\""+url+"\">"+description+"</a></html>");
-            setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-            setToolTipText(String.format("<html>%s<br/>%s</html>", url, tr("Right click = copy to clipboard")));
-        } else {
-            setText("<html>" + description + "</html>");
-            setCursor(null);
-            setToolTipText(null);
-        }
-    }
-
-    /**
-     * Sets the URL to be visited if the user clicks on this URL label. If null, the
-     * label turns into a normal label without hyperlink.
-     *
-     * @param url the url. Can be null.
-     */
-    public void setUrl(String url) {
-        this.url = url;
-        refresh();
-    }
-
-    /**
-     * Sets the text part of the URL label. Defaults to the empty string if description is null.
-     *
-     * @param description the description
-     */
-    public void setDescription(String description) {
-        this.description = description == null? "" : description;
-        this.description = this.description.replace("&", "&amp;").replace(">", "&gt;").replace("<", "&lt;");
-        refresh();
-    }
-
-    @Override
-    public void mouseClicked(MouseEvent e) {
-        if( SwingUtilities.isLeftMouseButton(e) ) {
-            OpenBrowser.displayUrl(url);
-        } else if( SwingUtilities.isRightMouseButton(e) ) {
-            Utils.copyToClipboard(url);
-        }
-    }
-    @Override
-    public void mousePressed(MouseEvent e) {    }
-    @Override
-    public void mouseEntered(MouseEvent e) {    }
-    @Override
-    public void mouseExited(MouseEvent e) {    }
-    @Override
-    public void mouseReleased(MouseEvent e) {    }
-
-}
