com.jidesoft.swing.JideSplitPane Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jide-oss Show documentation
Show all versions of jide-oss Show documentation
JIDE Common Layer (Professional Swing Components)
/*
* @(#)JideSplitPane.java
*
* Copyright 2002 JIDE Software Inc. All rights reserved.
*/
package com.jidesoft.swing;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.plaf.UIDefaultsLookup;
import javax.accessibility.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import java.util.Map;
/**
* JideSplitPane
is used to divide multiple Component
s.
*
* These Component
s in a split pane can be aligned left to right using
* JideSplitPane.HORIZONTAL_SPLIT
, or top to bottom using JideSplitPane.VERTICAL_SPLIT
.
*/
public class JideSplitPane extends JPanel implements ContainerListener, ComponentListener, Accessible {
/**
* The divider used for non-continuous layout is added to the split pane with this object.
*/
protected static final String NON_CONTINUOUS_DIVIDER =
"nonContinuousDivider";
/**
* Vertical split indicates the Component
s are split along the y axis. For example the two or more
* Component
s will be split one on top of the other.
*/
public static final int VERTICAL_SPLIT = 0;
/**
* Horizontal split indicates the Component
s are split along the x axis. For example the two or more
* Component
s will be split one to the left of the other.
*/
public static final int HORIZONTAL_SPLIT = 1;
/**
* Bound property name for orientation (horizontal or vertical).
*/
public static final String ORIENTATION_PROPERTY = "orientation";
/**
* Bound property name for border size.
*/
public static final String DIVIDER_SIZE_PROPERTY = "dividerSize";
/**
* Bound property name for border size.
*/
public static final String PROPERTY_DIVIDER_LOCATION = "dividerLocation";
/**
* Bound property name for continuousLayout.
*/
public static final String CONTINUOUS_LAYOUT_PROPERTY = "continuousLayout";
/**
* Bound property name for gripper.
*/
public static final String GRIPPER_PROPERTY = "gripper";
/**
* Bound property name for proportional layout.
*/
public static final String PROPORTIONAL_LAYOUT_PROPERTY = "proportionalLayout";
/**
* Bound property name for the proportions used in the layout.
*/
public static final String PROPORTIONS_PROPERTY = "proportions";
public static final String PROPERTY_HEAVYWEIGHT_COMPONENT_ENABLED = "heavyweightComponentEnabled";
/**
* How the views are split. The value of it can be either HORIZONTAL_SPLIT
or
* VERTICAL_SPLIT
.
*/
private int _orientation;
/**
* Size of the divider. All dividers have the same size. If orientation
is
* HORIZONTAL_SPLIT
, the size will equal to the width of the divider If orientation
is
* VERTICAL_SPLIT
, the size will equal to the height of the divider.
*/
private int _dividerSize = UIDefaultsLookup.getInt("JideSplitPane.dividerSize");
// /**
// * Instance for the shadow of the divider when non continuous layout
// * is being used.
// */
// private Contour _nonContinuousLayoutDivider;
private HeavyweightWrapper _nonContinuousLayoutDividerWrapper;
/**
* Continuous layout or not.
*/
private boolean _continuousLayout = false;
/**
* Layered pane where _nonContinuousLayoutDivider is added to.
*/
private Container _layeredPane;
/**
* If the gripper should be shown. Gripper is something on divider to indicate it can be dragged.
*/
private boolean _showGripper = false;
/**
* Whether the contained panes should be laid out proportionally.
*/
private boolean _proportionalLayout = false;
/**
* An array of the proportions to assign to the widths or heights of the contained panes. Has one fewer elements
* than there are contained panes; the last pane receives the remaining room.
*/
private double[] _proportions;
/**
* For proportional layouts only, when this flag is true the initial layout uses even proportions for the contained
* panes, unless the proportions are explicitly set.
*/
private boolean _initiallyEven = true;
private boolean _heavyweightComponentEnabled = false;
public WindowAdapter _windowDeactivatedListener;
private int _dividerStepSize = 0;
private boolean _dragResizable = true;
private boolean _hiddenByMyself = false;
/**
* Creates a new JideSplitPane
configured to arrange the child components side-by-side horizontally.
*/
public JideSplitPane() {
this(HORIZONTAL_SPLIT);
}
/**
* Creates a new JideSplitPane
configured with the specified orientation.
*
* @param newOrientation JideSplitPane.HORIZONTAL_SPLIT
or JideSplitPane.VERTICAL_SPLIT
* @throws IllegalArgumentException if orientation
is not one of HORIZONTAL_SPLIT or VERTICAL_SPLIT.
*/
public JideSplitPane(int newOrientation) {
super();
_orientation = newOrientation;
if (_orientation != HORIZONTAL_SPLIT && _orientation != VERTICAL_SPLIT)
throw new IllegalArgumentException("cannot create JideSplitPane, " +
"orientation must be one of " +
"JideSplitPane.HORIZONTAL_SPLIT " +
"or JideSplitPane.VERTICAL_SPLIT");
// setup layout
LayoutManager layoutManager;
if (_orientation == HORIZONTAL_SPLIT) {
layoutManager = new JideSplitPaneLayout(this, JideSplitPaneLayout.X_AXIS);
}
else {
layoutManager = new JideSplitPaneLayout(this, JideSplitPaneLayout.Y_AXIS);
}
super.setLayout(layoutManager);
setOpaque(false);
// setup listener
installListeners();
}
/**
* Get the step size while dragging the divider.
*
* The default value of the step size is 0, which means no constraints on the dragging position
*
* @return the step size.
*/
public int getDividerStepSize() {
return _dividerStepSize;
}
/**
* Set the step size while dragging the divider.
*
* The step size cannot be negative.
*
* @param dividerStepSize the step size
*/
public void setDividerStepSize(int dividerStepSize) {
if (dividerStepSize < 0) {
return;
}
_dividerStepSize = dividerStepSize;
}
@Override
public void updateUI() {
if (UIDefaultsLookup.get("JideSplitPane.dividerSize") == null) {
LookAndFeelFactory.installJideExtension();
}
super.updateUI();
}
/**
* Install listeners
*/
private void installListeners() {
addContainerListener(this);
}
/**
* Sets the size of the divider.
*
* @param newSize an integer giving the size of the divider in pixels
*/
public void setDividerSize(int newSize) {
int oldSize = _dividerSize;
if (oldSize != newSize) {
_dividerSize = newSize;
firePropertyChange(DIVIDER_SIZE_PROPERTY, oldSize, newSize);
invalidate();
}
}
/**
* Returns the size of the divider.
*
* @return an integer giving the size of the divider in pixels
*/
public int getDividerSize() {
return _dividerSize;
}
/**
* Inserts the specified pane to this container at the given position. Note: Divider is not counted.
*
* @param pane the pane to be added
* @param index the position at which to insert the component.
* @return the component pane
*/
public Component insertPane(Component pane, int index) {
return insertPane(pane, null, index);
}
/**
* Inserts the specified pane to this container at the given position. Note: Divider is not counted.
*
* @param pane the pane to be added
* @param constraint an object expressing layout constraints for this component
* @param index the position at which to insert the component.
* @return the component pane
*/
public Component insertPane(Component pane, Object constraint, int index) {
if (index <= 0) {
addImpl(pane, constraint, 0);
}
else if (index >= getPaneCount()) {
addImpl(pane, constraint, -1);
}
else {
addImpl(pane, constraint, (index << 1) - 1);
}
return pane;
}
/**
* Adds the specified pane to this container at the end.
*
* @param pane the pane to be added
* @return the pane pane
*/
public Component addPane(Component pane) {
if (pane == null) {
return null;
}
return super.add(pane);
}
/**
* Removes the pane, specified by index
, from this container.
*
* @param pane the pane to be removed.
*/
public void removePane(Component pane) {
removePane(indexOfPane(pane));
}
/**
* Replaces the pane at the position specified by index.
*
* @param pane new pane
* @param index position
*/
public void setPaneAt(Component pane, int index) {
setPaneAt(pane, null, index);
}
/**
* Replaces the pane at the position specified by index.
*
* @param pane new pane
* @param constraint an object expressing layout constraints for this component
* @param index position
*/
public void setPaneAt(Component pane, Object constraint, int index) {
double[] proportions = _proportions;
_proportions = null; // Just turn them off temporarily
removePane(index);
insertPane(pane, constraint, index);
_proportions = proportions;
validate();
}
/**
* Removes the pane, specified by index
, from this container.
*
* @param index the index of the component to be removed.
*/
public void removePane(int index) {
if (index == 0) { // if first one
super.remove(0); // the component
}
else { // not first one. then remove itself and the divider before it
super.remove(index << 1); // component
}
}
/**
* Sets the orientation, or how the splitter is divided. The options are: - JideSplitPane.VERTICAL_SPLIT
* (above/below orientation of components)
- JideSplitPane.HORIZONTAL_SPLIT (left/right orientation of
* components)
*
* @param orientation an integer specifying the orientation
* @throws IllegalArgumentException if orientation is not one of: HORIZONTAL_SPLIT or VERTICAL_SPLIT.
*/
public void setOrientation(int orientation) {
if ((orientation != VERTICAL_SPLIT) &&
(orientation != HORIZONTAL_SPLIT)) {
throw new IllegalArgumentException("JideSplitPane: orientation must " +
"be one of " +
"JideSplitPane.VERTICAL_SPLIT or " +
"JideSplitPane.HORIZONTAL_SPLIT");
}
if (_orientation == orientation)
return;
int oldOrientation = _orientation;
_orientation = orientation;
// if (_orientation == JideSplitPane.HORIZONTAL_SPLIT)
// setBorder(BorderFactory.createLineBorder(Color.RED));
// else
// setBorder(BorderFactory.createLineBorder(Color.CYAN));
JideSplitPaneLayout layoutManager;
if (_orientation == HORIZONTAL_SPLIT) {
layoutManager = new JideSplitPaneLayout(this, JideSplitPaneLayout.X_AXIS);
}
else {
layoutManager = new JideSplitPaneLayout(this, JideSplitPaneLayout.Y_AXIS);
}
Component[] components = getComponents();
LayoutManager oldManager = getLayout();
Map constraintMap = null;
if (oldManager instanceof JideSplitPaneLayout) {
constraintMap = ((JideSplitPaneLayout) oldManager).getConstraintMap();
}
if (components != null && constraintMap != null) {
for (Component comp : components) {
layoutManager.addLayoutComponent(comp, constraintMap.get(comp));
}
}
super.setLayout(layoutManager);
doLayout();
firePropertyChange(ORIENTATION_PROPERTY, oldOrientation, orientation);
}
/**
* Returns the orientation.
*
* @return an integer giving the orientation
*
* @see #setOrientation
*/
public int getOrientation() {
return _orientation;
}
/**
* Lays out the JideSplitPane
layout based on the preferred size children components, or based on the
* proportions if proportional layout is on. This will likely result in changing the divider location.
*/
public void resetToPreferredSizes() {
if (getLayout() instanceof JideBoxLayout) {
boolean old = ((JideBoxLayout) getLayout()).isResetWhenInvalidate();
try {
((JideBoxLayout) getLayout()).setResetWhenInvalidate(true);
((JideBoxLayout) getLayout()).invalidateLayout(this);
}
finally {
((JideBoxLayout) getLayout()).setResetWhenInvalidate(old);
}
}
doLayout();
}
/**
* Sets this split pane to lay its constituents out proportionally if the given flag is true, or by preferred sizes
* otherwise.
*
* @param proportionalLayout true or false.
*/
public void setProportionalLayout(boolean proportionalLayout) {
if (proportionalLayout == _proportionalLayout)
return;
_proportionalLayout = proportionalLayout;
revalidate();
firePropertyChange(PROPORTIONAL_LAYOUT_PROPERTY, !proportionalLayout, proportionalLayout);
if (!proportionalLayout)
setProportions(null);
}
/**
* Returns the proportional layout flag.
*
* @return true or false.
*/
public boolean isProportionalLayout() {
return _proportionalLayout;
}
void internalSetProportions(double[] proportions) {
_proportions = proportions;
}
/**
* Sets the proportions to use in laying out this split pane's children. Only applicable when {@link
* #isProportionalLayout} is true; calling it when false will throw an exception. The given array must either be
* null, or have one fewer slots than there are {@linkplain #getPaneCount() contained panes}. Each item in the
* array (if not null) must be a number between 0 and 1, and the sum of all of them must be no more than 1.
*
* @param proportions the proportions of all the panes.
*/
public void setProportions(double[] proportions) {
// if ( ! _proportionalLayout )
if (!_proportionalLayout && proportions != null)
throw new IllegalStateException("Can't set proportions on a non-proportional split pane");
if (Arrays.equals(proportions, _proportions))
return;
if (proportions != null && proportions.length != getPaneCount() - 1)
throw new IllegalArgumentException(
"Must provide one fewer proportions than there are panes: got " + proportions.length
+ ", expected " + (getPaneCount() - 1));
if (proportions != null) {
double sum = 0.0;
for (int i = 0; i < proportions.length; ++i) {
if (proportions[i] < 0.0)
proportions[i] = 0.0;
if (proportions[i] > 1.0)
proportions[i] = 1.0;
sum += proportions[i];
}
if (sum > 1.0)
throw new IllegalArgumentException("Sum of proportions must be no more than 1, got " + sum);
}
double[] oldProportions = _proportions;
_proportions = (proportions == null) ? null : proportions.clone();
LayoutManager layoutManager = getLayout();
boolean reset = false;
if (layoutManager instanceof JideBoxLayout) {
reset = ((JideBoxLayout) layoutManager).isResetWhenInvalidate();
((JideBoxLayout) layoutManager).setResetWhenInvalidate(true);
}
revalidate();
if (reset) {
((JideBoxLayout) layoutManager).setResetWhenInvalidate(reset);
}
firePropertyChange(PROPORTIONS_PROPERTY, oldProportions, proportions);
}
/**
* Returns the current array of proportions used for proportional layout, or null if none has been established via
* {@link #setProportions} or via user action.
*
* @return the proportions.
*/
public double[] getProportions() {
double[] answer = _proportions;
if (answer != null)
answer = answer.clone();
return answer;
}
/**
* Sets the flag telling whether to do even proportions for the initial proportional layout, in the absence of
* explicit proportions.
*
* @param initiallyEven true or false.
*/
public void setInitiallyEven(boolean initiallyEven) {
_initiallyEven = initiallyEven;
}
/**
* Returns the flag that tells whether to do even proportions for the initial proportional layout, in the absence of
* explicit proportions.
*
* @return true or false.
*/
public boolean isInitiallyEven() {
return _initiallyEven;
}
/**
* Returns true, so that calls to revalidate
on any descendant of this JideSplitPane
will
* cause a request to be queued that will validate the JideSplitPane
and all its descendants.
*
* @return true
*
* @see JComponent#revalidate
*/
@Override
public boolean isValidateRoot() {
return true;
}
/**
* Prepares dragging if it's not continuous layout. If it's continuous layout, do nothing.
*
* @param divider the divider
*/
protected void startDragging(JideSplitPaneDivider divider) {
if (!isContinuousLayout()) {
Component topLevelAncestor = getTopLevelAncestor();
if (_windowDeactivatedListener == null) {
// this a listener to remove the dragging outline when window is deactivated
_windowDeactivatedListener = new WindowAdapter() {
@Override
public void windowDeactivated(WindowEvent e) {
stopDragging();
if (e.getWindow() != null) {
e.getWindow().removeWindowListener(_windowDeactivatedListener);
}
}
};
}
if (topLevelAncestor instanceof Window)
((Window) topLevelAncestor).addWindowListener(_windowDeactivatedListener);
if (topLevelAncestor instanceof RootPaneContainer) {
_layeredPane = ((RootPaneContainer) topLevelAncestor).getLayeredPane();
// left over, remove them
if (_nonContinuousLayoutDividerWrapper == null) {
Contour nonContinuousLayoutDivider = new JideSplitPaneContour();
_nonContinuousLayoutDividerWrapper = new JideSplitPaneHeavyweightWrapper(nonContinuousLayoutDivider);
_nonContinuousLayoutDividerWrapper.setHeavyweight(isHeavyweightComponentEnabled());
}
_nonContinuousLayoutDividerWrapper.delegateSetCursor((_orientation == HORIZONTAL_SPLIT) ?
JideSplitPaneDivider.HORIZONTAL_CURSOR : JideSplitPaneDivider.VERTICAL_CURSOR);
_nonContinuousLayoutDividerWrapper.delegateSetVisible(false);
_nonContinuousLayoutDividerWrapper.delegateAdd(_layeredPane, JLayeredPane.DRAG_LAYER);
Rectangle bounds = getVisibleRect();
Rectangle layeredPaneBounds = SwingUtilities.convertRectangle(this, bounds, _layeredPane);
int dividerThickness = Math.min(4, getDividerSize());
if (getOrientation() == HORIZONTAL_SPLIT) {
_nonContinuousLayoutDividerWrapper.delegateSetBounds(layeredPaneBounds.x, layeredPaneBounds.y,
dividerThickness, layeredPaneBounds.height);
}
else {
_nonContinuousLayoutDividerWrapper.delegateSetBounds(layeredPaneBounds.x, layeredPaneBounds.y,
layeredPaneBounds.width, dividerThickness);
}
}
}
}
private void stopDragging() {
if (!isContinuousLayout() && _layeredPane != null && _nonContinuousLayoutDividerWrapper != null) {
_nonContinuousLayoutDividerWrapper.delegateSetVisible(false);
_nonContinuousLayoutDividerWrapper.delegateRemove(_layeredPane);
_nonContinuousLayoutDividerWrapper.delegateSetNull();
_nonContinuousLayoutDividerWrapper = null;
// add a protection in case there is another wrapper inside the layered pane
Component[] childComponents = _layeredPane.getComponents();
for (Component component : childComponents) {
if (component instanceof JideSplitPaneContour || component instanceof JideSplitPaneHeavyweightWrapper) {
_layeredPane.remove(component);
}
}
}
}
/**
* Drags divider to right location. If it's continuous layout, really drag the divider; if not, only drag the
* shadow.
*
* @param divider the divider
* @param location new location
*/
protected void dragDividerTo(JideSplitPaneDivider divider, int location) {
if (_layeredPane == null || isContinuousLayout()) {
setDividerLocation(divider, location);
}
else {
if (_nonContinuousLayoutDividerWrapper != null) {
Point p;
Dimension size = new Dimension();
Rectangle rect = getVisibleRect();
int dividerThickness = Math.min(4, getDividerSize());
Rectangle convertedRect = SwingUtilities.convertRectangle(this, rect, _layeredPane);
if (getOrientation() == HORIZONTAL_SPLIT) {
p = SwingUtilities.convertPoint(this, location, rect.y, _layeredPane);
p.x += ((getDividerSize() - dividerThickness) >> 1);
size.width = dividerThickness;
size.height = convertedRect.height;
}
else {
p = SwingUtilities.convertPoint(this, rect.x, location, _layeredPane);
p.y += ((getDividerSize() - dividerThickness) >> 1);
size.width = convertedRect.width;
size.height = dividerThickness;
}
_nonContinuousLayoutDividerWrapper.delegateSetBounds(new Rectangle(p, size));
_nonContinuousLayoutDividerWrapper.delegateSetVisible(true);
}
}
}
/**
* Finishes dragging. If it's not continuous layout, clear up the shadow component.
*
* @param divider the divider
* @param location new location
*/
protected void finishDraggingTo(JideSplitPaneDivider divider, int location) {
if (isContinuousLayout() || _nonContinuousLayoutDividerWrapper != null) {
stopDragging();
setDividerLocation(divider, location);
}
}
/**
* Returns the index of the divider. For example, the index of the first divider is 0, the index of the second is 1.
* Notes: Pane is not counted
*
* @param divider divider to get index
* @return index of the divider. -1 if comp doesn't exist in this container
*/
public int indexOfDivider(JideSplitPaneDivider divider) {
int index = indexOf(divider);
if (index == -1)
return index;
else {
if (index % 2 == 0)
//noinspection UseOfSystemOutOrSystemErr
System.err.println("Warning: divider's index is even. (index = " + index + ")");
return (index - 1) / 2;
}
}
/**
* Returns the index of the pane. For example, the index of the first pane is 0, the index of the second is 1.
* Notes: divider is not counted
*
* @param pane pane to get index
* @return index of the pane. -1 if comp doesn't exist in this container
*/
public int indexOfPane(Component pane) {
int index = indexOf(pane);
if (index == -1)
return -1;
else {
if (index % 2 != 0)
//noinspection UseOfSystemOutOrSystemErr
System.err.println("Warning: pane's index is odd. (index = " + index + ")");
return index >> 1;
}
}
/**
* Returns the index of the component.
*
* @param comp component to get index
* @return index of the comp. -1 if comp doesn't exist in this container
*/
public int indexOf(Component comp) {
for (int i = 0; i < getComponentCount(); i++) {
if (getComponent(i).equals(comp))
return i;
}
return -1;
}
/**
* Returns the divider at index.
*
* @param index index
* @return the divider at the index
*/
public JideSplitPaneDivider getDividerAt(int index) {
if (index < 0 || index * 2 + 1 >= getComponentCount())
return null;
return (JideSplitPaneDivider) getComponent(index * 2 + 1);
}
/**
* Returns the component at index.
*
* @param index index
* @return the component at the index
*/
public Component getPaneAt(int index) {
if (index < 0 || index << 1 >= getComponentCount())
return null;
return getComponent(index << 1);
}
/**
* Gets the count of panes, regardless of dividers.
*
* @return the count of panes
*/
public int getPaneCount() {
return (getComponentCount() + 1) >> 1;
}
/**
* Set the divider location.
*
* @param divider the divider
* @param location new location
*/
public void setDividerLocation(JideSplitPaneDivider divider, int location) {
setDividerLocation(indexOfDivider(divider), location);
}
/**
* Set the divider location. You can only call this method to set the divider location when the component is
* rendered on the screen. If the component has never been displayed before, this method call has no effect.
*
* @param dividerIndex the divider index, starting from 0 for the first divider.
* @param location new location
*/
public void setDividerLocation(int dividerIndex, int location) {
((JideSplitPaneLayout) getLayout()).setDividerLocation(dividerIndex, location, true);
validate();
}
/**
* Get the divider location. You can only get a valid divider location when the component is displayed on the
* screen. If the component has never been displayed on screen, -1 will be returned.
*
* @param dividerIndex the divider index
* @return the location of the divider.
*/
public int getDividerLocation(int dividerIndex) {
return ((JideSplitPaneLayout) getLayout()).getDividerLocation(dividerIndex);
}
/**
* Invoked when a component has been added to the container. Basically if you add anything which is not divider, a
* divider will automatically added before or after the component.
*
* @param e ContainerEvent
*/
public void componentAdded(ContainerEvent e) {
e.getChild().addComponentListener(this);
if (!(e.getChild() instanceof JideSplitPaneDivider)) {
addExtraDividers();
if (isOneTouchExpandable()) {
e.getChild().setMinimumSize(new Dimension(0, 0));
}
}
setDividersVisible();
resetToPreferredSizes();
}
/**
* Invoked when a component has been removed from the container. Basically if you remove anything which is not
* divider, a divider will automatically deleted before or after the component.
*
* @param e ContainerEvent
*/
public void componentRemoved(ContainerEvent e) {
e.getChild().removeComponentListener(this);
if (!(e.getChild() instanceof JideSplitPaneDivider)) {
removeExtraDividers();
}
setDividersVisible();
resetToPreferredSizes();
/*
if (getComponentCount() == 1 && getComponent(0) instanceof JideSplitPane) {
JideSplitPane childPane = (JideSplitPane)getComponent(0);
if(getOrientation() != childPane.getOrientation()) {
setOrientation(childPane.getOrientation());
}
// copy all children of its splitpane child to this
boolean savedAutoRemove = childPane.isAutomaticallyRemove();
childPane.setAutomaticallyRemove(false);
for(int i = 0; i < childPane.getComponentCount(); i ++) {
if(childPane.getComponent(i) instanceof JideSplitPaneDivider)
continue;
System.out.println("Adding " + childPane.getComponent(i));
add(childPane.getComponent(i));
i --;
}
childPane.setAutomaticallyRemove(savedAutoRemove);
System.out.println("Removing " + childPane);
remove(childPane);
}
if (isAutomaticallyRemove() && getComponentCount() == 0 && getParent() != null) {
System.out.println("Automatically Removing this " + this);
getParent().remove(this);
return;
}
*/
}
public void componentResized(ComponentEvent e) {
}
public void componentMoved(ComponentEvent e) {
}
public void componentShown(ComponentEvent e) {
if (e.getComponent() instanceof JideSplitPaneDivider) {
return;
}
setDividersVisible();
resetToPreferredSizes();
}
public void componentHidden(ComponentEvent e) {
if (e.getComponent() instanceof JideSplitPaneDivider) {
return;
}
setDividersVisible();
resetToPreferredSizes();
}
/**
* Remove extra divider. One is considered as extra dividers where two dividers are adjacent.
*
* @return true if dividers are removed.
*/
protected boolean removeExtraDividers() {
int extra = 0;
if (getComponentCount() == 0) {
if (_proportions != null)
setProportions(null);
return false;
}
boolean changed = false;
// remove first divider if it's one
if (getComponent(0) instanceof JideSplitPaneDivider) {
((JideSplitPaneDivider) getComponent(0)).setJideSplitPane(null);
remove(0);
removeProportion(0);
changed = true;
}
for (int i = 0; i < getComponentCount(); i++) {
Component comp = getComponent(i);
if (comp instanceof JideSplitPaneDivider) {
extra++;
if (extra == 2) {
comp = getComponent(i - 1); // remove the first one which was newly added
((JideSplitPaneDivider) comp).setJideSplitPane(null);
remove(comp);
if (_proportions != null && getPaneCount() == _proportions.length)
removeProportion(i / 2);
changed = true;
extra--;
i--;
}
}
else
extra = 0;
}
if (extra == 1 && getComponent(getComponentCount() - 1) instanceof JideSplitPaneDivider) { // remove last one if it's a divider
((JideSplitPaneDivider) getComponent(getComponentCount() - 1)).setJideSplitPane(null);
remove(getComponentCount() - 1);
removeProportion((getComponentCount() + 1) / 2);
changed = true;
}
return changed;
}
/**
* Removes the proportion at the given pane index, spreading its value proportionally across the other proportions.
* If it's the last proportion being removed, sets the proportions to null.
*
* @param paneIndex the pane index.
*/
protected void removeProportion(int paneIndex) {
double[] oldProportions = _proportions;
if (oldProportions == null)
return;
if (oldProportions.length <= 1) {
setProportions(null);
return;
}
double[] newProportions = new double[oldProportions.length - 1];
double p;
if (paneIndex < oldProportions.length)
p = oldProportions[paneIndex];
else {
p = 1.0;
for (double proportion : oldProportions) p -= proportion;
}
double total = 1.0 - p;
for (int i = 0; i < newProportions.length; ++i) {
int j = (i < paneIndex) ? i : i + 1;
newProportions[i] = oldProportions[j] / total;
}
// to make sure the proportion adds up to 1 for this special case.
if (newProportions.length == 1) {
// newProportions[0] = 1.0;
}
setProportions(newProportions);
}
/**
* Add divider if there are two panes side by side without a divider in between.
*/
protected void addExtraDividers() {
int extra = 0;
for (int i = 0; i < getComponentCount(); i++) {
Component comp = getComponent(i);
if (!(comp instanceof JideSplitPaneDivider)) {
extra++;
if (extra == 2) {
add(createSplitPaneDivider(), JideSplitPaneLayout.FIX, i);
if (_proportions != null && getPaneCount() == _proportions.length + 2)
addProportion((i + 1) / 2);
extra = 0;
}
}
else
extra = 0;
}
}
/**
* Adds a proportion at the given pane index, taking a proportional amount from each of the existing proportions.
*
* @param paneIndex the pane index.
*/
protected void addProportion(int paneIndex) {
double[] oldProportions = _proportions;
if (oldProportions == null)
return;
double[] newProportions = new double[oldProportions.length + 1];
double p = 1.0 / (newProportions.length + 1);
double total = 1.0 - p;
for (int i = 0; i < newProportions.length; ++i) {
if (i == paneIndex)
newProportions[i] = p;
else {
int j = (i < paneIndex) ? i : i - 1;
if (j < oldProportions.length)
newProportions[i] = oldProportions[j] * total;
else
newProportions[i] = p;
}
}
setProportions(newProportions);
}
@Override
public void setVisible(boolean aFlag) {
_hiddenByMyself = false;
super.setVisible(aFlag);
}
/**
* Before this method is call, the panes must be separated by dividers.
*/
protected void setDividersVisible() {
boolean anyVisible = false;
if (getComponentCount() == 1) {
anyVisible = getComponent(0).isVisible();
}
else if (getComponentCount() > 1) {
boolean anyPrevVisible = false;
for (int i = 0; i < getComponentCount(); i++) {
Component comp = getComponent(i);
if (!(comp instanceof JideSplitPaneDivider)) {
if (comp.isVisible() && !anyVisible) {
anyVisible = true;
}
continue;
}
boolean visiblePrev = i - 1 >= 0 && getComponent(i - 1).isVisible();
boolean visibleNext = i + 1 < getComponentCount() && getComponent(i + 1).isVisible();
if (visiblePrev && visibleNext) {
comp.setVisible(true);
}
else if (!visiblePrev && !visibleNext) {
comp.setVisible(false);
}
else if (visiblePrev && !visibleNext) {
comp.setVisible(false);
anyPrevVisible = true;
}
else /*if (visibleNext && !visiblePrev)*/ {
if (anyPrevVisible) {
comp.setVisible(true);
anyPrevVisible = false;
}
else {
comp.setVisible(false);
}
}
}
}
if (!anyVisible) {
super.setVisible(false);
_hiddenByMyself = true;
}
else if (_hiddenByMyself) {
super.setVisible(true);
}
}
protected JideSplitPaneDivider createSplitPaneDivider() {
return new JideSplitPaneDivider(this);
}
/**
* Get previous divider's, if any, location from current divider. If there is no previous divider, return 0.
*
* @param divider the divider
* @param ignoreVisibility true to not check if the pane is visible.
* @param reversed from left to right or reversed.
* @return the location of previous divider if any
*/
protected int getPreviousDividerLocation(JideSplitPaneDivider divider, boolean ignoreVisibility, boolean reversed) {
int index = indexOfDivider(divider);
int location = -1;
if (reversed) {
if (((index + 1) * 2) + 1 <= getComponentCount()) {
for (int i = index + 1; (i * 2) + 1 < getComponentCount(); i++) {
if (ignoreVisibility || getDividerAt(i).isVisible()) {
if (_orientation == HORIZONTAL_SPLIT) {
location = getDividerAt(i).getBounds().x;
}
else {
location = getDividerAt(i).getBounds().y;
}
break;
}
}
}
}
else {
if (index > 0) {
for (int i = index - 1; i >= 0; i--) {
if (ignoreVisibility || getDividerAt(i).isVisible()) {
if (_orientation == HORIZONTAL_SPLIT) {
location = getDividerAt(i).getBounds().x;
}
else {
location = getDividerAt(i).getBounds().y;
}
break;
}
}
}
}
if (location != -1) {
return location + getDividerSize();
}
return 0;
}
/**
* Get previous divider's, if any, location from current divider. If there is no previous divider, return 0.
*
* @param divider the divider
* @param ignoreVisibility true to not check if the pane is visible.
* @param reversed from left to right or reversed.
* @return the location of next divider if any
*/
public int getNextDividerLocation(JideSplitPaneDivider divider, boolean ignoreVisibility, boolean reversed) {
int index = indexOfDivider(divider);
int location = -1;
if (!reversed) {
if (((index + 1) * 2) + 1 <= getComponentCount()) {
for (int i = index + 1; (i * 2) + 1 < getComponentCount(); i++) {
if (ignoreVisibility || getDividerAt(i).isVisible()) {
if (_orientation == HORIZONTAL_SPLIT) {
location = getDividerAt(i).getBounds().x;
}
else {
location = getDividerAt(i).getBounds().y;
}
break;
}
}
}
}
else {
if (index > 0) {
for (int i = index - 1; i >= 0; i--) {
if (ignoreVisibility || getDividerAt(i).isVisible()) {
if (_orientation == HORIZONTAL_SPLIT) {
location = getDividerAt(i).getBounds().x;
}
else {
location = getDividerAt(i).getBounds().y;
}
break;
}
}
}
}
if (location != -1) {
return location - getDividerSize();
}
return getOrientation() == HORIZONTAL_SPLIT ? getWidth() - getDividerSize() : getHeight() - getDividerSize();
}
/**
* Checks if the gripper is visible.
*
* @return true if gripper is visible
*/
public boolean isShowGripper() {
return _showGripper;
}
/**
* Sets the visibility of gripper.
*
* @param showGripper true to show gripper
*/
public void setShowGripper(boolean showGripper) {
boolean oldShowGripper = _showGripper;
if (oldShowGripper != showGripper) {
_showGripper = showGripper;
firePropertyChange(GRIPPER_PROPERTY, oldShowGripper, _showGripper);
}
}
/**
* Causes this container to lay out its components. Most programs should not call this method directly, but should
* invoke the validate
method instead.
*
* @see LayoutManager#layoutContainer
* @see #setLayout
* @see #validate
* @since JDK1.1
*/
@Override
public void doLayout() {
if (removeExtraDividers()) {
((JideSplitPaneLayout) getLayout()).invalidateLayout(this);
}
super.doLayout();
}
/**
* Determines whether the JSplitPane is set to use a continuous layout.
*
* @return true or false.
*/
public boolean isContinuousLayout() {
return _continuousLayout;
}
/**
* Turn continuous layout on/off.
*
* @param continuousLayout true or false.
*/
public void setContinuousLayout(boolean continuousLayout) {
boolean oldCD = _continuousLayout;
_continuousLayout = continuousLayout;
firePropertyChange(CONTINUOUS_LAYOUT_PROPERTY, oldCD, continuousLayout);
}
/**
* Gets the AccessibleContext associated with this JideSplitPane. For split panes, the AccessibleContext takes the
* form of an AccessibleJideSplitPane. A new AccessibleJideSplitPane instance is created if necessary.
*
* @return an AccessibleJideSplitPane that serves as the AccessibleContext of this JideSplitPane
*/
@Override
public AccessibleContext getAccessibleContext() {
if (accessibleContext == null) {
accessibleContext = new AccessibleJideSplitPane();
}
return accessibleContext;
}
/**
* Get the flag indicating if dragging the divider could resize the panes.
*
* By default, the value is true for JideSplitPane. You could set it to false if you don't like it.
*
* @return the flag.
*/
public boolean isDragResizable() {
return _dragResizable;
}
/**
* Set the flag indicating if dragging the divider could resize the panes.
*
* @param dragResizable the flag
*/
public void setDragResizable(boolean dragResizable) {
_dragResizable = dragResizable;
}
/**
* This class implements accessibility support for the JideSplitPane
class. It provides an
* implementation of the Java Accessibility API appropriate to split pane user-interface elements.
*/
protected class AccessibleJideSplitPane extends AccessibleJComponent {
private static final long serialVersionUID = -6167624875135108683L;
/**
* Gets the state set of this object.
*
* @return an instance of AccessibleState containing the current state of the object
*
* @see javax.accessibility.AccessibleState
*/
@Override
public AccessibleStateSet getAccessibleStateSet() {
AccessibleStateSet states = super.getAccessibleStateSet();
if (getOrientation() == VERTICAL_SPLIT) {
states.add(AccessibleState.VERTICAL);
}
else {
states.add(AccessibleState.HORIZONTAL);
}
return states;
}
/**
* Gets the role of this object.
*
* @return an instance of AccessibleRole describing the role of the object
*
* @see AccessibleRole
*/
@Override
public AccessibleRole getAccessibleRole() {
return AccessibleRole.SPLIT_PANE;
}
} // inner class AccessibleJideSplitPane
/**
* @return true if the heavyweight component is enabled.
*/
public boolean isHeavyweightComponentEnabled() {
return _heavyweightComponentEnabled;
}
/**
* Enables heavyweight components. The difference is the divider. If true, the divider will be heavyweight divider.
* Otherwise it will use lightweight divider.
*
* @param heavyweightComponentEnabled true to enable the usage of heavyweight components.
*/
public void setHeavyweightComponentEnabled(boolean heavyweightComponentEnabled) {
boolean old = _heavyweightComponentEnabled;
if (_heavyweightComponentEnabled != heavyweightComponentEnabled) {
_heavyweightComponentEnabled = heavyweightComponentEnabled;
firePropertyChange(PROPERTY_HEAVYWEIGHT_COMPONENT_ENABLED, old, _heavyweightComponentEnabled);
}
}
/*
* Added on 05/14/2008 in response to http://www.jidesoft.com/forum/viewtopic.php?p=26074#26074,
* http://www.jidesoft.com/forum/viewtopic.php?p=5148#5148 and
* http://www.jidesoft.com/forum/viewtopic.php?p=23403#23403.
*
* The addition below provides the option of adding a one-touch button which is capable of expanding/collapsing the
* split pane (in one click of the mouse).
*
* @see #setOneTouchExpandable(boolean)
*/
/**
* Bound property for oneTouchExpandable
.
*
* @see #setOneTouchExpandable
*/
public static final String ONE_TOUCH_EXPANDABLE_PROPERTY = "oneTouchExpandable";
/**
* Flag indicating whether the SplitPane's divider should be one-touch expandable/collapsible. The default value of
* this property is false
*
* @see #setOneTouchExpandable
* @see #isOneTouchExpandable
*/
private boolean _oneTouchExpandable = false;
/**
* The default width/height of the divider (when horizontally/vertically split respectively).
*/
private int oneTouchExpandableDividerSize = 8;
/**
* The image displayed on the left one-touch button. If no image is supplied, a default triangle will be painted
* onto the button.
*
* @see #setLeftOneTouchButtonImageIcon
*/
private ImageIcon _leftOneTouchButtonImageIcon = null;
/**
* The image displayed on the right one-touch button. If no image is supplied, a default triangle will be painted
* onto the button.
*
* @see #setRightOneTouchButtonImageIcon
*/
private ImageIcon _rightOneTouchButtonImageIcon = null;
/**
* Sets the value of the oneTouchExpandable
property. If true
, the JSplitPane
* will display a UI widget on the divider to quickly expand/collapse the divider.
The default value of this
* property is false
.
Please note: Some look and feels might not support one-touch expanding;
* they will ignore this property.
*
* @param oneTouchExpandable true
to specify that the split pane should provide a collapse/expand
* widget
* @see #isOneTouchExpandable
*/
public void setOneTouchExpandable(boolean oneTouchExpandable) {
boolean oldValue = _oneTouchExpandable;
if (oldValue != oneTouchExpandable) {
_oneTouchExpandable = oneTouchExpandable;
/*
* We need to widen/shrink the dividers width so that we can display/remove the one-touch buttons.
*/
LayoutManager layoutManager = getLayout();
if (layoutManager instanceof JideBoxLayout) {
((JideBoxLayout) layoutManager).setResetWhenInvalidate(true);
}
if (oneTouchExpandable) {
setDividerSize(oneTouchExpandableDividerSize);
}
else {
setDividerSize(UIDefaultsLookup.getInt("JideSplitPane.dividerSize"));
}
/*
* We now fire a bound property so each divider listening can set up its own one-touch buttons.
*/
firePropertyChange(ONE_TOUCH_EXPANDABLE_PROPERTY, oldValue, _oneTouchExpandable);
revalidate();
repaint();
if (layoutManager instanceof JideBoxLayout) {
((JideBoxLayout) layoutManager).setResetWhenInvalidate(false);
}
}
}
/**
* Returns whether one-touch expand/collapse is on.
*
* @return the value of the oneTouchExpandable
property
*
* @see #setOneTouchExpandable
*/
public boolean isOneTouchExpandable() {
return _oneTouchExpandable;
}
/**
* Sets the left button's image icon. By default, the button has a width of 5 pixels and a height of 10 pixel in
* HORIZONTAL_SPLIT mode (and a width of 10 pixels and a height of 5 pixel in VERTICAL_SPLIT mode) -- this should be
* considered when assigning its imageIcon.
*
* @param leftButtonImageIcon the image to be displayed on the left one-touch button
*/
public void setLeftOneTouchButtonImageIcon(ImageIcon leftButtonImageIcon) {
_leftOneTouchButtonImageIcon = leftButtonImageIcon;
}
/**
* Gets the left button's image icon.
*
* @return the imageIcon used displayed on the left one-touch button
*/
public ImageIcon getLeftOneTouchButtonImageIcon() {
return _leftOneTouchButtonImageIcon;
}
/**
* Sets the right button's image icon. By default, the button has a width of 5 pixels and a height of 10 pixel in
* HORIZONTAL_SPLIT mode (and a width of 10 pixels and a height of 5 pixel in VERTICAL_SPLIT mode) -- this should be
* considered when assigning its imageIcon.
*
* @param rightButtonImageIcon the image to be displayed on the right one-touch button
*/
public void setRightOneTouchButtonImageIcon(ImageIcon rightButtonImageIcon) {
_rightOneTouchButtonImageIcon = rightButtonImageIcon;
}
/**
* Gets the right button's image icon.
*
* @return the imageIcon used displayed on the left one-touch button
*/
public ImageIcon getRightOneTouchButtonImageIcon() {
return _rightOneTouchButtonImageIcon;
}
/**
* Sets the divider locations.
*
* @param locations the new divider locations.
*/
public void setDividerLocations(int[] locations) {
for (int i = 0; i < locations.length; i++) {
int location = locations[i];
setDividerLocation(i, location);
}
}
/**
* Gets the divider locations.
*
* @return the divider locations.
*/
public int[] getDividerLocations() {
int count = getPaneCount();
if (getPaneCount() == 0) {
return new int[0];
}
int[] locations = new int[count - 1];
for (int i = 0; i < count - 1; i++) {
locations[i] = getDividerLocation(i);
}
return locations;
}
private class JideSplitPaneContour extends Contour {
public JideSplitPaneContour() {
super();
}
public JideSplitPaneContour(int tabHeight) {
super(tabHeight);
}
}
private class JideSplitPaneHeavyweightWrapper extends HeavyweightWrapper {
public JideSplitPaneHeavyweightWrapper(Component component) {
super(component);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy