| /* BasicSplitPaneUI.java -- |
| Copyright (C) 2003, 2004, 2005, 2006, Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package javax.swing.plaf.basic; |
| |
| import java.awt.Canvas; |
| import java.awt.Color; |
| import java.awt.Component; |
| import java.awt.Container; |
| import java.awt.Dimension; |
| import java.awt.Graphics; |
| import java.awt.Insets; |
| import java.awt.LayoutManager2; |
| import java.awt.Point; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.event.FocusAdapter; |
| import java.awt.event.FocusEvent; |
| import java.awt.event.FocusListener; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| |
| import javax.swing.AbstractAction; |
| import javax.swing.ActionMap; |
| import javax.swing.InputMap; |
| import javax.swing.JComponent; |
| import javax.swing.JSlider; |
| import javax.swing.JSplitPane; |
| import javax.swing.KeyStroke; |
| import javax.swing.LookAndFeel; |
| import javax.swing.SwingUtilities; |
| import javax.swing.UIManager; |
| import javax.swing.plaf.ActionMapUIResource; |
| import javax.swing.plaf.ComponentUI; |
| import javax.swing.plaf.SplitPaneUI; |
| import javax.swing.plaf.UIResource; |
| |
| /** |
| * This is the Basic Look and Feel implementation of the SplitPaneUI class. |
| */ |
| public class BasicSplitPaneUI extends SplitPaneUI |
| { |
| /** |
| * This Layout Manager controls the position and size of the components when |
| * the JSplitPane's orientation is HORIZONTAL_SPLIT. |
| * |
| * @specnote Apparently this class was intended to be protected, |
| * but was made public by a compiler bug and is now |
| * public for compatibility. |
| */ |
| public class BasicHorizontalLayoutManager implements LayoutManager2 |
| { |
| // 3 components at a time. |
| // LEFT/TOP = 0 |
| // RIGHT/BOTTOM = 1 |
| // DIVIDER = 2 |
| |
| /** |
| * This array contains the components in the JSplitPane. The left/top |
| * component is at index 0, the right/bottom is at 1, and the divider is |
| * at 2. |
| */ |
| protected Component[] components = new Component[3]; |
| |
| // These are the _current_ widths of the associated component. |
| |
| /** |
| * This array contains the current width (for HORIZONTAL_SPLIT) or height |
| * (for VERTICAL_SPLIT) of the components. The indices are the same as |
| * for components. |
| */ |
| protected int[] sizes = new int[3]; |
| |
| /** |
| * Creates a new instance. This is package private because the reference |
| * implementation has no public constructor either. Still, we need to |
| * call it from BasicVerticalLayoutManager. |
| */ |
| BasicHorizontalLayoutManager() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * This method adds the component given to the JSplitPane. The position of |
| * the component is given by the constraints object. |
| * |
| * @param comp The Component to add. |
| * @param constraints The constraints that bind the object. |
| */ |
| public void addLayoutComponent(Component comp, Object constraints) |
| { |
| addLayoutComponent((String) constraints, comp); |
| } |
| |
| /** |
| * This method is called to add a Component to the JSplitPane. The |
| * placement string determines where the Component will be placed. The |
| * string should be one of LEFT, RIGHT, TOP, BOTTOM or null (signals that |
| * the component is the divider). |
| * |
| * @param place The placement of the Component. |
| * @param component The Component to add. |
| * |
| * @throws IllegalArgumentException DOCUMENT ME! |
| */ |
| public void addLayoutComponent(String place, Component component) |
| { |
| int i = 0; |
| if (place == null) |
| i = 2; |
| else if (place.equals(JSplitPane.TOP) || place.equals(JSplitPane.LEFT)) |
| i = 0; |
| else if (place.equals(JSplitPane.BOTTOM) |
| || place.equals(JSplitPane.RIGHT)) |
| i = 1; |
| else |
| throw new IllegalArgumentException("Illegal placement in JSplitPane"); |
| components[i] = component; |
| resetSizeAt(i); |
| splitPane.revalidate(); |
| splitPane.repaint(); |
| } |
| |
| /** |
| * This method returns the width of the JSplitPane minus the insets. |
| * |
| * @param containerSize The Dimensions of the JSplitPane. |
| * @param insets The Insets of the JSplitPane. |
| * |
| * @return The width of the JSplitPane minus the insets. |
| */ |
| protected int getAvailableSize(Dimension containerSize, Insets insets) |
| { |
| return containerSize.width - insets.left - insets.right; |
| } |
| |
| /** |
| * This method returns the given insets left value. If the given inset is |
| * null, then 0 is returned. |
| * |
| * @param insets The Insets to use with the JSplitPane. |
| * |
| * @return The inset's left value. |
| */ |
| protected int getInitialLocation(Insets insets) |
| { |
| if (insets != null) |
| return insets.left; |
| return 0; |
| } |
| |
| /** |
| * This specifies how a component is aligned with respect to other |
| * components in the x fdirection. |
| * |
| * @param target The container. |
| * |
| * @return The component's alignment. |
| */ |
| public float getLayoutAlignmentX(Container target) |
| { |
| return target.getAlignmentX(); |
| } |
| |
| /** |
| * This specifies how a component is aligned with respect to other |
| * components in the y direction. |
| * |
| * @param target The container. |
| * |
| * @return The component's alignment. |
| */ |
| public float getLayoutAlignmentY(Container target) |
| { |
| return target.getAlignmentY(); |
| } |
| |
| /** |
| * This method returns the preferred width of the component. |
| * |
| * @param c The component to measure. |
| * |
| * @return The preferred width of the component. |
| */ |
| protected int getPreferredSizeOfComponent(Component c) |
| { |
| Dimension dims = c.getPreferredSize(); |
| if (dims != null) |
| return dims.width; |
| return 0; |
| } |
| |
| /** |
| * This method returns the current width of the component. |
| * |
| * @param c The component to measure. |
| * |
| * @return The width of the component. |
| */ |
| protected int getSizeOfComponent(Component c) |
| { |
| return c.getWidth(); |
| } |
| |
| /** |
| * This method returns the sizes array. |
| * |
| * @return The sizes array. |
| */ |
| protected int[] getSizes() |
| { |
| return sizes; |
| } |
| |
| /** |
| * This method invalidates the layout. It does nothing. |
| * |
| * @param c The container to invalidate. |
| */ |
| public void invalidateLayout(Container c) |
| { |
| // DO NOTHING |
| } |
| |
| /** |
| * This method lays out the components in the container. |
| * |
| * @param container The container to lay out. |
| */ |
| public void layoutContainer(Container container) |
| { |
| if (container instanceof JSplitPane) |
| { |
| JSplitPane split = (JSplitPane) container; |
| distributeExtraSpace(); |
| Insets insets = split.getInsets(); |
| Dimension dims = split.getSize(); |
| int loc = getInitialLocation(insets); |
| int available = getAvailableSize(dims, insets); |
| sizes[0] = getDividerLocation(split) - loc; |
| sizes[1] = available - sizes[0] - sizes[2]; |
| // The size of the divider won't change. |
| |
| // Layout component#1. |
| setComponentToSize(components[0], sizes[0], loc, insets, dims); |
| // Layout divider. |
| loc += sizes[0]; |
| setComponentToSize(components[2], sizes[2], loc, insets, dims); |
| // Layout component#2. |
| loc += sizes[2]; |
| setComponentToSize(components[1], sizes[1], loc, insets, dims); |
| } |
| } |
| |
| /** |
| * This method returns the maximum size for the container given the |
| * components. It returns a new Dimension object that has width and |
| * height equal to Integer.MAX_VALUE. |
| * |
| * @param target The container to measure. |
| * |
| * @return The maximum size. |
| */ |
| public Dimension maximumLayoutSize(Container target) |
| { |
| return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); |
| } |
| |
| /** |
| * This method returns the container's minimum size. The minimum width is |
| * the sum of all the component's minimum widths. The minimum height is |
| * the maximum of all the components' minimum heights. |
| * |
| * @param target The container to measure. |
| * |
| * @return The minimum size. |
| */ |
| public Dimension minimumLayoutSize(Container target) |
| { |
| if (target instanceof JSplitPane) |
| { |
| JSplitPane split = (JSplitPane) target; |
| Insets insets = target.getInsets(); |
| |
| int height = 0; |
| int width = 0; |
| for (int i = 0; i < components.length; i++) |
| { |
| if (components[i] == null) |
| continue; |
| Dimension dims = components[i].getMinimumSize(); |
| if (dims != null) |
| { |
| width += dims.width; |
| height = Math.max(height, dims.height); |
| } |
| } |
| return new Dimension(width, height); |
| } |
| return null; |
| } |
| |
| /** |
| * This method returns the container's preferred size. The preferred width |
| * is the sum of all the component's preferred widths. The preferred |
| * height is the maximum of all the components' preferred heights. |
| * |
| * @param target The container to measure. |
| * |
| * @return The preferred size. |
| */ |
| public Dimension preferredLayoutSize(Container target) |
| { |
| if (target instanceof JSplitPane) |
| { |
| JSplitPane split = (JSplitPane) target; |
| Insets insets = target.getInsets(); |
| |
| int height = 0; |
| int width = 0; |
| for (int i = 0; i < components.length; i++) |
| { |
| if (components[i] == null) |
| continue; |
| Dimension dims = components[i].getPreferredSize(); |
| if (dims != null) |
| { |
| width += dims.width; |
| if (!(components[i] instanceof BasicSplitPaneDivider)) |
| height = Math.max(height, dims.height); |
| } |
| } |
| return new Dimension(width, height); |
| } |
| return null; |
| } |
| |
| /** |
| * This method removes the component from the layout. |
| * |
| * @param component The component to remove from the layout. |
| */ |
| public void removeLayoutComponent(Component component) |
| { |
| for (int i = 0; i < components.length; i++) |
| { |
| if (component == components[i]) |
| { |
| components[i] = null; |
| sizes[i] = 0; |
| } |
| } |
| } |
| |
| /** |
| * This method resets the size of Component to the preferred size. |
| * |
| * @param index The index of the component to reset. |
| */ |
| protected void resetSizeAt(int index) |
| { |
| if (components[index] != null) |
| sizes[index] = getPreferredSizeOfComponent(components[index]); |
| } |
| |
| /** |
| * This method resets the sizes of all the components. |
| */ |
| public void resetToPreferredSizes() |
| { |
| for (int i = 0; i < components.length; i++) |
| resetSizeAt(i); |
| setDividerLocation(splitPane, |
| getInitialLocation(splitPane.getInsets()) + sizes[0]); |
| } |
| |
| /** |
| * This methods sets the bounds of the given component. The width is the |
| * size. The height is the container size minus the top and bottom |
| * inset. The x coordinate is the location given. The y coordinate is |
| * the top inset. |
| * |
| * @param c The component to set. |
| * @param size The width of the component. |
| * @param location The x coordinate. |
| * @param insets The insets to use. |
| * @param containerSize The height of the container. |
| */ |
| protected void setComponentToSize(Component c, int size, int location, |
| Insets insets, Dimension containerSize) |
| { |
| int w = size; |
| int h = containerSize.height - insets.top - insets.bottom; |
| int x = location; |
| int y = insets.top; |
| c.setBounds(x, y, w, h); |
| } |
| |
| /** |
| * This method stores the given int array as the new sizes array. |
| * |
| * @param newSizes The array to use as sizes. |
| */ |
| protected void setSizes(int[] newSizes) |
| { |
| sizes = newSizes; |
| } |
| |
| /** |
| * This method determines the size of each component. It should be called |
| * when a new Layout Manager is created for an existing JSplitPane. |
| */ |
| protected void updateComponents() |
| { |
| Component left = splitPane.getLeftComponent(); |
| Component right = splitPane.getRightComponent(); |
| |
| if (left != null) |
| { |
| components[0] = left; |
| resetSizeAt(0); |
| } |
| if (right != null) |
| { |
| components[1] = right; |
| resetSizeAt(1); |
| } |
| components[2] = divider; |
| resetSizeAt(2); |
| } |
| |
| /** |
| * This method resizes the left and right components to fit inside the |
| * JSplitPane when there is extra space. |
| */ |
| void distributeExtraSpace() |
| { |
| // FIXME: This needs to be reimplemented correctly. |
| } |
| |
| /** |
| * This method returns the minimum width of the component at the given |
| * index. |
| * |
| * @param index The index to check. |
| * |
| * @return The minimum width. |
| */ |
| int minimumSizeOfComponent(int index) |
| { |
| Dimension dims = components[index].getMinimumSize(); |
| if (dims != null) |
| return dims.width; |
| else |
| return 0; |
| } |
| } //end BasicHorizontalLayoutManager |
| |
| /** |
| * This class is the Layout Manager for the JSplitPane when the orientation |
| * is VERTICAL_SPLIT. |
| * |
| * @specnote Apparently this class was intended to be protected, |
| * but was made public by a compiler bug and is now |
| * public for compatibility. |
| */ |
| public class BasicVerticalLayoutManager |
| extends BasicHorizontalLayoutManager |
| { |
| /** |
| * This method returns the height of the container minus the top and |
| * bottom inset. |
| * |
| * @param containerSize The size of the container. |
| * @param insets The insets of the container. |
| * |
| * @return The height minus top and bottom inset. |
| */ |
| protected int getAvailableSize(Dimension containerSize, Insets insets) |
| { |
| return containerSize.height - insets.top - insets.bottom; |
| } |
| |
| /** |
| * This method returns the top inset. |
| * |
| * @param insets The Insets to use. |
| * |
| * @return The top inset. |
| */ |
| protected int getInitialLocation(Insets insets) |
| { |
| return insets.top; |
| } |
| |
| /** |
| * This method returns the preferred height of the component. |
| * |
| * @param c The component to measure. |
| * |
| * @return The preferred height of the component. |
| */ |
| protected int getPreferredSizeOfComponent(Component c) |
| { |
| Dimension dims = c.getPreferredSize(); |
| if (dims != null) |
| return dims.height; |
| return 0; |
| } |
| |
| /** |
| * This method returns the current height of the component. |
| * |
| * @param c The component to measure. |
| * |
| * @return The current height of the component. |
| */ |
| protected int getSizeOfComponent(Component c) |
| { |
| return c.getHeight(); |
| } |
| |
| /** |
| * This method returns the minimum layout size. The minimum height is the |
| * sum of all the components' minimum heights. The minimum width is the |
| * maximum of all the components' minimum widths. |
| * |
| * @param container The container to measure. |
| * |
| * @return The minimum size. |
| */ |
| public Dimension minimumLayoutSize(Container container) |
| { |
| if (container instanceof JSplitPane) |
| { |
| JSplitPane split = (JSplitPane) container; |
| Insets insets = container.getInsets(); |
| |
| int height = 0; |
| int width = 0; |
| for (int i = 0; i < components.length; i++) |
| { |
| if (components[i] == null) |
| continue; |
| Dimension dims = components[i].getMinimumSize(); |
| if (dims != null) |
| { |
| height += dims.height; |
| width = Math.max(width, dims.width); |
| } |
| } |
| return new Dimension(width, height); |
| } |
| return null; |
| } |
| |
| /** |
| * This method returns the preferred layout size. The preferred height is |
| * the sum of all the components' preferred heights. The preferred width |
| * is the maximum of all the components' preferred widths. |
| * |
| * @param container The container to measure. |
| * |
| * @return The preferred size. |
| */ |
| public Dimension preferredLayoutSize(Container container) |
| { |
| if (container instanceof JSplitPane) |
| { |
| JSplitPane split = (JSplitPane) container; |
| Insets insets = container.getInsets(); |
| |
| int height = 0; |
| int width = 0; |
| for (int i = 0; i < components.length; i++) |
| { |
| if (components[i] == null) |
| continue; |
| Dimension dims = components[i].getPreferredSize(); |
| if (dims != null) |
| { |
| height += dims.height; |
| width = Math.max(width, dims.width); |
| } |
| } |
| return new Dimension(width, height); |
| } |
| return null; |
| } |
| |
| /** |
| * This method sets the bounds of the given component. The y coordinate is |
| * the location given. The x coordinate is the left inset. The height is |
| * the size given. The width is the container size minus the left and |
| * right inset. |
| * |
| * @param c The component to set bounds for. |
| * @param size The height. |
| * @param location The y coordinate. |
| * @param insets The insets to use. |
| * @param containerSize The container's size. |
| */ |
| protected void setComponentToSize(Component c, int size, int location, |
| Insets insets, Dimension containerSize) |
| { |
| int y = location; |
| int x = insets.left; |
| int h = size; |
| int w = containerSize.width - insets.left - insets.right; |
| c.setBounds(x, y, w, h); |
| } |
| |
| /** |
| * This method returns the minimum height of the component at the given |
| * index. |
| * |
| * @param index The index of the component to check. |
| * |
| * @return The minimum height of the given component. |
| */ |
| int minimumSizeOfComponent(int index) |
| { |
| Dimension dims = components[index].getMinimumSize(); |
| if (dims != null) |
| return dims.height; |
| else |
| return 0; |
| } |
| } |
| |
| /** |
| * This class handles FocusEvents from the JComponent. |
| * |
| * @specnote Apparently this class was intended to be protected, |
| * but was made public by a compiler bug and is now |
| * public for compatibility. |
| */ |
| public class FocusHandler extends FocusAdapter |
| { |
| /** |
| * This method is called when the JSplitPane gains focus. |
| * |
| * @param ev The FocusEvent. |
| */ |
| public void focusGained(FocusEvent ev) |
| { |
| // repaint the divider because its background color may change due to |
| // the focus state... |
| divider.repaint(); |
| } |
| |
| /** |
| * This method is called when the JSplitPane loses focus. |
| * |
| * @param ev The FocusEvent. |
| */ |
| public void focusLost(FocusEvent ev) |
| { |
| // repaint the divider because its background color may change due to |
| // the focus state... |
| divider.repaint(); |
| } |
| } |
| |
| /** |
| * This is a deprecated class. It is supposed to be used for handling down |
| * and right key presses. |
| * |
| * @specnote Apparently this class was intended to be protected, |
| * but was made public by a compiler bug and is now |
| * public for compatibility. |
| */ |
| public class KeyboardDownRightHandler implements ActionListener |
| { |
| /** |
| * This method is called when the down or right keys are pressed. |
| * |
| * @param ev The ActionEvent |
| */ |
| public void actionPerformed(ActionEvent ev) |
| { |
| // FIXME: implement. |
| } |
| } |
| |
| /** |
| * This is a deprecated class. It is supposed to be used for handling end |
| * key presses. |
| * |
| * @specnote Apparently this class was intended to be protected, |
| * but was made public by a compiler bug and is now |
| * public for compatibility. |
| */ |
| public class KeyboardEndHandler implements ActionListener |
| { |
| /** |
| * This method is called when the end key is pressed. |
| * |
| * @param ev The ActionEvent. |
| */ |
| public void actionPerformed(ActionEvent ev) |
| { |
| // FIXME: implement. |
| } |
| } |
| |
| /** |
| * This is a deprecated class. It is supposed to be used for handling home |
| * key presses. |
| * |
| * @specnote Apparently this class was intended to be protected, |
| * but was made public by a compiler bug and is now |
| * public for compatibility. |
| */ |
| public class KeyboardHomeHandler implements ActionListener |
| { |
| /** |
| * This method is called when the home key is pressed. |
| * |
| * @param ev The ActionEvent. |
| */ |
| public void actionPerformed(ActionEvent ev) |
| { |
| // FIXME: implement. |
| } |
| } |
| |
| /** |
| * This is a deprecated class. It is supposed to be used for handling resize |
| * toggles. |
| * |
| * @specnote Apparently this class was intended to be protected, |
| * but was made public by a compiler bug and is now |
| * public for compatibility. |
| */ |
| public class KeyboardResizeToggleHandler implements ActionListener |
| { |
| /** |
| * This method is called when a resize is toggled. |
| * |
| * @param ev The ActionEvent. |
| */ |
| public void actionPerformed(ActionEvent ev) |
| { |
| // FIXME: implement. |
| } |
| } |
| |
| /** |
| * This is a deprecated class. It is supposed to be used for handler up and |
| * left key presses. |
| * |
| * @specnote Apparently this class was intended to be protected, |
| * but was made public by a compiler bug and is now |
| * public for compatibility. |
| */ |
| public class KeyboardUpLeftHandler implements ActionListener |
| { |
| /** |
| * This method is called when the left or up keys are pressed. |
| * |
| * @param ev The ActionEvent. |
| */ |
| public void actionPerformed(ActionEvent ev) |
| { |
| // FIXME: implement. |
| } |
| } |
| |
| /** |
| * This helper class handles PropertyChangeEvents from the JSplitPane. When |
| * a property changes, this will update the UI accordingly. |
| * |
| * @specnote Apparently this class was intended to be protected, |
| * but was made public by a compiler bug and is now |
| * public for compatibility. |
| */ |
| public class PropertyHandler implements PropertyChangeListener |
| { |
| /** |
| * This method is called whenever one of the JSplitPane's properties |
| * change. |
| * |
| * @param e DOCUMENT ME! |
| */ |
| public void propertyChange(PropertyChangeEvent e) |
| { |
| if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY)) |
| { |
| int newSize = splitPane.getDividerSize(); |
| int[] tmpSizes = layoutManager.getSizes(); |
| dividerSize = tmpSizes[2]; |
| int newSpace = newSize - tmpSizes[2]; |
| tmpSizes[2] = newSize; |
| |
| tmpSizes[0] += newSpace / 2; |
| tmpSizes[1] += newSpace / 2; |
| |
| layoutManager.setSizes(tmpSizes); |
| } |
| else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY)) |
| { |
| int max = layoutManager.getAvailableSize(splitPane.getSize(), |
| splitPane.getInsets()); |
| int dividerLoc = getDividerLocation(splitPane); |
| double prop = ((double) dividerLoc) / max; |
| |
| resetLayoutManager(); |
| if (prop <= 1 && prop >= 0) |
| splitPane.setDividerLocation(prop); |
| } |
| // Don't have to deal with continuous_layout - only |
| // necessary in dragging modes (and it's checked |
| // every time you drag there) |
| // Don't have to deal with resize_weight (as there |
| // will be no extra space associated with this |
| // event - the changes to the weighting will |
| // be taken into account the next time the |
| // sizes change.) |
| // Don't have to deal with divider_location |
| // The method in JSplitPane calls our setDividerLocation |
| // so we'll know about those anyway. |
| // Don't have to deal with last_divider_location |
| // Although I'm not sure why, it doesn't seem to |
| // have any effect on Sun's JSplitPane. |
| // one_touch_expandable changes are dealt with |
| // by our divider. |
| } |
| } |
| |
| /** The location of the divider when dragging began. */ |
| protected int beginDragDividerLocation; |
| |
| /** The size of the divider while dragging. */ |
| protected int dividerSize; |
| |
| /** The location where the last drag location ended. */ |
| transient int lastDragLocation = -1; |
| |
| /** The distance the divider is moved when moved by keyboard actions. */ |
| // Sun defines this as 3 |
| protected static int KEYBOARD_DIVIDER_MOVE_OFFSET = 3; |
| |
| /** The divider that divides this JSplitPane. */ |
| protected BasicSplitPaneDivider divider; |
| |
| /** The listener that listens for PropertyChangeEvents from the JSplitPane. */ |
| protected PropertyChangeListener propertyChangeListener; |
| |
| /** The JSplitPane's focus handler. */ |
| protected FocusListener focusListener; |
| |
| /** @deprecated The handler for down and right key presses. */ |
| protected ActionListener keyboardDownRightListener; |
| |
| /** @deprecated The handler for end key presses. */ |
| protected ActionListener keyboardEndListener; |
| |
| /** @deprecated The handler for home key presses. */ |
| protected ActionListener keyboardHomeListener; |
| |
| /** @deprecated The handler for toggling resizes. */ |
| protected ActionListener keyboardResizeToggleListener; |
| |
| /** @deprecated The handler for up and left key presses. */ |
| protected ActionListener keyboardUpLeftListener; |
| |
| /** The JSplitPane's current layout manager. */ |
| protected BasicHorizontalLayoutManager layoutManager; |
| |
| /** @deprecated The divider resize toggle key. */ |
| protected KeyStroke dividerResizeToggleKey; |
| |
| /** @deprecated The down key. */ |
| protected KeyStroke downKey; |
| |
| /** @deprecated The end key. */ |
| protected KeyStroke endKey; |
| |
| /** @deprecated The home key. */ |
| protected KeyStroke homeKey; |
| |
| /** @deprecated The left key. */ |
| protected KeyStroke leftKey; |
| |
| /** @deprecated The right key. */ |
| protected KeyStroke rightKey; |
| |
| /** @deprecated The up key. */ |
| protected KeyStroke upKey; |
| |
| /** Set to true when dragging heavy weight components. */ |
| protected boolean draggingHW; |
| |
| /** |
| * The constraints object used when adding the non-continuous divider to the |
| * JSplitPane. |
| */ |
| protected static final String NON_CONTINUOUS_DIVIDER |
| = "nonContinuousDivider"; |
| |
| /** The dark divider used when dragging in non-continuous layout mode. */ |
| protected Component nonContinuousLayoutDivider; |
| |
| /** The JSplitPane that this UI draws. */ |
| protected JSplitPane splitPane; |
| |
| private int dividerLocation; |
| |
| /** |
| * Creates a new BasicSplitPaneUI object. |
| */ |
| public BasicSplitPaneUI() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * This method creates a new BasicSplitPaneUI for the given JComponent. |
| * |
| * @param x The JComponent to create a UI for. |
| * |
| * @return A new BasicSplitPaneUI. |
| */ |
| public static ComponentUI createUI(JComponent x) |
| { |
| return new BasicSplitPaneUI(); |
| } |
| |
| /** |
| * This method installs the BasicSplitPaneUI for the given JComponent. |
| * |
| * @param c The JComponent to install the UI for. |
| */ |
| public void installUI(JComponent c) |
| { |
| if (c instanceof JSplitPane) |
| { |
| splitPane = (JSplitPane) c; |
| installDefaults(); |
| installListeners(); |
| installKeyboardActions(); |
| } |
| } |
| |
| /** |
| * This method uninstalls the BasicSplitPaneUI for the given JComponent. |
| * |
| * @param c The JComponent to uninstall the UI for. |
| */ |
| public void uninstallUI(JComponent c) |
| { |
| uninstallKeyboardActions(); |
| uninstallListeners(); |
| uninstallDefaults(); |
| |
| splitPane = null; |
| } |
| |
| /** |
| * This method installs the defaults given by the Look and Feel. |
| */ |
| protected void installDefaults() |
| { |
| LookAndFeel.installColors(splitPane, "SplitPane.background", |
| "SplitPane.foreground"); |
| LookAndFeel.installBorder(splitPane, "SplitPane.border"); |
| divider = createDefaultDivider(); |
| divider.setBorder(UIManager.getBorder("SplitPaneDivider.border")); |
| resetLayoutManager(); |
| nonContinuousLayoutDivider = createDefaultNonContinuousLayoutDivider(); |
| splitPane.add(divider, JSplitPane.DIVIDER); |
| |
| // There is no need to add the nonContinuousLayoutDivider |
| splitPane.setDividerSize(UIManager.getInt("SplitPane.dividerSize")); |
| splitPane.setOpaque(true); |
| } |
| |
| /** |
| * This method uninstalls the defaults and nulls any objects created during |
| * install. |
| */ |
| protected void uninstallDefaults() |
| { |
| layoutManager = null; |
| splitPane.remove(divider); |
| divider = null; |
| nonContinuousLayoutDivider = null; |
| |
| if (splitPane.getBackground() instanceof UIResource) |
| splitPane.setBackground(null); |
| if (splitPane.getBorder() instanceof UIResource) |
| splitPane.setBorder(null); |
| } |
| |
| /** |
| * This method installs the listeners needed for this UI to function. |
| */ |
| protected void installListeners() |
| { |
| propertyChangeListener = createPropertyChangeListener(); |
| focusListener = createFocusListener(); |
| |
| splitPane.addPropertyChangeListener(propertyChangeListener); |
| splitPane.addFocusListener(focusListener); |
| } |
| |
| /** |
| * This method uninstalls all listeners registered for the UI. |
| */ |
| protected void uninstallListeners() |
| { |
| splitPane.removePropertyChangeListener(propertyChangeListener); |
| splitPane.removeFocusListener(focusListener); |
| |
| focusListener = null; |
| propertyChangeListener = null; |
| } |
| |
| /** |
| * Returns the input map for the specified condition. |
| * |
| * @param condition the condition. |
| * |
| * @return The input map. |
| */ |
| InputMap getInputMap(int condition) |
| { |
| if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) |
| return (InputMap) UIManager.get("SplitPane.ancestorInputMap"); |
| return null; |
| } |
| |
| /** |
| * Returns the action map for the {@link JSplitPane}. All sliders share |
| * a single action map which is created the first time this method is |
| * called, then stored in the UIDefaults table for subsequent access. |
| * |
| * @return The shared action map. |
| */ |
| ActionMap getActionMap() |
| { |
| ActionMap map = (ActionMap) UIManager.get("SplitPane.actionMap"); |
| |
| if (map == null) // first time here |
| { |
| map = createActionMap(); |
| if (map != null) |
| UIManager.put("SplitPane.actionMap", map); |
| } |
| return map; |
| } |
| |
| /** |
| * Creates the action map shared by all {@link JSlider} instances. |
| * This method is called once by {@link #getActionMap()} when it |
| * finds no action map in the UIDefaults table...after the map is |
| * created, it gets added to the defaults table so that subsequent |
| * calls to {@link #getActionMap()} will return the same shared |
| * instance. |
| * |
| * @return The action map. |
| */ |
| ActionMap createActionMap() |
| { |
| ActionMap map = new ActionMapUIResource(); |
| map.put("toggleFocus", |
| new AbstractAction("toggleFocus") { |
| public void actionPerformed(ActionEvent event) |
| { |
| // FIXME: What to do here? |
| } |
| } |
| ); |
| map.put("startResize", |
| new AbstractAction("startResize") { |
| public void actionPerformed(ActionEvent event) |
| { |
| splitPane.requestFocus(); |
| } |
| } |
| ); |
| map.put("selectMax", |
| new AbstractAction("selectMax") { |
| public void actionPerformed(ActionEvent event) |
| { |
| splitPane.setDividerLocation(1.0); |
| } |
| } |
| ); |
| map.put("selectMin", |
| new AbstractAction("selectMin") { |
| public void actionPerformed(ActionEvent event) |
| { |
| splitPane.setDividerLocation(0.0); |
| } |
| } |
| ); |
| map.put("negativeIncrement", |
| new AbstractAction("negativeIncrement") { |
| public void actionPerformed(ActionEvent event) |
| { |
| setDividerLocation(splitPane, Math.max(dividerLocation |
| - KEYBOARD_DIVIDER_MOVE_OFFSET, 0)); |
| } |
| } |
| ); |
| map.put("positiveIncrement", |
| new AbstractAction("positiveIncrement") { |
| public void actionPerformed(ActionEvent event) |
| { |
| setDividerLocation(splitPane, dividerLocation |
| + KEYBOARD_DIVIDER_MOVE_OFFSET); |
| } |
| } |
| ); |
| map.put("focusOutBackward", |
| new AbstractAction("focusOutBackward") { |
| public void actionPerformed(ActionEvent event) |
| { |
| // FIXME: implement this |
| } |
| } |
| ); |
| map.put("focusOutForward", |
| new AbstractAction("focusOutForward") { |
| public void actionPerformed(ActionEvent event) |
| { |
| // FIXME: implement this |
| } |
| } |
| ); |
| return map; |
| } |
| |
| /** |
| * Installs any keyboard actions. The list of keys that need to be bound are |
| * listed in Basic look and feel's defaults. |
| */ |
| protected void installKeyboardActions() |
| { |
| InputMap keyMap = getInputMap( |
| JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); |
| SwingUtilities.replaceUIInputMap(splitPane, |
| JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap); |
| ActionMap map = getActionMap(); |
| SwingUtilities.replaceUIActionMap(splitPane, map); |
| } |
| |
| /** |
| * This method reverses the work done in installKeyboardActions. |
| */ |
| protected void uninstallKeyboardActions() |
| { |
| SwingUtilities.replaceUIActionMap(splitPane, null); |
| SwingUtilities.replaceUIInputMap(splitPane, |
| JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); |
| } |
| |
| /** |
| * This method creates a new PropertyChangeListener. |
| * |
| * @return A new PropertyChangeListener. |
| */ |
| protected PropertyChangeListener createPropertyChangeListener() |
| { |
| return new PropertyHandler(); |
| } |
| |
| /** |
| * This method creates a new FocusListener. |
| * |
| * @return A new FocusListener. |
| */ |
| protected FocusListener createFocusListener() |
| { |
| return new FocusHandler(); |
| } |
| |
| /** |
| * This method creates a new ActionListener for up and left key presses. |
| * |
| * @return A new ActionListener for up and left keys. |
| * |
| * @deprecated 1.3 |
| */ |
| protected ActionListener createKeyboardUpLeftListener() |
| { |
| return new KeyboardUpLeftHandler(); |
| } |
| |
| /** |
| * This method creates a new ActionListener for down and right key presses. |
| * |
| * @return A new ActionListener for down and right keys. |
| * |
| * @deprecated 1.3 |
| */ |
| protected ActionListener createKeyboardDownRightListener() |
| { |
| return new KeyboardDownRightHandler(); |
| } |
| |
| /** |
| * This method creates a new ActionListener for home key presses. |
| * |
| * @return A new ActionListener for home keys. |
| * |
| * @deprecated |
| */ |
| protected ActionListener createKeyboardHomeListener() |
| { |
| return new KeyboardHomeHandler(); |
| } |
| |
| /** |
| * This method creates a new ActionListener for end key presses.i |
| * |
| * @return A new ActionListener for end keys. |
| * |
| * @deprecated 1.3 |
| */ |
| protected ActionListener createKeyboardEndListener() |
| { |
| return new KeyboardEndHandler(); |
| } |
| |
| /** |
| * This method creates a new ActionListener for resize toggle key events. |
| * |
| * @return A new ActionListener for resize toggle keys. |
| * |
| * @deprecated 1.3 |
| */ |
| protected ActionListener createKeyboardResizeToggleListener() |
| { |
| return new KeyboardResizeToggleHandler(); |
| } |
| |
| /** |
| * This method returns the orientation of the JSplitPane. |
| * |
| * @return The orientation of the JSplitPane. |
| */ |
| public int getOrientation() |
| { |
| return splitPane.getOrientation(); |
| } |
| |
| /** |
| * This method sets the orientation of the JSplitPane. |
| * |
| * @param orientation The new orientation of the JSplitPane. |
| */ |
| public void setOrientation(int orientation) |
| { |
| splitPane.setOrientation(orientation); |
| } |
| |
| /** |
| * This method returns true if the JSplitPane is using continuous layout. |
| * |
| * @return True if the JSplitPane is using continuous layout. |
| */ |
| public boolean isContinuousLayout() |
| { |
| return splitPane.isContinuousLayout(); |
| } |
| |
| /** |
| * This method sets the continuous layout property of the JSplitPane. |
| * |
| * @param b True if the JsplitPane is to use continuous layout. |
| */ |
| public void setContinuousLayout(boolean b) |
| { |
| splitPane.setContinuousLayout(b); |
| } |
| |
| /** |
| * This method returns the last location the divider was dragged to. |
| * |
| * @return The last location the divider was dragged to. |
| */ |
| public int getLastDragLocation() |
| { |
| return lastDragLocation; |
| } |
| |
| /** |
| * This method sets the last location the divider was dragged to. |
| * |
| * @param l The last location the divider was dragged to. |
| */ |
| public void setLastDragLocation(int l) |
| { |
| lastDragLocation = l; |
| } |
| |
| /** |
| * This method returns the BasicSplitPaneDivider that divides this |
| * JSplitPane. |
| * |
| * @return The divider for the JSplitPane. |
| */ |
| public BasicSplitPaneDivider getDivider() |
| { |
| return divider; |
| } |
| |
| /** |
| * This method creates a nonContinuousLayoutDivider for use with the |
| * JSplitPane in nonContinousLayout mode. The default divider is a gray |
| * Canvas. |
| * |
| * @return The default nonContinousLayoutDivider. |
| */ |
| protected Component createDefaultNonContinuousLayoutDivider() |
| { |
| if (nonContinuousLayoutDivider == null) |
| { |
| nonContinuousLayoutDivider = new Canvas(); |
| Color c = UIManager.getColor("SplitPaneDivider.draggingColor"); |
| nonContinuousLayoutDivider.setBackground(c); |
| } |
| return nonContinuousLayoutDivider; |
| } |
| |
| /** |
| * This method sets the component to use as the nonContinuousLayoutDivider. |
| * |
| * @param newDivider The component to use as the nonContinuousLayoutDivider. |
| */ |
| protected void setNonContinuousLayoutDivider(Component newDivider) |
| { |
| setNonContinuousLayoutDivider(newDivider, true); |
| } |
| |
| /** |
| * This method sets the component to use as the nonContinuousLayoutDivider. |
| * |
| * @param newDivider The component to use as the nonContinuousLayoutDivider. |
| * @param rememberSizes FIXME: document. |
| */ |
| protected void setNonContinuousLayoutDivider(Component newDivider, |
| boolean rememberSizes) |
| { |
| // FIXME: use rememberSizes for something |
| nonContinuousLayoutDivider = newDivider; |
| } |
| |
| /** |
| * This method returns the nonContinuousLayoutDivider. |
| * |
| * @return The nonContinuousLayoutDivider. |
| */ |
| public Component getNonContinuousLayoutDivider() |
| { |
| return nonContinuousLayoutDivider; |
| } |
| |
| /** |
| * This method returns the JSplitPane that this BasicSplitPaneUI draws. |
| * |
| * @return The JSplitPane. |
| */ |
| public JSplitPane getSplitPane() |
| { |
| return splitPane; |
| } |
| |
| /** |
| * This method creates the divider used normally with the JSplitPane. |
| * |
| * @return The default divider. |
| */ |
| public BasicSplitPaneDivider createDefaultDivider() |
| { |
| if (divider == null) |
| divider = new BasicSplitPaneDivider(this); |
| return divider; |
| } |
| |
| /** |
| * This method is called when JSplitPane's resetToPreferredSizes is called. |
| * It resets the sizes of all components in the JSplitPane. |
| * |
| * @param jc The JSplitPane to reset. |
| */ |
| public void resetToPreferredSizes(JSplitPane jc) |
| { |
| layoutManager.resetToPreferredSizes(); |
| } |
| |
| /** |
| * This method sets the location of the divider. |
| * |
| * @param jc The JSplitPane to set the divider location in. |
| * @param location The new location of the divider. |
| */ |
| public void setDividerLocation(JSplitPane jc, int location) |
| { |
| dividerLocation = location; |
| splitPane.revalidate(); |
| splitPane.repaint(); |
| } |
| |
| /** |
| * This method returns the location of the divider. |
| * |
| * @param jc The JSplitPane to retrieve the location for. |
| * |
| * @return The location of the divider. |
| */ |
| public int getDividerLocation(JSplitPane jc) |
| { |
| return dividerLocation; |
| } |
| |
| /** |
| * This method returns the smallest value possible for the location of the |
| * divider. |
| * |
| * @param jc The JSplitPane. |
| * |
| * @return The minimum divider location. |
| */ |
| public int getMinimumDividerLocation(JSplitPane jc) |
| { |
| int value = layoutManager.getInitialLocation(jc.getInsets()); |
| if (layoutManager.components[0] != null) |
| value += layoutManager.minimumSizeOfComponent(0); |
| return value; |
| } |
| |
| /** |
| * This method returns the largest value possible for the location of the |
| * divider. |
| * |
| * @param jc The JSplitPane. |
| * |
| * @return The maximum divider location. |
| */ |
| public int getMaximumDividerLocation(JSplitPane jc) |
| { |
| int value = layoutManager.getInitialLocation(jc.getInsets()) |
| + layoutManager.getAvailableSize(jc.getSize(), jc.getInsets()) |
| - splitPane.getDividerSize(); |
| if (layoutManager.components[1] != null) |
| value -= layoutManager.minimumSizeOfComponent(1); |
| return value; |
| } |
| |
| /** |
| * This method is called after the children of the JSplitPane are painted. |
| * |
| * @param jc The JSplitPane. |
| * @param g The Graphics object to paint with. |
| */ |
| public void finishedPaintingChildren(JSplitPane jc, Graphics g) |
| { |
| if (! splitPane.isContinuousLayout() && nonContinuousLayoutDivider != null |
| && nonContinuousLayoutDivider.isVisible()) |
| javax.swing.SwingUtilities.paintComponent(g, nonContinuousLayoutDivider, |
| null, |
| nonContinuousLayoutDivider |
| .getBounds()); |
| } |
| |
| /** |
| * This method is called to paint the JSplitPane. |
| * |
| * @param g The Graphics object to paint with. |
| * @param jc The JSplitPane to paint. |
| */ |
| public void paint(Graphics g, JComponent jc) |
| { |
| // TODO: What should be done here? |
| } |
| |
| /** |
| * This method returns the preferred size of the JSplitPane. |
| * |
| * @param jc The JSplitPane. |
| * |
| * @return The preferred size of the JSplitPane. |
| */ |
| public Dimension getPreferredSize(JComponent jc) |
| { |
| return layoutManager.preferredLayoutSize((Container) jc); |
| } |
| |
| /** |
| * This method returns the minimum size of the JSplitPane. |
| * |
| * @param jc The JSplitPane. |
| * |
| * @return The minimum size of the JSplitPane. |
| */ |
| public Dimension getMinimumSize(JComponent jc) |
| { |
| return layoutManager.minimumLayoutSize((Container) jc); |
| } |
| |
| /** |
| * This method returns the maximum size of the JSplitPane. |
| * |
| * @param jc The JSplitPane. |
| * |
| * @return The maximum size of the JSplitPane. |
| */ |
| public Dimension getMaximumSize(JComponent jc) |
| { |
| return layoutManager.maximumLayoutSize((Container) jc); |
| } |
| |
| /** |
| * This method returns the border insets of the current border. |
| * |
| * @param jc The JSplitPane. |
| * |
| * @return The current border insets. |
| */ |
| public Insets getInsets(JComponent jc) |
| { |
| return splitPane.getBorder().getBorderInsets(splitPane); |
| } |
| |
| /** |
| * This method resets the current layout manager. The type of layout manager |
| * is dependent on the current orientation. |
| */ |
| protected void resetLayoutManager() |
| { |
| if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT) |
| layoutManager = new BasicHorizontalLayoutManager(); |
| else |
| layoutManager = new BasicVerticalLayoutManager(); |
| getSplitPane().setLayout(layoutManager); |
| layoutManager.updateComponents(); |
| |
| // invalidating by itself does not invalidate the layout. |
| getSplitPane().revalidate(); |
| } |
| |
| /** |
| * This method is called when dragging starts. It resets lastDragLocation |
| * and dividerSize. |
| */ |
| protected void startDragging() |
| { |
| Component left = splitPane.getLeftComponent(); |
| Component right = splitPane.getRightComponent(); |
| dividerSize = divider.getDividerSize(); |
| setLastDragLocation(-1); |
| |
| if ((left != null && !left.isLightweight()) |
| || (right != null && !right.isLightweight())) |
| draggingHW = true; |
| |
| if (splitPane.isContinuousLayout()) |
| nonContinuousLayoutDivider.setVisible(false); |
| else |
| { |
| nonContinuousLayoutDivider.setVisible(true); |
| nonContinuousLayoutDivider.setBounds(divider.getBounds()); |
| } |
| } |
| |
| /** |
| * This method is called whenever the divider is dragged. If the JSplitPane |
| * is in continuousLayout mode, the divider needs to be moved and the |
| * JSplitPane needs to be laid out. |
| * |
| * @param location The new location of the divider. |
| */ |
| protected void dragDividerTo(int location) |
| { |
| location = validLocation(location); |
| if (beginDragDividerLocation == -1) |
| beginDragDividerLocation = location; |
| |
| if (splitPane.isContinuousLayout()) |
| splitPane.setDividerLocation(location); |
| else |
| { |
| Point p = nonContinuousLayoutDivider.getLocation(); |
| if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT) |
| p.x = location; |
| else |
| p.y = location; |
| nonContinuousLayoutDivider.setLocation(p); |
| } |
| setLastDragLocation(location); |
| splitPane.repaint(); |
| } |
| |
| /** |
| * This method is called when the dragging is finished. |
| * |
| * @param location The location where the drag finished. |
| */ |
| protected void finishDraggingTo(int location) |
| { |
| if (nonContinuousLayoutDivider != null) |
| nonContinuousLayoutDivider.setVisible(false); |
| draggingHW = false; |
| location = validLocation(location); |
| splitPane.setDividerLocation(location); |
| splitPane.setLastDividerLocation(beginDragDividerLocation); |
| beginDragDividerLocation = -1; |
| } |
| |
| /** |
| * This method returns the width of one of the sides of the divider's border. |
| * |
| * @return The width of one side of the divider's border. |
| * |
| * @deprecated 1.3 |
| */ |
| protected int getDividerBorderSize() |
| { |
| if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT) |
| return divider.getBorder().getBorderInsets(divider).left; |
| else |
| return divider.getBorder().getBorderInsets(divider).top; |
| } |
| |
| /** |
| * This is a helper method that returns a valid location for the divider |
| * when dragging. |
| * |
| * @param location The location to check. |
| * |
| * @return A valid location. |
| */ |
| private int validLocation(int location) |
| { |
| int min = getMinimumDividerLocation(splitPane); |
| int max = getMaximumDividerLocation(splitPane); |
| if (min > 0 && location < min) |
| return min; |
| if (max > 0 && location > max) |
| return max; |
| return location; |
| } |
| } |