/*
 * The contents of this file are subject to the terms of the Common Development
 * and Distribution License (the License). You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
 * or http://www.netbeans.org/cddl.txt.
 * 
 * When distributing Covered Code, include this CDDL Header Notice in each file
 * and include the License file at http://www.netbeans.org/cddl.txt.
 * If applicable, add the following below the CDDL Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 * Microsystems, Inc. All Rights Reserved.
 */
package org.openide.awt;

import org.openide.ErrorManager;
import org.openide.util.NbBundle;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import java.text.MessageFormat;

import java.util.Enumeration;
import java.util.ResourceBundle;
import java.util.Vector;

import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.accessibility.AccessibleRole;

import javax.swing.*;
import javax.swing.border.*;


/** The SplittedPanel widget is a Panel that can contain one or two components and
* place them side-by-side vertically or horizontally with a splitter in the middle.
* User can move the split point by dragging the splitter with mouse.
* The two components are accessed by add/remove methods with constraints value
* ADD_SPLITTER, ADD_FIRST, ADD_SECOND, ....
* The split position could be either absolute or proportional (according to the
* "Absolute" property setting) - in thwe absolute mode the split point remains same
* when resizing (i.e. the left/top component keeps its size and only the
* right/bottom component resizes), while in the proportional mode the splitPosition
* is a percentage of the width/height assigned to the left/top component.

* <P><TABLE BORDER COLS=3 WIDTH=100%>
* <TR><TH WIDTH=15%>Property<TH WIDTH=15%>Property Type<TH>Description
* <TR><TD> SplitType            <TD> int       <TD> The type of the splitting - HORIZONTAL, VERTICAL or NONE
* <TR><TD> SplitPosition        <TD> int       <TD> The position of the split point - either absolute position or number of percents
*                                                   according to the "Absolute" property settings, could be one of FIRST_PREFERRED or
*                                                   SECOND_PREFERRED, which means that the split point should be placed so that
*                                                   the first(left/top) resp. second (bottom/rignt) is sized according to its preferredSize
*                                                   (in this case the Absolute property setting is ignored)
* <TR><TD> SplitterType         <TD> int       <TD> The type of the component that renders the splitter - DEFAULT_SPLITTER, RAISED_SPLITTER,
*                                                   EMPTY_SPLITTER.
* <TR><TD> SplitterComponent    <TD> Component <TD> The component that renders the splitter. A custom component can be provided in addition to EMPTY_SPLITTER and RAISED-SPLITTER using this method.
* <TR><TD> SplitAbsolute        <TD> boolean   <TD> if true then the meaning of the SplitPosition is absolute points,
*                                                   otherwise the SplitPosition is a number of percents
* <TR><TD> SplitDragable        <TD> boolean   <TD> if true then the split point can be dragged using a mouse,
*                                                   otherwise the SplitPosition is fixed
* <TR><TD> SplitTypeChangeEnabled  <TD> boolean<TD> if true then the split type can be changed via popup menu commands
* <TR><TD> SwapPanesEnabled        <TD> boolean<TD> if true then the panes can be swapped via popup menu command
* </TABLE>
*
* @author  Ian Formanek
* @deprecated This class does nothing interesting that cannot be done with a JSplitPane.
*  Use a JSplitPane instead.
*/
public class SplittedPanel extends JComponent implements Accessible {
    /** generated Serialized Version UID */
    static final long serialVersionUID = 5058424218525927233L;

    /** constant for no split - only the first (left/top) component will be shown */
    public final static int NONE = 0;

    /** constant for vertical split */
    public final static int VERTICAL = 1;

    /** constant for horizontal split */
    public final static int HORIZONTAL = 2;

    /** constraints constant for adding a splitter */
    public static final Object ADD_SPLITTER = new Integer(0);

    /** constraints constant for adding a component to the first (left/top) pane */
    public static final Object ADD_FIRST = new Integer(1);

    /** constraints constant for adding a component to the second (right/bottom) pane */
    public static final Object ADD_SECOND = new Integer(2);

    /** constraints constant for adding a component to the left(top) pane (an alias for the ADD_FIRST constant) */
    public static final Object ADD_LEFT = ADD_FIRST;

    /** constraints constant for adding a component to the top(left) pane (an alias for the ADD_FIRST constant) */
    public static final Object ADD_TOP = ADD_FIRST;

    /** constraints constant for adding a component to the right(bottom) pane (an alias for the ADD_SECOND constant) */
    public static final Object ADD_RIGHT = ADD_SECOND;

    /** constraints constant for adding a component to the bottom(right) pane (an alias for the ADD_SECOND constant) */
    public static final Object ADD_BOTTOM = ADD_SECOND;

    /** constant for moving the split point so that the first (left/top) component is sized according to its preferredSize */
    public static final int FIRST_PREFERRED = -1;

    /** constant for moving the split point so that the second (right/bottom) component is sized according to its preferredSize */
    public static final int SECOND_PREFERRED = -2;

    /** constant for splitter component types - raised splitter*/
    public static final int RAISED_SPLITTER = 0;

    /** constant for splitter component types - empty splitter */
    public static final int EMPTY_SPLITTER = 1;

    /** constant for splitter component types - default splitter (raised)*/
    public static final int DEFAULT_SPLITTER = RAISED_SPLITTER;
    private static MessageFormat nameFormat = null;
    private static MessageFormat descriptionFormat = null;

    /** The default split type */
    private final static int DEFAULT_SPLIT_TYPE = HORIZONTAL;

    /** Save the last preferred setting (first or second). Double click reset the splitPosition to this value */
    private int resetPosition = FIRST_PREFERRED;

    /** Is popup menu enabled?*/
    private Boolean popupMenuEnabled;
    private boolean drawBumps;

    ///////////////////////
    // Private variables //
    ///////////////////////

    /** the first (left/top) component */
    private Component firstComponent = null;

    /** the second (right/bottom) component */
    private Component secondComponent = null;

    /** the splitter component */
    private Component splitter = null;

    /** the splitter component type */
    private int splitterType = DEFAULT_SPLITTER;

    /** the mouse adapter that does the dragging of the splitter*/
    private transient MouseListenerAdapter mouseAdapter;

    /** current split type */
    private int splitType = NONE;

    /** current split position */
    private int splitPosition = 50;
    private boolean absolute = false;
    private boolean dragable = true;
    private boolean continuousLayout = true;

    /** current enabled/disabled state of change of split type */
    private boolean splitTypeChangeEnabled = true;

    /** current enabled/disabled state of change of swapping panes */
    private boolean swapPanesEnabled = true;

    /** current keepSecondSame state - this has bigger priority than keepFirstSame  */
    private boolean keepSecondSame = false;

    /** current keepFirstSame state */
    private boolean keepFirstSame = false;
    transient private boolean splitIsChanging = false;
    private int dragPos = -1;

    /** true if the panes were swapped, false otherwise */
    private boolean panesSwapped = false;

    /** popup menu for setting vertical/horizontal splitting */
    transient private JPopupMenu popupMenu;

    /** The popup menu item */
    transient private JRadioButtonMenuItem verticalCMI;

    /** The popup menu item */
    transient private JRadioButtonMenuItem horizontalCMI;

    /** The popup menu item */
    transient private JMenuItem swapCMI;

    /** The popup menu item */
    transient private JMenuItem splitterCMI;

    /** A Vector of SplitChangeListeners */
    transient private Vector listeners;

    /** Accessible context */
    private AccessibleContext accessibleContext;

