alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */


package org.netbeans.core.windows.view.ui;

import org.netbeans.core.windows.Constants;
import org.netbeans.core.windows.ModeImpl;
import org.netbeans.core.windows.WindowManagerImpl;
import org.netbeans.core.windows.view.SplitView;
import org.netbeans.core.windows.view.ViewElement;
import org.netbeans.core.windows.view.dnd.TopComponentDroppable;
import org.openide.windows.TopComponent;

import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import java.awt.*; // PENDING


/**
 * JSplitPane component which uses resizing policy according the UI spec.
 * I.e. the nested splits don't propagate draggig of parent split etc.
 *
 * @author  Marek Slama, Peter Zavadsky
 */
public class NestedSplitPane extends JSplitPane {


    private static final String uiClassID = "NestedSplitPaneUI"; // NOI18N
    
    private final SplitView splitView;
    
    private double tempResizeWeight = -1;

    
    /** Creates a new instance of NestedSplitPane */
    public NestedSplitPane(SplitView splitView,
    int orientation, Component first, Component second) {
        super(orientation, first, second);
        this.splitView = splitView;
        setOpaque(false);
        setBorder (BorderFactory.createEmptyBorder());
    }

    
    /** Returns location of innnermost divider with given orientation. If there is not
     * such divider (orientation is different) it returns -1. */
    public int getInnerDividerLocation (int orientation, String side, int ind) {
        if (orientation == getOrientation()) {
            if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
                if (JSplitPane.LEFT.equals(side)) {
                    Component c = getRightComponent();
                    if (c instanceof NestedSplitPane) {
                        //Recursion
                        NestedSplitPane sp = (NestedSplitPane) c;
                        ind++;
                        int loc = sp.getInnerDividerLocation(orientation, side, ind);
                        int newLoc;
                        if (loc != -1) {
                            //Recompute location from inner pane to this pane
                            Point p = SwingUtilities.convertPoint(sp, loc, 0, this);
                            newLoc = p.x;
                        } else {
                            newLoc = getDividerLocation();
                        }
                        return newLoc;
                    } else {
                        int loc = getDividerLocation();
                        return loc;
                    }
                } else { //RIGHT
                    Component c = getLeftComponent();
                    if (c instanceof NestedSplitPane) {
                        //Recursion
                        NestedSplitPane sp = (NestedSplitPane) c;
                        ind++;
                        int loc = sp.getInnerDividerLocation(orientation, side, ind);
                        int newLoc;
                        if (loc != -1) {
                            //Recompute location from inner pane to this pane
                            Point p = SwingUtilities.convertPoint(sp, loc, 0, this);
                            newLoc = p.x;
                        } else {
                            newLoc = getDividerLocation();
                        }
                        return newLoc;
                    } else {
                        int loc = getDividerLocation();
                        return loc;
                    }
                }
            } else { //VERTICAL
                if (JSplitPane.TOP.equals(side)) {
                    Component c = getBottomComponent();
                    if (c instanceof NestedSplitPane) {
                        //Recursion
                        NestedSplitPane sp = (NestedSplitPane) c;
                        ind++;
                        int loc = sp.getInnerDividerLocation(orientation, side, ind);
                        int newLoc;
                        if (loc != -1) {
                            //Recompute location from inner pane to this pane
                            Point p = SwingUtilities.convertPoint(sp, 0, loc, this);
                            newLoc = p.y;
                        } else {
                            newLoc = getDividerLocation();
                        }
                        return newLoc;
                    } else {
                        int loc = getDividerLocation();
                        return loc;
                    }
                } else { //BOTTOM
                    Component c = getTopComponent();
                    if (c instanceof NestedSplitPane) {
                        //Recursion
                        NestedSplitPane sp = (NestedSplitPane) c;
                        ind++;
                        int loc = sp.getInnerDividerLocation(orientation, side, ind);
                        int newLoc;
                        if (loc != -1) {
                            //Recompute location from inner pane to this pane
                            Point p = SwingUtilities.convertPoint(sp, 0, loc, this);
                            newLoc = p.y;
                        } else {
                            newLoc = getDividerLocation();
                        }
                        return newLoc;
                    } else {
                        int loc = getDividerLocation();
                        return loc;
                    }
                }
            }
        } else {
            return -1;
        }
    }
    
    private void setTemporaryResizeWeight(double tempResizeWeight) {
        this.tempResizeWeight = tempResizeWeight;
    }
    
    public double getResizeWeight() {
        if (tempResizeWeight != -1) {
            // Reset back, it is used only once during one dragging 'session'.
            double ret = tempResizeWeight;
            tempResizeWeight = -1;
            return ret;
        } else {
            return super.getResizeWeight();
        }
    }
    
    /** Overwritten to get desired divider dragging behaviour. Minimum size must
     * be set according to orientation of parent if parent is also split pane.
     * Otherwise delegate to superclass. */
    public Dimension getMinimumSize () {
        final Dimension originalMinimum = super.getMinimumSize();
        
        if(getParent() != null && (getParent() instanceof NestedSplitPane)) {
            NestedSplitPane parent = (NestedSplitPane)getParent();
            
            if((parent.getOrientation() == JSplitPane.HORIZONTAL_SPLIT)
            && (getOrientation() == JSplitPane.HORIZONTAL_SPLIT)) {
                //Check left
                if (JSplitPane.LEFT.equals(parent.getChildConstraint(this))) {
                    Component child = getRightComponent();
                    if (child instanceof NestedSplitPane && ((NestedSplitPane)child).getOrientation() == JSplitPane.HORIZONTAL_SPLIT) {
                        NestedSplitPane childSplit = (NestedSplitPane)child;
                        int loc = childSplit.getInnerDividerLocation(JSplitPane.HORIZONTAL_SPLIT, JSplitPane.LEFT, 0);
                        
                        //Recompute location from inner pane to this pane
                        Point point = SwingUtilities.convertPoint(childSplit, loc, 0, this);
                        int newLoc = point.x;

                        return new Dimension(newLoc + child.getMinimumSize().width, originalMinimum.height);
                    } else {
                        int loc = getDividerLocation();
                        return new Dimension(loc + child.getMinimumSize().width, originalMinimum.height);
                    }
                } else { //RIGHT
                    Component child = getLeftComponent();
                    if (child instanceof NestedSplitPane && ((NestedSplitPane)child).getOrientation() == JSplitPane.HORIZONTAL_SPLIT) {
                        NestedSplitPane childSplit = (NestedSplitPane)child;
                        int loc = childSplit.getInnerDividerLocation(JSplitPane.HORIZONTAL_SPLIT, JSplitPane.RIGHT, 0);

                        //Recompute location from inner pane to this pane
                        Point point = SwingUtilities.convertPoint(childSplit, loc, 0, this);
                        int newLoc = point.x;
                        
                        return new Dimension(getWidth() - newLoc + child.getMinimumSize().width, originalMinimum.height);
                    } else {
                        int loc = getDividerLocation();
                        return new Dimension(getWidth() - loc + child.getMinimumSize().width, originalMinimum.height);
                    }
                }
            } 
            if ((parent.getOrientation() == JSplitPane.VERTICAL_SPLIT) &&
                (getOrientation() == JSplitPane.VERTICAL_SPLIT)) {
                //Check top
                if (JSplitPane.TOP.equals(parent.getChildConstraint(this))) {
                    Component child = getBottomComponent();
                    if (child instanceof NestedSplitPane && ((NestedSplitPane)child).getOrientation() == JSplitPane.VERTICAL_SPLIT) {
                        NestedSplitPane childSplit = (NestedSplitPane)child;
                        int loc = childSplit.getInnerDividerLocation(JSplitPane.VERTICAL_SPLIT, JSplitPane.TOP, 0);

                        //Recompute location from inner pane to this pane
                        Point point = SwingUtilities.convertPoint(childSplit, 0, loc, this);
                        int newLoc = point.y;
                        
                        return new Dimension(originalMinimum.width, newLoc + child.getMinimumSize().height);
                    } else {
                        int loc = getDividerLocation();
                        return new Dimension(originalMinimum.width, loc + child.getMinimumSize().height);
                    }
                } else { //BOTTOM
                    Component child = getTopComponent();
                    if (child instanceof NestedSplitPane && ((NestedSplitPane)child).getOrientation() == JSplitPane.VERTICAL_SPLIT) {
                        NestedSplitPane childSplit = (NestedSplitPane)child;
                        int loc = childSplit.getInnerDividerLocation(JSplitPane.VERTICAL_SPLIT, JSplitPane.BOTTOM, 0);

                        //Recompute location from inner pane to this pane
                        Point point = SwingUtilities.convertPoint(childSplit, 0, loc, this);
                        int newLoc = point.y;
                        
                        return new Dimension(originalMinimum.width, getHeight() - newLoc + child.getMinimumSize().height);
                    } else {
                        int loc = getDividerLocation();
                        return new Dimension(originalMinimum.width, getHeight() - loc + child.getMinimumSize().height);
                    }
                }
            }
        }
        
        return originalMinimum;
    }
    
    private String getChildConstraint (Component c) {
        if (getOrientation() == VERTICAL_SPLIT) {
            if (c == getTopComponent()) {
                return JSplitPane.TOP;
            } else if (c == getBottomComponent()) {
                return JSplitPane.BOTTOM;
            }
        } else {
            if (c == getLeftComponent()) {
                return JSplitPane.LEFT;
            } else if (c == getRightComponent()) {
                return JSplitPane.RIGHT;
            }
        }
        
        return null;
    }

    private void draggingFinishedTo(int location) {
        firePropertyChange("topDividerLocation", null, new Integer(location)); // NOI18N
    }

    /** Overriden to fix #45988 */
    public void doLayout() {
        fixPreferredSizes(getSize());
        super.doLayout();
    }
    
    /** Whole method is just hacky fix of #45988. Pref sizes are kept in sync
     * to prevent JSplitPane's layout manager in JDK 1.5 to use minimum sizes
     */
    private void fixPreferredSizes(Dimension availableSpace) {
        JComponent firstC = (JComponent)getLeftComponent();
        JComponent secondC = (JComponent)getRightComponent();
        
        if (firstC == null || secondC == null) {
            return;
        }
        
        Dimension firstPref = firstC.getPreferredSize();
        Dimension secondPref = secondC.getPreferredSize();
        Insets insets = getInsets();
        
        boolean shouldFix = false;

        if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
            int curPrefW = firstPref.width + secondPref.width +
                            getDividerSize() + 
                            insets.left + insets.right;
            if (availableSpace.width < curPrefW) {
                shouldFix = true;
                firstPref.width = Math.max(0, firstPref.width - (curPrefW - availableSpace.width));
            }
        } else { // Vertical split
            int curPrefH = firstPref.height + secondPref.height +
                            getDividerSize() + 
                            insets.top + insets.bottom;
            if (availableSpace.height < curPrefH) {
                shouldFix = true;
                firstPref.height = Math.max(0, firstPref.height - (curPrefH - availableSpace.height));
            }
        }

        if (shouldFix) {
            firstC.setPreferredSize(firstPref);
        }
    }
    
    // UI staff>>
    public void updateUI() {
        // Ensure there is the (special) value in UIDefaults.
        try {
            //Does not work on Plastic L&F via classname - plastic returns
            //something else for the class name that's not assignable
            //to NestedSplitPanelUI
            Object className = UIManager.getDefaults().get(uiClassID);
            if(className == null) {
                UIManager.getDefaults().put(uiClassID,
                    "org.netbeans.core.windows.view.ui.NestedSplitPane$NestedSplitPaneUI"); // NOI18N
            }
            if (!"org.netbeans.core.windows.view.ui.NestedSplitPane$NestedSplitPaneUI".equals(className)) { // NOI18N
                //Avoid error on Plastic L&F that starts main window with no
                //contents - Tim
                setUI (new NestedSplitPaneUI());
            } else {
                setUI((NestedSplitPaneUI)UIManager.getUI(this));
            }
            revalidate();
        } catch (Error e) {
            setUI (new NestedSplitPaneUI());
        }
    }

    public String getUIClassID() {
        return uiClassID;
    }
    // UI staff<<
    
    
    // XXX It should be refined to 'interface'-like UI for NestedSplitPane and this one as a basic one.
    /** UI class for NestedSplitPane.
     * Only purpose of overriding the defaul is to get the event, when user starts
     * and finish dragging the split divider. */
    public static class NestedSplitPaneUI extends BasicSplitPaneUI {
        
        /** Overriden, set our own defaults for dividers in addition to
         * superclass version. No border for dividers.
         */
        public void installUI(JComponent c) {
            super.installUI(c);
            getDivider().setBorder(null);
            if ("GTK".equals(UIManager.getLookAndFeel().getID())) {
                ((JSplitPane) c).setDividerSize(2);
            }
        }
        
        protected void startDragging() {
            // Set temporary resize policy for left child if it is a split pane.
            Component c = splitPane.getLeftComponent();
            if(c instanceof NestedSplitPane) {
                NestedSplitPane leftSplit = (NestedSplitPane)c;
                if(leftSplit.getOrientation() == splitPane.getOrientation()) {
                    leftSplit.setTemporaryResizeWeight(0.0D);
                }
            }
            
            // Set temporary resize policy for right child if it is a split pane.
            c = splitPane.getRightComponent();
            if(c instanceof NestedSplitPane) {
                NestedSplitPane rightSplit = (NestedSplitPane)c;
                if(rightSplit.getOrientation() == splitPane.getOrientation()) {
                    rightSplit.setTemporaryResizeWeight(1.0D);
                }
            }
            
            super.startDragging ();
        }
        
        protected void finishDraggingTo(int location) {
            super.finishDraggingTo(location);
            ((NestedSplitPane)splitPane).draggingFinishedTo(location);
        }
        
        public static ComponentUI createUI(JComponent x) {
            return new NestedSplitPaneUI();
        }
        
        /** Creates the default divider. Overrides superclass method */
        public BasicSplitPaneDivider createDefaultDivider() {
            BasicSplitPaneDivider div = new NestedSplitPaneDivider(this);
            if (isXPLF()) {
                div.setBackground((Color)UIManager.get("nb_workplace_fill"));
            }
            return div;
        }

        /** Sublclassed to implement TopComponentDroppable */
        private static class NestedSplitPaneDivider extends BasicSplitPaneDivider
        implements TopComponentDroppable {
            
            public NestedSplitPaneDivider(NestedSplitPaneUI ui) {
                super(ui);
            }
            
            /** Implement TopComponentDroppable method. */
            public ViewElement getDropViewElement() {
                return ((NestedSplitPane)splitPaneUI.getSplitPane()).splitView;
            }
            
            public Component getDropComponent() {
                return splitPaneUI.getSplitPane();
            }
            
            public Object getConstraintForLocation(Point location) {
                // PENDING
                return null;
            }
            
            public Shape getIndicationForLocation(Point location) {
                NestedSplitPane split = (NestedSplitPane)splitPaneUI.getSplitPane();
                java.awt.Rectangle r = split.getBounds();
                int splitPosition = split.getDividerLocation();
                
                // XXX PENDING adjusts according the nested splits.
                Component comp = split.getRightComponent();
                if(comp instanceof NestedSplitPane
                && ((NestedSplitPane)comp).getOrientation() == split.getOrientation()) {
                    Component right = ((NestedSplitPane)comp).getRightComponent();
                    if(split.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) {
                        r.setBounds(r.x, r.y, r.width - right.getWidth(), r.height);
                    } else {
                        r.setBounds(r.x, r.y, r.width, r.height - right.getHeight());
                    }
                }
                
                double ratio = Constants.DROP_BETWEEN_RATIO;
                if(split.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) {
                    r.setBounds(splitPosition - (int)(splitPosition * ratio), 0, (int)(r.width * ratio), r.height);
                } else {
                    r.setBounds(0, splitPosition - (int)(splitPosition * ratio), r.width, (int)(r.height * ratio));
                }
                return r;
            }
            
            public boolean canDrop(TopComponent transfer, Point location) {
                if(Constants.SWITCH_MODE_ADD_NO_RESTRICT
                || WindowManagerImpl.getInstance().isTopComponentAllowedToMoveAnywhere(transfer)) {
                    return true;
                }
                
                // TODO.. it is more complicated, to detect whether the drop could happen.
                // this is just a rough solution.
                NestedSplitPane split = (NestedSplitPane)splitPaneUI.getSplitPane();
                ModeImpl mode = (ModeImpl)WindowManagerImpl.getInstance().findMode(transfer);
                int kind = mode != null ? mode.getKind() : Constants.MODE_KIND_EDITOR;
                if(kind == Constants.MODE_KIND_EDITOR
                && (hasEditorOnly(split.getLeftComponent())
                        && hasEditorOnly(split.getRightComponent()))){
                    return true;
                } else if(kind == Constants.MODE_KIND_VIEW
                && (hasViewSome(split.getLeftComponent())
                        || hasViewSome(split.getRightComponent()))) {
                    return true;
                }
                
                return false;
            }
            
            private boolean hasEditorOnly(Component component) {
                if(component instanceof ModeComponent) {
                    return ((ModeComponent)component).getKind() == Constants.MODE_KIND_EDITOR;
                } else if(component instanceof NestedSplitPane) {
                    NestedSplitPane split = (NestedSplitPane)component;
                    return hasEditorOnly(split.getLeftComponent()) && hasEditorOnly(split.getRightComponent());
                }
                return false;
            }
            
            private boolean hasViewSome(Component component) {
                if(component instanceof ModeComponent) {
                    return ((ModeComponent)component).getKind() == Constants.MODE_KIND_VIEW;
                } else if(component instanceof NestedSplitPane) {
                    NestedSplitPane split = (NestedSplitPane)component;
                    return hasViewSome(split.getLeftComponent()) || hasViewSome(split.getRightComponent());
                }
                return false;
            }

            public boolean supportsKind(int kind, TopComponent transfer) {
                if(Constants.SWITCH_MODE_ADD_NO_RESTRICT
                || WindowManagerImpl.getInstance().isTopComponentAllowedToMoveAnywhere(transfer)) {
                    return true;
                }

                return kind == Constants.MODE_KIND_EDITOR || kind == Constants.MODE_KIND_VIEW;
            }
            
            public void paint(java.awt.Graphics g) {
                //All that should be necessary to get the right color is...nothing
                //XXX double check this on windows
//                int parents = 0;
//                Component comp = getDropComponent();
//                while (comp.getParent() instanceof NestedSplitPane) {
//                    comp = comp.getParent();
//                    parents = parents + 1;
//                }
                Color c = UIManager.getColor ("nb_workplace_fill");
//                switch (parents) {
//                    case 0 : c = java.awt.Color.BLUE; break;
//                    case 1 : c = java.awt.Color.MAGENTA;  break;
//                    case 2 : c = java.awt.Color.CYAN;  break;
//                    case 3 : c = java.awt.Color.RED;  break;
//                    case 4 : c = java.awt.Color.GREEN;  break;
//                    case 5 : c = java.awt.Color.YELLOW;  break;
//                    case 6 : c = java.awt.Color.WHITE;  break;
//                }
                if (c != null) {
                    g.setColor (c);
                    Dimension size = getSize();
                    g.fillRect(0, 0, size.width, size.height);
                    super.paint(g);
                }
            }            
            
        } // End of class NestedSplitPaneDivider.
    } // End of NestedSplitPaneUI class.

    /** Finds if windows LF with XP theme is active.
     * @return true if windows LF and XP theme is active, false otherwise */
    public static boolean isXPLF () {
        if (!"Windows".equals(UIManager.getLookAndFeel().getID())) { //NOI18N
            return false;
        }
        Boolean isXP = (Boolean)Toolkit.getDefaultToolkit().
                        getDesktopProperty("win.xpstyle.themeActive"); //NOI18N
        return isXP == null ? false : isXP.booleanValue();
    }    

}

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.