    /** Constructs a new empty SplittedPanel with no spliting.
    */
    public SplittedPanel() {
        splitter = new DefaultSplitter(getDefaultSplitterSize());
        accessibleContext = null;
        setLayout(new SplitLayout());
        add(splitter, ADD_SPLITTER);
        init();

        RuntimeException rte = new RuntimeException("SplittedPanel is deprecated.  Please use JSplitPane instead"); //NOI18N
        ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, rte);
    }

    /** Initializes the SplittedPanel */
    private void init() {
        setSplitterCursor();
        mouseAdapter = new MouseListenerAdapter();

        if (dragable) {
            splitter.addMouseMotionListener(mouseAdapter);
            splitter.addMouseListener(mouseAdapter);
            addSplitChangeListener(mouseAdapter);
        }

        initAccessible();
    }

    /** Updates splitting, too. */
    public void updateUI() {
        super.updateUI();
        updateSplitting();

        Object o = UIManager.get("nb.SplittedPanel.drawBumps");
        drawBumps = Boolean.TRUE.equals(o);
    }

    /** Updates the visual state and layout when the split state changes. */
    protected void updateSplitting() {
        if ((firstComponent != null) && (secondComponent != null)) {
            invalidate();
            firstComponent.invalidate();
            splitter.invalidate();
            secondComponent.invalidate();
            validate();
        }
    }

    /** Computes component sizes after performing the flip,
    * it means splitTypeChange */
    protected void computeSizesAfterFlip() {
        if ((firstComponent == null) || (secondComponent == null)) {
            return;
        }

        Dimension ourSize = getSize();
        int splitterSize;

        switch (splitType) {
        case VERTICAL:

            if (ourSize.width == 0) {
                break;
            }

            splitterSize = splitter.getPreferredSize().height;

            int newHeight = ((ourSize.height - splitterSize) * firstComponent.getSize().width) / ourSize.width;
            firstComponent.setSize(new Dimension(ourSize.width, newHeight));
            secondComponent.setSize(new Dimension(ourSize.width, ourSize.height - newHeight - splitterSize));

            break;

        case HORIZONTAL:

            if (ourSize.height == 0) {
                break;
            }

            splitterSize = splitter.getPreferredSize().width;

            int newWidth = ((ourSize.width - splitterSize) * firstComponent.getSize().height) / ourSize.height;
            firstComponent.setSize(new Dimension(newWidth, ourSize.height));
            secondComponent.setSize(new Dimension(ourSize.width - newWidth - splitterSize, ourSize.height));

            break;
        }
    }

    /** Updates the splitter's cursor according to the current SplittedPanel settings. */
    protected void setSplitterCursor() {
        if (dragable) {
            if (splitType == VERTICAL) {
                splitter.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
            } else {
                splitter.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
            }
        } else {
            splitter.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        }
    }

    /** Is popup menu on spliter enabled? See issue 25216.*/
    private boolean isPopupMenuEnabled() {
        if (popupMenuEnabled == null) {
            Object o = getClientProperty("popupMenuEnabled");

            if (o instanceof Boolean) {
                popupMenuEnabled = (Boolean) o;
            } else {
                popupMenuEnabled = Boolean.TRUE;
            }
        }

        return popupMenuEnabled.booleanValue();
    }

    /** Updates the splitter's popup menu. */
    protected void updatePopupMenu() {
        if (popupMenu == null) {
            popupMenu = new JPopupMenu();

            java.util.ResourceBundle awtBundle = NbBundle.getBundle(SplittedPanel.class);

            popupMenu.add(verticalCMI = new JRadioButtonMenuItem(awtBundle.getString("SplittedPanelVertical")));
            popupMenu.add(horizontalCMI = new JRadioButtonMenuItem(awtBundle.getString("SplittedPanelHorizontal")));
            popupMenu.add(new JSeparator());
            popupMenu.add(swapCMI = new JMenuItem(awtBundle.getString("SplittedPanelSwap")));
            popupMenu.add(new JSeparator());
            popupMenu.add(splitterCMI = new JMenuItem(awtBundle.getString("ResetSplitter")));

            ActionListener al = new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        if (horizontalCMI.equals(e.getSource())) {
                            setSplitType(HORIZONTAL);
                        } else {
                            setSplitType(VERTICAL);
                        }
                    }
                };

            verticalCMI.addActionListener(al);
            horizontalCMI.addActionListener(al);
            swapCMI.addActionListener(
                new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        swapPanes();
                    }
                }
            );
            splitterCMI.addActionListener(
                new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        resetSplitter();
                    }
                }
            );
        }

        if (splitType == VERTICAL) {
            verticalCMI.setSelected(true);
            horizontalCMI.setSelected(false);
        } else {
            verticalCMI.setSelected(false);
            horizontalCMI.setSelected(true);
        }

        if (splitTypeChangeEnabled) {
            verticalCMI.setEnabled(true);
            horizontalCMI.setEnabled(true);
        } else {
            verticalCMI.setEnabled(false);
            horizontalCMI.setEnabled(false);
        }

        if (swapPanesEnabled) {
            swapCMI.setEnabled(true);
        } else {
            swapCMI.setEnabled(false);
        }

        splitterCMI.setEnabled((getSplitPosition() != FIRST_PREFERRED) && (getSplitPosition() != SECOND_PREFERRED));
    }

    /** Reset split line to follow the largest name. */
    private void resetSplitter() {
        if ((getSplitPosition() != FIRST_PREFERRED) && (getSplitPosition() != SECOND_PREFERRED)) {
            setSplitPosition(resetPosition);

            if (splitterCMI != null) {
                splitterCMI.setEnabled(false);
            }
        }
    }

    /** Swaps the panes.*/
    public void swapPanes() {
        if (!swapPanesEnabled) {
            return;
        }

        if ((firstComponent == null) || (secondComponent == null)) {
            return;
        }

        splitIsChanging = true;
        panesSwapped = !panesSwapped;

        if (keepSecondSame) {
            keepSecondSame = false;
            keepFirstSame = true;
        } else if (keepFirstSame) {
            keepSecondSame = true;
            keepFirstSame = false;
        }

        Component aFirstComponent = firstComponent;
        Component aSecondComponent = secondComponent;

        remove(aFirstComponent);
        remove(aSecondComponent);
        add(aSecondComponent, ADD_FIRST);
        add(aFirstComponent, ADD_SECOND);
        updateSplitting();
        splitIsChanging = false;
    }

    /** @return true if the panes are swapped, false otherwise */
    public boolean getPanesSwapped() {
        return panesSwapped;
    }

    ///////////////////////////////
    // Property accessor methods //
    ///////////////////////////////

    /** Getter method for the SplitType property.
    * @return Current SplitType value.
    */
    public int getSplitType() {
        return splitType;
    }

    /** Setter method for the SplitType property.
    * @param value New SplitType value.
    */
    public void setSplitType(int value) {
        if (splitType == value) {
            return;
        }

        int oldSplitType = splitType;
        splitType = value;

        if ((oldSplitType != NONE) && (splitType != NONE)) {
            computeSizesAfterFlip();
        }

        setSplitterCursor();
        updateSplitting();
        updatePopupMenu();

        initAccessible();
    }

    /** Getter method for the SplitPosition property.
    * @return Current SplitPosition value.
    */
    public int getSplitPosition() {
        return splitPosition;
    }

    /** Setter method for the SplitPosition property.
    * @param value New SplitPosition value.
    */
    public void setSplitPosition(int value) {
        if (splitPosition == value) {
            return;
        }

        int oldValue = splitPosition;
        splitPosition = value;
        splitIsChanging = true;
        updateSplitting();
        fireSplitChange(oldValue, splitPosition);
        splitIsChanging = false;
    }

    /** Getter method for the SplitterType property.
    * @return Current SplitterType value.
    * @see #EMPTY_SPLITTER
    * @see #RAISED_SPLITTER
    * @see #DEFAULT_SPLITTER
    */
    public int getSplitterType() {
        return splitterType;
    }

    private int getDefaultSplitterSize() {
        Object o = UIManager.get("nb.SplittedPanel.dividerSize"); //NOI18N

        if (o != null) {
            return ((Integer) o).intValue();
        }

        o = UIManager.get("SplitPane.dividerSize"); //NOI18N

        if (o != null) {
            return ((Integer) o).intValue();
        }

        return 6;
    }

    /** Setter method for the SplitterType property.
    * @param type New SplitterType value.
    * @see #EMPTY_SPLITTER
    * @see #RAISED_SPLITTER
    * @see #DEFAULT_SPLITTER
    */
    public void setSplitterType(int type) {
        if (splitterType == type) {
            return;
        }

        splitterType = type;

        switch (splitterType) {
        case EMPTY_SPLITTER:
            splitter = new EmptySplitter();

            break;

        default:
        case RAISED_SPLITTER:
            splitter = new DefaultSplitter(getDefaultSplitterSize());

            break;
        }

        add(splitter, ADD_SPLITTER);
        updateSplitting();
    }

    /** Getter method for the SplitterComponent property.
    * @return Current SplitterComponent value.
    * @see #getSplitterType
    */
    public Component getSplitterComponent() {
        return splitter;
    }

    /** Setter method for the SplitterComponent property.
    * @param comp New SplitterComponent value.
    * @see #setSplitterType
    */
    public void setSplitterComponent(Component comp) {
        if (splitter == comp) {
            return;
        }

        if (dragable) {
            splitter.removeMouseMotionListener(mouseAdapter);
            splitter.removeMouseListener(mouseAdapter);
        }

        remove(splitter);
        splitter = comp;
        add(splitter, ADD_SPLITTER);

        if (dragable) {
            splitter.addMouseMotionListener(mouseAdapter);
            splitter.addMouseListener(mouseAdapter);
        }

        setSplitterCursor();
        updateSplitting();
    }

    /** Getter method for the SplitAbsolute property.
    * @return Current SplitAbsolute value.
    */
    public boolean isSplitAbsolute() {
        return absolute;
    }

    /** Setter method for the SplitAbsolute property.
    * @param value New SplitAbsolute value.
    */
    public void setSplitAbsolute(boolean value) {
        if (absolute == value) {
            return;
        }

        absolute = value;
        updateSplitting();
    }

    /** Getter method for the SplitDragable property.
    * @return Current SplitDragable value.
    */
    public boolean isSplitDragable() {
        return dragable;
    }

    /** Setter method for the Dragable property.
    * @param value New Dragable value.
    */
    public void setSplitDragable(boolean value) {
        if (dragable == value) {
            return;
        }

        dragable = value;

        if (dragable) {
            splitter.addMouseMotionListener(mouseAdapter);
            splitter.addMouseListener(mouseAdapter);
        } else {
            splitter.removeMouseMotionListener(mouseAdapter);
            splitter.removeMouseListener(mouseAdapter);
        }

        setSplitterCursor();
    }

    /** Getter method for the ContinuousLayout property.
    * @return Current ContinuousLayout value.
    */
    public boolean isContinuousLayout() {
        return continuousLayout;
    }

    /** Setter method for the ContinuousLayout property.
    * @param value New ContinuousLayout value.
    */
    public void setContinuousLayout(boolean value) {
        continuousLayout = value;
    }

    /** Getter method for the KeepFirstSame property.
    * @return Current KeepFirstSame value.
    */
    public boolean getKeepFirstSame() {
        return keepFirstSame;
    }

    /** Setter method for the KeepFirstSame property.
    * @param value New KeepFirstSame value.
    */
    public void setKeepFirstSame(boolean value) {
        keepFirstSame = value;
    }

    /** Getter method for the KeepSecondSame property.
    * @return Current KeepSecondSame value.
    */
    public boolean getKeepSecondSame() {
        return keepSecondSame;
    }

    /** Setter method for the KeepSecondSame property.
    * @param value New KeepSecondSame value.
    */
    public void setKeepSecondSame(boolean value) {
        keepSecondSame = value;
    }

    /** Getter method for the SplitTypeChangeEnabled property.
    * @return Current SplitTypeChangeEnabled value.
    */
    public boolean isSplitTypeChangeEnabled() {
        return splitTypeChangeEnabled;
    }

    /** Setter method for the SplitTypeChangeEnabled property.
    * @param value New SplitTypeChangeEnabled value.
    */
    public void setSplitTypeChangeEnabled(boolean value) {
        if (splitTypeChangeEnabled == value) {
            return;
        }

        splitTypeChangeEnabled = value;
        updatePopupMenu();
    }

    /** Getter method for the SwapPanesEnabled property.
    * @return Current SwapPanesEnabled value.
    */
    public boolean isSwapPanesEnabled() {
        return swapPanesEnabled;
    }

    /** Setter method for the SwapPanesEnabled property.
    * @param value New SwapPanesEnabled value.
    */
    public void setSwapPanesEnabled(boolean value) {
        if (swapPanesEnabled == value) {
            return;
        }

        swapPanesEnabled = value;
        updatePopupMenu();
    }

    /////////////////////
    // Event Listeners //
    /////////////////////

    /** Adds specified listener to the current set of SplitChangeListeners */
    public void addSplitChangeListener(SplitChangeListener l) {
        if (listeners == null) {
            listeners = new Vector();
        }

        listeners.addElement(l);
    }

    /** Removes specified listener from the current set of SplitChangeListeners */
    public void removeSplitChangeListener(SplitChangeListener l) {
        if (listeners == null) {
            return;
        }

        listeners.removeElement(l);
    }

    /** Fires the SplitChange event */
    protected void fireSplitChange(int oldValue, int newValue) {
        if (listeners == null) {
            return;
        }

        Vector l;

        synchronized (this) {
            l = (Vector) listeners.clone();
        }

        Enumeration en = l.elements();
        SplitChangeEvent evt = new SplitChangeEvent(this, oldValue, newValue);

        while (en.hasMoreElements()) {
            SplitChangeListener scl = (SplitChangeListener) en.nextElement();
            scl.splitChanged(evt);
        }
    }

    /* Read accessible context
     * @return - accessible context
     */
    public AccessibleContext getAccessibleContext() {
        if (accessibleContext == null) {
            accessibleContext = new AccessibleJComponent() {
                        public AccessibleRole getAccessibleRole() {
                            return AccessibleRole.SPLIT_PANE;
                        }
                    };
            initAccessible();
        }

        return accessibleContext;
    }

    private void initAccessible() {
        if (nameFormat == null) {
            ResourceBundle bundle = NbBundle.getBundle(SplittedPanel.class);
            nameFormat = new MessageFormat(bundle.getString("ACS_SplittedPanel_Name"));
        }

        getAccessibleContext().setAccessibleName(
            nameFormat.format(
                new Object[] {
                    ((firstComponent == null) || !(firstComponent instanceof Accessible)) ? null
                                                                                          : firstComponent.getAccessibleContext()
                                                                                                          .getAccessibleName(),
                    ((secondComponent == null) || !(secondComponent instanceof Accessible)) ? null
                                                                                            : secondComponent.getAccessibleContext()
                                                                                                             .getAccessibleName()
                }
            )
        );

        if (descriptionFormat == null) {
            ResourceBundle bundle = NbBundle.getBundle(SplittedPanel.class);
            descriptionFormat = new MessageFormat(bundle.getString("ACS_SplittedPanel_Description"));
        }

        getAccessibleContext().setAccessibleDescription(
            descriptionFormat.format(
                new Object[] {
                    ((firstComponent == null) || !(firstComponent instanceof Accessible)) ? null
                                                                                          : firstComponent.getAccessibleContext()
                                                                                                          .getAccessibleDescription(),
                    ((secondComponent == null) || !(secondComponent instanceof Accessible)) ? null
                                                                                            : secondComponent.getAccessibleContext()
                                                                                                             .getAccessibleDescription()
                }
            )
        );
    }

    /** Deserializes the component and initializes it. */
    private void readObject(java.io.ObjectInputStream ois)
    throws java.io.IOException, ClassNotFoundException {
        ois.defaultReadObject();
        init();
    }

    ///////////////////
    // Inner Classes //
    ///////////////////

    /** A listener interface for tracking split point changes */
    public static interface SplitChangeListener {
        /** Called when a split point changes
        * @param evt The SplitChangeEvent that describes the change
        */
        public void splitChanged(SplitChangeEvent evt);
    }

    /** An event that describes a split point change
      * @deprecated This class does nothing interesting that cannot be done with a JSplitPane.
      * Use a JSplitPane instead.
     */
    public static class SplitChangeEvent extends java.util.EventObject {
        /** generated Serialized Version UID */
        static final long serialVersionUID = 6748966611210836878L;
        private int oldValue;
        private int newValue;

        /** Constructs a new SplitChangeEvent for specified source SplittedPanel and
        * old and new SplitPositions
        */
        public SplitChangeEvent(SplittedPanel splittedPanel, int oldValue, int newValue) {
            super(splittedPanel);
            this.oldValue = oldValue;
            this.newValue = newValue;
        }

        /** @return the old splitterPosition */
        public int getOldValue() {
            return oldValue;
        }

        /** @return the new splitterPosition */
        public int getNewValue() {
            return newValue;
        }
    }

    /** The EmptySplitter is an empty splitter component with specified width.
     * It can be used as the splitter via setSplitterComponent.
     * @deprecated This class does nothing interesting that cannot be done with a JSplitPane.
     * Use a JSplitPane instead.
    */
    public static class EmptySplitter extends JComponent implements Accessible {
        /** generated Serialized Version UID */
        static final long serialVersionUID = 929648193440460693L;
        private int width;
        private AccessibleContext accessibleContext;

        public EmptySplitter() {
            this(0);
        }

        public EmptySplitter(int width) {
            ResourceBundle bundle = NbBundle.getBundle(SplittedPanel.class);

            accessibleContext = null;
            this.width = width;

            getAccessibleContext().setAccessibleName(bundle.getString("ACS_SplittedPanel_EmptySplitter"));
            getAccessibleContext().setAccessibleName(bundle.getString("ACSD_SplittedPanel_EmptySplitter"));
        }

        public Dimension getPreferredSize() {
            return new Dimension(width, width);
        }

        public AccessibleContext getAccessibleContext() {
            if (accessibleContext == null) {
                accessibleContext = new AccessibleJComponent() {
                            public AccessibleRole getAccessibleRole() {
                                return AccessibleRole.SPLIT_PANE;
                            }
                        };
            }

            return accessibleContext;
        }
    }

    /** The DefaultSplitter class implements a splitting line that is to be used as a default splitter for the SplittedPanel.
    * It paints a raised 3D-line with given width.
    */
    class DefaultSplitter extends JComponent implements Accessible {
        static final long serialVersionUID = -4223135481223014719L;
        private int splitterSize;

        /**
        * Constructs a new DefaultSplitter with given width.
        * @param aWidth  the desired width of the splitting line, if the value is lower than 2, the width of 2 is used
        */
        public DefaultSplitter(int aSplitterSize) {
            splitterSize = aSplitterSize;

            if (splitterSize < 2) {
                splitterSize = 2;
            }
        }

        public Dimension getPreferredSize() {
            return new Dimension(splitterSize, splitterSize);
        }

        public void paint(Graphics g) {
            super.paint(g);

            if (splitterSize <= 2) {
                return;
            }

            Dimension size = this.getSize();
            int height = size.height - 1;
            g.setColor(this.getBackground());

            Color high = UIManager.getColor("controlLtHighlight");

            //Color bg=UIManager.getColor("control");
            Color low = UIManager.getColor("controlDkShadow");

            boolean isMetal = UIManager.getLookAndFeel().getClass() == javax.swing.plaf.metal.MetalLookAndFeel.class;

            boolean firstHasBorder = true;
            boolean secondHasBorder = true;

            if (firstComponent instanceof JComponent) {
                Border b1 = ((JComponent) firstComponent).getBorder();
                firstHasBorder = (b1 != null) && (!(b1 instanceof EmptyBorder));
            }

            if (secondComponent instanceof JComponent) {
                Border b2 = ((JComponent) secondComponent).getBorder();
                secondHasBorder = (b2 != null) && (!(b2 instanceof EmptyBorder));
            }

            if (panesSwapped) {
                boolean b = firstHasBorder;
                firstHasBorder = secondHasBorder;
                secondHasBorder = b;
            }

            //draw the bumps
            if (isMetal && (splitterSize > 3) && drawBumps) {
                //looks backwards, but isn't - splitType==vertical gives you a
                //horizontal splitter
                int starty = (firstHasBorder && (splitType == VERTICAL)) ? 0 : 2;
                int startx = (firstHasBorder && (splitType == HORIZONTAL)) ? 0 : 2;

                for (int x = startx; (x + 1) < size.width; x += 4) {
                    for (int y = starty; (y + 1) < height; y += 4) {
                        g.setColor(this.getBackground().brighter());
                        g.drawLine(x, y, x, y);

                        if ((x < size.width) && (y < height)) {
                            g.drawLine(x + 2, y + 2, x + 2, y + 2);
                        }

                        g.setColor(this.getBackground().darker().darker());
                        g.drawLine(x + 1, y + 1, x + 1, y + 1);

                        if ((x < size.width) && (y < height)) {
                            g.drawLine(x + 3, y + 3, x + 3, y + 3);
                        }
                    }
                }
            }

            if (splitType == HORIZONTAL) {
                int pos = (size.width - splitterSize) / 2;

                if (!firstHasBorder) {
                    g.setColor(isMetal ? low : high);
                    g.drawLine(pos, 0, pos, size.height - 1);

                    if (isMetal) {
                        g.setColor(high);
                        g.drawLine(pos + 1, 0, pos + 1, size.height - 1);
                    }
                }

                if (!secondHasBorder) {
                    g.setColor(isMetal ? high : low);
                    g.drawLine((pos + splitterSize) - 1, 0, (pos + splitterSize) - 1, size.height - 1);

                    if (isMetal) {
                        g.setColor(low);
                        g.drawLine((pos + splitterSize) - 2, 0, (pos + splitterSize) - 2, size.height - 1);
                    }
                }
            } else if (splitType == VERTICAL) {
                int pos = (size.height - splitterSize) / 2;

                if (!firstHasBorder) {
                    g.setColor(isMetal ? low : high);
                    g.drawLine(0, pos, size.width - 1, pos);

                    if (isMetal) {
                        g.setColor(high);
                        g.drawLine(0, pos + 1, size.width - 1, pos + 1);
                    }
                }

                if (!secondHasBorder) {
                    g.setColor(isMetal ? high : low);
                    g.drawLine(0, (pos + splitterSize) - 1, size.width - 1, (pos + splitterSize) - 1);

                    if (isMetal) {
                        g.setColor(low);
                        g.drawLine(0, (pos + splitterSize) - 2, size.width - 1, (pos + splitterSize) - 2);
                    }
                }
            }
        }

        public AccessibleContext getAccessibleContext() {
            return SplittedPanel.this.getAccessibleContext();
        }
    }

    /**
    * The MouseListenerAdapter class implements the dragging behaviour of the splitter.
    */
    class MouseListenerAdapter extends MouseUtils.PopupMouseAdapter implements MouseListener, MouseMotionListener,
        SplitChangeListener {
        /** Called when the sequnce of mouse events should lead to actual
         * showing of the popup menu.
         * Should be redefined to show the menu.
         * param evt The mouse release event - should be used to obtain the
         *           position of the popup menu
         */
        protected void showPopup(MouseEvent e) {
            updatePopupMenu();

            if (isPopupMenuEnabled()) {
                popupMenu.show(splitter, e.getX(), e.getY());
            }
        }

        /** A method implemented from the MouseListener interface to handle the splitter dragging */
        public void mouseReleased(MouseEvent e) {
            super.mouseReleased(e);

            if (continuousLayout == false) {
                if (dragPos == -1) {
                    return;
                }

                if (!absolute) {
                    Dimension d = getSize();

                    if (splitType == VERTICAL) {
                        dragPos = (100 * dragPos) / d.height;
                    } else {
                        dragPos = (100 * dragPos) / d.width;
                    }
                }

                setSplitPosition(dragPos);
                dragPos = -1;
            }
        }

        /** A method implemented from the MouseMotionListener interface to handle the splitter dragging */
        public void mouseDragged(MouseEvent e) {
            if (continuousLayout == true) {
                Dimension d = getSize();
                Point splitterPos = splitter.getLocation();
                e.translatePoint(splitterPos.x, splitterPos.y);

                if (splitType == VERTICAL) {
                    dragPos = e.getY();

                    if (dragPos > d.height) {
                        dragPos = d.height;
                    }
                } else {
                    dragPos = e.getX();

                    if (dragPos > d.width) {
                        dragPos = d.width;
                    }
                }

                if (dragPos < 0) {
                    dragPos = 0;
                }

                if (continuousLayout) {
                    if (dragPos == -1) {
                        return;
                    }

                    int newDragPos = dragPos;

                    if (!absolute) {
                        if (splitType == VERTICAL) {
                            newDragPos = (100 * dragPos) / d.height;
                        } else {
                            newDragPos = (100 * dragPos) / d.width;
                        }
                    }

                    setSplitPosition(newDragPos);
                }
            }
        }

        /** A method implemented from the MouseMotionListener interface to handle the splitter dragging */
        public void mouseMoved(MouseEvent e) {
        }

        /* Double click on the splitter re-sets the splitter to follow one of the fields */
        public void mouseClicked(MouseEvent e) {
            super.mouseClicked(e);

            if ((e.getClickCount() == 2) && isPopupMenuEnabled()) {
                resetSplitter();
            }
        }

        public void splitChanged(SplitChangeEvent evt) {
            if ((evt.getNewValue() == FIRST_PREFERRED) || (evt.getNewValue() == SECOND_PREFERRED)) {
                resetPosition = evt.getNewValue();

                if (splitterCMI != null) {
                    splitterCMI.setEnabled(true);
                }
            }
        }
    }

    /**
    * The SplitLayout class implements a LayoutManager for the SplittedPanel.
    */
    class SplitLayout extends Object implements LayoutManager2, java.io.Serializable {
        static final long serialVersionUID = 2034500275182524789L;

        public void addLayoutComponent(String name, Component comp) {
            throw new IllegalArgumentException("You must use the add(Component, Object) method for adding"); // NOI18N
        }

        public void addLayoutComponent(Component comp, Object constraints) {
            if (constraints == ADD_SPLITTER) { // adding a splitter
                splitter = comp;
            } else if (constraints == ADD_FIRST) { // adding to the left/top

                if ((firstComponent != null) && (secondComponent == null)) { // if we altready have ... [PENDING]
                    secondComponent = firstComponent;
                }

                firstComponent = comp;

                if ((secondComponent != null) && (splitType == NONE)) {
                    splitType = DEFAULT_SPLIT_TYPE;
                }
            } else if (constraints == ADD_SECOND) {
                if (firstComponent == null) {
                    firstComponent = comp;
                } else {
                    secondComponent = comp;

                    if (splitType == NONE) {
                        splitType = DEFAULT_SPLIT_TYPE;
                    }
                }
            } else {
                throw new IllegalArgumentException("You must use one of the SplittedPanel.ADD_XXX constraints Objects"); // NOI18N
            }

            initAccessible();
        }

        public void removeLayoutComponent(Component comp) {
            if (comp.equals(secondComponent)) {
                secondComponent = null;
            } else if (comp.equals(firstComponent)) {
                firstComponent = null;

                if (secondComponent != null) {
                    firstComponent = secondComponent;
                    secondComponent = null;
                }
            }

            initAccessible();
        }

        public Dimension preferredLayoutSize(Container parent) {
            int width = 0;
            int height = 0;

            if (firstComponent != null) {
                Dimension d = firstComponent.getPreferredSize();
                width = d.width;
                height = d.height;
            }

            if (secondComponent != null) {
                Dimension d = secondComponent.getPreferredSize();

                if (splitType == VERTICAL) {
                    int splitterSize = splitter.getPreferredSize().height;

                    if (width < d.width) {
                        width = d.width;
                    }

                    height += (splitterSize + d.height);
                } else {
                    int splitterSize = splitter.getPreferredSize().width;

                    if (height < d.height) {
                        height = d.height;
                    }

                    width += (splitterSize + d.width);
                }
            }

            return new Dimension(width, height);
        }

        public Dimension minimumLayoutSize(Container parent) {
            int width = 0;
            int height = 0;

            if (firstComponent != null) {
                Dimension d = firstComponent.getMinimumSize();
                width = d.width;
                height = d.height;
            }

            if (secondComponent != null) {
                Dimension d = secondComponent.getMinimumSize();

                if (splitType == VERTICAL) {
                    int splitterSize = splitter.getMinimumSize().height;

                    if (width < d.width) {
                        width = d.width;
                    }

                    height += (splitterSize + d.height);
                } else {
                    int splitterSize = splitter.getMinimumSize().width;

                    if (height < d.height) {
                        height = d.height;
                    }

                    width += (splitterSize + d.width);
                }
            }

            return new Dimension(width, height);
        }

        public void layoutContainer(Container parent) {
            Dimension d = parent.getSize();
            int sPosition = splitPosition;

            // 1. first preferred
            if (splitPosition == FIRST_PREFERRED) {
                if (splitType == VERTICAL) {
                    sPosition = firstComponent.getPreferredSize().height;
                } else {
                    sPosition = firstComponent.getPreferredSize().width;
                }

                // 2. second preferred
            } else if (splitPosition == SECOND_PREFERRED) {
                if (splitType == VERTICAL) {
                    sPosition = d.height - splitter.getPreferredSize().width -
                        secondComponent.getPreferredSize().height;
                } else {
                    sPosition = d.width - splitter.getPreferredSize().height -
                        secondComponent.getPreferredSize().width;
                }

                // 3. percent position
            } else if (!absolute) {
                int sp = splitPosition;

                if (sp > 100) {
                    sp = 100;
                }

                if (splitType == VERTICAL) {
                    sPosition = (d.height * sp) / 100;
                } else {
                    sPosition = (d.width * sp) / 100;
                }
            }

            if ((splitType != NONE) && (firstComponent != null) && (secondComponent != null)) { // splitted

                if (splitType == VERTICAL) {
                    int splitterSize = splitter.getPreferredSize().height;

                    if ((firstComponent == null) || (secondComponent == null)) {
                        splitterSize = 0;
                    }

                    if (keepSecondSame && !splitIsChanging) {
                        Dimension secondSize = secondComponent.getSize();

                        if (secondSize.height != 0) {
                            sPosition = d.height - secondSize.height - splitterSize;
                        }
                    }

                    if ((sPosition + splitterSize) > d.height) {
                        sPosition = d.height - splitterSize;
                    }

                    if (sPosition < 0) {
                        sPosition = 0;
                    }

                    firstComponent.setBounds(new Rectangle(0, 0, d.width, sPosition));
                    splitter.setBounds(new Rectangle(0, sPosition, d.width, splitterSize));
                    secondComponent.setBounds(
                        new Rectangle(0, sPosition + splitterSize, d.width, d.height - sPosition - splitterSize)
                    );
                } else {
                    int splitterSize = splitter.getPreferredSize().width;

                    if ((firstComponent == null) || (secondComponent == null)) {
                        splitterSize = 0;
                    }

                    if (keepSecondSame && !splitIsChanging) {
                        Dimension secondSize = secondComponent.getSize();

                        if (secondSize.width != 0) {
                            sPosition = d.width - secondSize.width - splitterSize;
                        }
                    }

                    if ((sPosition + splitterSize) > d.width) {
                        sPosition = d.width - splitterSize;
                    }

                    if (sPosition < 0) {
                        sPosition = 0;
                    }

                    firstComponent.setBounds(new Rectangle(0, 0, sPosition, d.height));
                    splitter.setBounds(new Rectangle(sPosition, 0, splitterSize, d.height));
                    secondComponent.setBounds(
                        new Rectangle(sPosition + splitterSize, 0, d.width - sPosition - splitterSize, d.height)
                    );
                }
            } else if (firstComponent != null) {
                firstComponent.setBounds(new Rectangle(0, 0, d.width - 1, d.height - 1));

                if (splitter != null) {
                    splitter.setBounds(0, 0, 0, 0);
                }
            }
        }

        public Dimension maximumLayoutSize(Container target) {
            return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
        }

        public float getLayoutAlignmentX(Container target) {
            return 0;
        }

        public float getLayoutAlignmentY(Container target) {
            return 0;
        }

        public void invalidateLayout(Container target) {
        }
    }
}
