You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1122 lines
39 KiB
1122 lines
39 KiB
/*
|
|
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package javax.swing;
|
|
|
|
|
|
import javax.swing.border.*;
|
|
|
|
import java.awt.LayoutManager;
|
|
import java.awt.Component;
|
|
import java.awt.Container;
|
|
import java.awt.Rectangle;
|
|
import java.awt.Dimension;
|
|
import java.awt.Insets;
|
|
import java.io.Serializable;
|
|
|
|
|
|
/**
|
|
* The layout manager used by <code>JScrollPane</code>.
|
|
* <code>JScrollPaneLayout</code> is
|
|
* responsible for nine components: a viewport, two scrollbars,
|
|
* a row header, a column header, and four "corner" components.
|
|
* <p>
|
|
* <strong>Warning:</strong>
|
|
* Serialized objects of this class will not be compatible with
|
|
* future Swing releases. The current serialization support is
|
|
* appropriate for short term storage or RMI between applications running
|
|
* the same version of Swing. As of 1.4, support for long term storage
|
|
* of all JavaBeans™
|
|
* has been added to the <code>java.beans</code> package.
|
|
* Please see {@link java.beans.XMLEncoder}.
|
|
*
|
|
* @see JScrollPane
|
|
* @see JViewport
|
|
*
|
|
* @author Hans Muller
|
|
*/
|
|
public class ScrollPaneLayout
|
|
implements LayoutManager, ScrollPaneConstants, Serializable
|
|
{
|
|
|
|
/**
|
|
* The scrollpane's viewport child.
|
|
* Default is an empty <code>JViewport</code>.
|
|
* @see JScrollPane#setViewport
|
|
*/
|
|
protected JViewport viewport;
|
|
|
|
|
|
/**
|
|
* The scrollpane's vertical scrollbar child.
|
|
* Default is a <code>JScrollBar</code>.
|
|
* @see JScrollPane#setVerticalScrollBar
|
|
*/
|
|
protected JScrollBar vsb;
|
|
|
|
|
|
/**
|
|
* The scrollpane's horizontal scrollbar child.
|
|
* Default is a <code>JScrollBar</code>.
|
|
* @see JScrollPane#setHorizontalScrollBar
|
|
*/
|
|
protected JScrollBar hsb;
|
|
|
|
|
|
/**
|
|
* The row header child. Default is <code>null</code>.
|
|
* @see JScrollPane#setRowHeader
|
|
*/
|
|
protected JViewport rowHead;
|
|
|
|
|
|
/**
|
|
* The column header child. Default is <code>null</code>.
|
|
* @see JScrollPane#setColumnHeader
|
|
*/
|
|
protected JViewport colHead;
|
|
|
|
|
|
/**
|
|
* The component to display in the lower left corner.
|
|
* Default is <code>null</code>.
|
|
* @see JScrollPane#setCorner
|
|
*/
|
|
protected Component lowerLeft;
|
|
|
|
|
|
/**
|
|
* The component to display in the lower right corner.
|
|
* Default is <code>null</code>.
|
|
* @see JScrollPane#setCorner
|
|
*/
|
|
protected Component lowerRight;
|
|
|
|
|
|
/**
|
|
* The component to display in the upper left corner.
|
|
* Default is <code>null</code>.
|
|
* @see JScrollPane#setCorner
|
|
*/
|
|
protected Component upperLeft;
|
|
|
|
|
|
/**
|
|
* The component to display in the upper right corner.
|
|
* Default is <code>null</code>.
|
|
* @see JScrollPane#setCorner
|
|
*/
|
|
protected Component upperRight;
|
|
|
|
|
|
/**
|
|
* The display policy for the vertical scrollbar.
|
|
* The default is <code>ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED</code>.
|
|
* <p>
|
|
* This field is obsolete, please use the <code>JScrollPane</code> field instead.
|
|
*
|
|
* @see JScrollPane#setVerticalScrollBarPolicy
|
|
*/
|
|
protected int vsbPolicy = VERTICAL_SCROLLBAR_AS_NEEDED;
|
|
|
|
|
|
/**
|
|
* The display policy for the horizontal scrollbar.
|
|
* The default is <code>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED</code>.
|
|
* <p>
|
|
* This field is obsolete, please use the <code>JScrollPane</code> field instead.
|
|
*
|
|
* @see JScrollPane#setHorizontalScrollBarPolicy
|
|
*/
|
|
protected int hsbPolicy = HORIZONTAL_SCROLLBAR_AS_NEEDED;
|
|
|
|
|
|
/**
|
|
* This method is invoked after the ScrollPaneLayout is set as the
|
|
* LayoutManager of a <code>JScrollPane</code>.
|
|
* It initializes all of the internal fields that
|
|
* are ordinarily set by <code>addLayoutComponent</code>. For example:
|
|
* <pre>
|
|
* ScrollPaneLayout mySPLayout = new ScrollPanelLayout() {
|
|
* public void layoutContainer(Container p) {
|
|
* super.layoutContainer(p);
|
|
* // do some extra work here ...
|
|
* }
|
|
* };
|
|
* scrollpane.setLayout(mySPLayout):
|
|
* </pre>
|
|
*/
|
|
public void syncWithScrollPane(JScrollPane sp) {
|
|
viewport = sp.getViewport();
|
|
vsb = sp.getVerticalScrollBar();
|
|
hsb = sp.getHorizontalScrollBar();
|
|
rowHead = sp.getRowHeader();
|
|
colHead = sp.getColumnHeader();
|
|
lowerLeft = sp.getCorner(LOWER_LEFT_CORNER);
|
|
lowerRight = sp.getCorner(LOWER_RIGHT_CORNER);
|
|
upperLeft = sp.getCorner(UPPER_LEFT_CORNER);
|
|
upperRight = sp.getCorner(UPPER_RIGHT_CORNER);
|
|
vsbPolicy = sp.getVerticalScrollBarPolicy();
|
|
hsbPolicy = sp.getHorizontalScrollBarPolicy();
|
|
}
|
|
|
|
|
|
/**
|
|
* Removes an existing component. When a new component, such as
|
|
* the left corner, or vertical scrollbar, is added, the old one,
|
|
* if it exists, must be removed.
|
|
* <p>
|
|
* This method returns <code>newC</code>. If <code>oldC</code> is
|
|
* not equal to <code>newC</code> and is non-<code>null</code>,
|
|
* it will be removed from its parent.
|
|
*
|
|
* @param oldC the <code>Component</code> to replace
|
|
* @param newC the <code>Component</code> to add
|
|
* @return the <code>newC</code>
|
|
*/
|
|
protected Component addSingletonComponent(Component oldC, Component newC)
|
|
{
|
|
if ((oldC != null) && (oldC != newC)) {
|
|
oldC.getParent().remove(oldC);
|
|
}
|
|
return newC;
|
|
}
|
|
|
|
|
|
/**
|
|
* Adds the specified component to the layout. The layout is
|
|
* identified using one of:
|
|
* <ul>
|
|
* <li>ScrollPaneConstants.VIEWPORT
|
|
* <li>ScrollPaneConstants.VERTICAL_SCROLLBAR
|
|
* <li>ScrollPaneConstants.HORIZONTAL_SCROLLBAR
|
|
* <li>ScrollPaneConstants.ROW_HEADER
|
|
* <li>ScrollPaneConstants.COLUMN_HEADER
|
|
* <li>ScrollPaneConstants.LOWER_LEFT_CORNER
|
|
* <li>ScrollPaneConstants.LOWER_RIGHT_CORNER
|
|
* <li>ScrollPaneConstants.UPPER_LEFT_CORNER
|
|
* <li>ScrollPaneConstants.UPPER_RIGHT_CORNER
|
|
* </ul>
|
|
*
|
|
* @param s the component identifier
|
|
* @param c the the component to be added
|
|
* @exception IllegalArgumentException if <code>s</code> is an invalid key
|
|
*/
|
|
public void addLayoutComponent(String s, Component c)
|
|
{
|
|
if (s.equals(VIEWPORT)) {
|
|
viewport = (JViewport)addSingletonComponent(viewport, c);
|
|
}
|
|
else if (s.equals(VERTICAL_SCROLLBAR)) {
|
|
vsb = (JScrollBar)addSingletonComponent(vsb, c);
|
|
}
|
|
else if (s.equals(HORIZONTAL_SCROLLBAR)) {
|
|
hsb = (JScrollBar)addSingletonComponent(hsb, c);
|
|
}
|
|
else if (s.equals(ROW_HEADER)) {
|
|
rowHead = (JViewport)addSingletonComponent(rowHead, c);
|
|
}
|
|
else if (s.equals(COLUMN_HEADER)) {
|
|
colHead = (JViewport)addSingletonComponent(colHead, c);
|
|
}
|
|
else if (s.equals(LOWER_LEFT_CORNER)) {
|
|
lowerLeft = addSingletonComponent(lowerLeft, c);
|
|
}
|
|
else if (s.equals(LOWER_RIGHT_CORNER)) {
|
|
lowerRight = addSingletonComponent(lowerRight, c);
|
|
}
|
|
else if (s.equals(UPPER_LEFT_CORNER)) {
|
|
upperLeft = addSingletonComponent(upperLeft, c);
|
|
}
|
|
else if (s.equals(UPPER_RIGHT_CORNER)) {
|
|
upperRight = addSingletonComponent(upperRight, c);
|
|
}
|
|
else {
|
|
throw new IllegalArgumentException("invalid layout key " + s);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Removes the specified component from the layout.
|
|
*
|
|
* @param c the component to remove
|
|
*/
|
|
public void removeLayoutComponent(Component c)
|
|
{
|
|
if (c == viewport) {
|
|
viewport = null;
|
|
}
|
|
else if (c == vsb) {
|
|
vsb = null;
|
|
}
|
|
else if (c == hsb) {
|
|
hsb = null;
|
|
}
|
|
else if (c == rowHead) {
|
|
rowHead = null;
|
|
}
|
|
else if (c == colHead) {
|
|
colHead = null;
|
|
}
|
|
else if (c == lowerLeft) {
|
|
lowerLeft = null;
|
|
}
|
|
else if (c == lowerRight) {
|
|
lowerRight = null;
|
|
}
|
|
else if (c == upperLeft) {
|
|
upperLeft = null;
|
|
}
|
|
else if (c == upperRight) {
|
|
upperRight = null;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the vertical scrollbar-display policy.
|
|
*
|
|
* @return an integer giving the display policy
|
|
* @see #setVerticalScrollBarPolicy
|
|
*/
|
|
public int getVerticalScrollBarPolicy() {
|
|
return vsbPolicy;
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the vertical scrollbar-display policy. The options
|
|
* are:
|
|
* <ul>
|
|
* <li>ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED
|
|
* <li>ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER
|
|
* <li>ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS
|
|
* </ul>
|
|
* Note: Applications should use the <code>JScrollPane</code> version
|
|
* of this method. It only exists for backwards compatibility
|
|
* with the Swing 1.0.2 (and earlier) versions of this class.
|
|
*
|
|
* @param x an integer giving the display policy
|
|
* @exception IllegalArgumentException if <code>x</code> is an invalid
|
|
* vertical scroll bar policy, as listed above
|
|
*/
|
|
public void setVerticalScrollBarPolicy(int x) {
|
|
switch (x) {
|
|
case VERTICAL_SCROLLBAR_AS_NEEDED:
|
|
case VERTICAL_SCROLLBAR_NEVER:
|
|
case VERTICAL_SCROLLBAR_ALWAYS:
|
|
vsbPolicy = x;
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("invalid verticalScrollBarPolicy");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the horizontal scrollbar-display policy.
|
|
*
|
|
* @return an integer giving the display policy
|
|
* @see #setHorizontalScrollBarPolicy
|
|
*/
|
|
public int getHorizontalScrollBarPolicy() {
|
|
return hsbPolicy;
|
|
}
|
|
|
|
/**
|
|
* Sets the horizontal scrollbar-display policy.
|
|
* The options are:<ul>
|
|
* <li>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED
|
|
* <li>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
|
|
* <li>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS
|
|
* </ul>
|
|
* Note: Applications should use the <code>JScrollPane</code> version
|
|
* of this method. It only exists for backwards compatibility
|
|
* with the Swing 1.0.2 (and earlier) versions of this class.
|
|
*
|
|
* @param x an int giving the display policy
|
|
* @exception IllegalArgumentException if <code>x</code> is not a valid
|
|
* horizontal scrollbar policy, as listed above
|
|
*/
|
|
public void setHorizontalScrollBarPolicy(int x) {
|
|
switch (x) {
|
|
case HORIZONTAL_SCROLLBAR_AS_NEEDED:
|
|
case HORIZONTAL_SCROLLBAR_NEVER:
|
|
case HORIZONTAL_SCROLLBAR_ALWAYS:
|
|
hsbPolicy = x;
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("invalid horizontalScrollBarPolicy");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the <code>JViewport</code> object that displays the
|
|
* scrollable contents.
|
|
* @return the <code>JViewport</code> object that displays the scrollable contents
|
|
* @see JScrollPane#getViewport
|
|
*/
|
|
public JViewport getViewport() {
|
|
return viewport;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the <code>JScrollBar</code> object that handles horizontal scrolling.
|
|
* @return the <code>JScrollBar</code> object that handles horizontal scrolling
|
|
* @see JScrollPane#getHorizontalScrollBar
|
|
*/
|
|
public JScrollBar getHorizontalScrollBar() {
|
|
return hsb;
|
|
}
|
|
|
|
/**
|
|
* Returns the <code>JScrollBar</code> object that handles vertical scrolling.
|
|
* @return the <code>JScrollBar</code> object that handles vertical scrolling
|
|
* @see JScrollPane#getVerticalScrollBar
|
|
*/
|
|
public JScrollBar getVerticalScrollBar() {
|
|
return vsb;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the <code>JViewport</code> object that is the row header.
|
|
* @return the <code>JViewport</code> object that is the row header
|
|
* @see JScrollPane#getRowHeader
|
|
*/
|
|
public JViewport getRowHeader() {
|
|
return rowHead;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the <code>JViewport</code> object that is the column header.
|
|
* @return the <code>JViewport</code> object that is the column header
|
|
* @see JScrollPane#getColumnHeader
|
|
*/
|
|
public JViewport getColumnHeader() {
|
|
return colHead;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the <code>Component</code> at the specified corner.
|
|
* @param key the <code>String</code> specifying the corner
|
|
* @return the <code>Component</code> at the specified corner, as defined in
|
|
* {@link ScrollPaneConstants}; if <code>key</code> is not one of the
|
|
* four corners, <code>null</code> is returned
|
|
* @see JScrollPane#getCorner
|
|
*/
|
|
public Component getCorner(String key) {
|
|
if (key.equals(LOWER_LEFT_CORNER)) {
|
|
return lowerLeft;
|
|
}
|
|
else if (key.equals(LOWER_RIGHT_CORNER)) {
|
|
return lowerRight;
|
|
}
|
|
else if (key.equals(UPPER_LEFT_CORNER)) {
|
|
return upperLeft;
|
|
}
|
|
else if (key.equals(UPPER_RIGHT_CORNER)) {
|
|
return upperRight;
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* The preferred size of a <code>ScrollPane</code> is the size of the insets,
|
|
* plus the preferred size of the viewport, plus the preferred size of
|
|
* the visible headers, plus the preferred size of the scrollbars
|
|
* that will appear given the current view and the current
|
|
* scrollbar displayPolicies.
|
|
* <p>Note that the rowHeader is calculated as part of the preferred width
|
|
* and the colHeader is calculated as part of the preferred size.
|
|
*
|
|
* @param parent the <code>Container</code> that will be laid out
|
|
* @return a <code>Dimension</code> object specifying the preferred size of the
|
|
* viewport and any scrollbars
|
|
* @see ViewportLayout
|
|
* @see LayoutManager
|
|
*/
|
|
public Dimension preferredLayoutSize(Container parent)
|
|
{
|
|
/* Sync the (now obsolete) policy fields with the
|
|
* JScrollPane.
|
|
*/
|
|
JScrollPane scrollPane = (JScrollPane)parent;
|
|
vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
|
|
hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
|
|
|
|
Insets insets = parent.getInsets();
|
|
int prefWidth = insets.left + insets.right;
|
|
int prefHeight = insets.top + insets.bottom;
|
|
|
|
/* Note that viewport.getViewSize() is equivalent to
|
|
* viewport.getView().getPreferredSize() modulo a null
|
|
* view or a view whose size was explicitly set.
|
|
*/
|
|
|
|
Dimension extentSize = null;
|
|
Dimension viewSize = null;
|
|
Component view = null;
|
|
|
|
if (viewport != null) {
|
|
extentSize = viewport.getPreferredSize();
|
|
view = viewport.getView();
|
|
if (view != null) {
|
|
viewSize = view.getPreferredSize();
|
|
} else {
|
|
viewSize = new Dimension(0, 0);
|
|
}
|
|
}
|
|
|
|
/* If there's a viewport add its preferredSize.
|
|
*/
|
|
|
|
if (extentSize != null) {
|
|
prefWidth += extentSize.width;
|
|
prefHeight += extentSize.height;
|
|
}
|
|
|
|
/* If there's a JScrollPane.viewportBorder, add its insets.
|
|
*/
|
|
|
|
Border viewportBorder = scrollPane.getViewportBorder();
|
|
if (viewportBorder != null) {
|
|
Insets vpbInsets = viewportBorder.getBorderInsets(parent);
|
|
prefWidth += vpbInsets.left + vpbInsets.right;
|
|
prefHeight += vpbInsets.top + vpbInsets.bottom;
|
|
}
|
|
|
|
/* If a header exists and it's visible, factor its
|
|
* preferred size in.
|
|
*/
|
|
|
|
if ((rowHead != null) && rowHead.isVisible()) {
|
|
prefWidth += rowHead.getPreferredSize().width;
|
|
}
|
|
|
|
if ((colHead != null) && colHead.isVisible()) {
|
|
prefHeight += colHead.getPreferredSize().height;
|
|
}
|
|
|
|
/* If a scrollbar is going to appear, factor its preferred size in.
|
|
* If the scrollbars policy is AS_NEEDED, this can be a little
|
|
* tricky:
|
|
*
|
|
* - If the view is a Scrollable then scrollableTracksViewportWidth
|
|
* and scrollableTracksViewportHeight can be used to effectively
|
|
* disable scrolling (if they're true) in their respective dimensions.
|
|
*
|
|
* - Assuming that a scrollbar hasn't been disabled by the
|
|
* previous constraint, we need to decide if the scrollbar is going
|
|
* to appear to correctly compute the JScrollPanes preferred size.
|
|
* To do this we compare the preferredSize of the viewport (the
|
|
* extentSize) to the preferredSize of the view. Although we're
|
|
* not responsible for laying out the view we'll assume that the
|
|
* JViewport will always give it its preferredSize.
|
|
*/
|
|
|
|
if ((vsb != null) && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
|
|
if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
|
|
prefWidth += vsb.getPreferredSize().width;
|
|
}
|
|
else if ((viewSize != null) && (extentSize != null)) {
|
|
boolean canScroll = true;
|
|
if (view instanceof Scrollable) {
|
|
canScroll = !((Scrollable)view).getScrollableTracksViewportHeight();
|
|
}
|
|
if (canScroll && (viewSize.height > extentSize.height)) {
|
|
prefWidth += vsb.getPreferredSize().width;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((hsb != null) && (hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER)) {
|
|
if (hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) {
|
|
prefHeight += hsb.getPreferredSize().height;
|
|
}
|
|
else if ((viewSize != null) && (extentSize != null)) {
|
|
boolean canScroll = true;
|
|
if (view instanceof Scrollable) {
|
|
canScroll = !((Scrollable)view).getScrollableTracksViewportWidth();
|
|
}
|
|
if (canScroll && (viewSize.width > extentSize.width)) {
|
|
prefHeight += hsb.getPreferredSize().height;
|
|
}
|
|
}
|
|
}
|
|
|
|
return new Dimension(prefWidth, prefHeight);
|
|
}
|
|
|
|
|
|
/**
|
|
* The minimum size of a <code>ScrollPane</code> is the size of the insets
|
|
* plus minimum size of the viewport, plus the scrollpane's
|
|
* viewportBorder insets, plus the minimum size
|
|
* of the visible headers, plus the minimum size of the
|
|
* scrollbars whose displayPolicy isn't NEVER.
|
|
*
|
|
* @param parent the <code>Container</code> that will be laid out
|
|
* @return a <code>Dimension</code> object specifying the minimum size
|
|
*/
|
|
public Dimension minimumLayoutSize(Container parent)
|
|
{
|
|
/* Sync the (now obsolete) policy fields with the
|
|
* JScrollPane.
|
|
*/
|
|
JScrollPane scrollPane = (JScrollPane)parent;
|
|
vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
|
|
hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
|
|
|
|
Insets insets = parent.getInsets();
|
|
int minWidth = insets.left + insets.right;
|
|
int minHeight = insets.top + insets.bottom;
|
|
|
|
/* If there's a viewport add its minimumSize.
|
|
*/
|
|
|
|
if (viewport != null) {
|
|
Dimension size = viewport.getMinimumSize();
|
|
minWidth += size.width;
|
|
minHeight += size.height;
|
|
}
|
|
|
|
/* If there's a JScrollPane.viewportBorder, add its insets.
|
|
*/
|
|
|
|
Border viewportBorder = scrollPane.getViewportBorder();
|
|
if (viewportBorder != null) {
|
|
Insets vpbInsets = viewportBorder.getBorderInsets(parent);
|
|
minWidth += vpbInsets.left + vpbInsets.right;
|
|
minHeight += vpbInsets.top + vpbInsets.bottom;
|
|
}
|
|
|
|
/* If a header exists and it's visible, factor its
|
|
* minimum size in.
|
|
*/
|
|
|
|
if ((rowHead != null) && rowHead.isVisible()) {
|
|
Dimension size = rowHead.getMinimumSize();
|
|
minWidth += size.width;
|
|
minHeight = Math.max(minHeight, size.height);
|
|
}
|
|
|
|
if ((colHead != null) && colHead.isVisible()) {
|
|
Dimension size = colHead.getMinimumSize();
|
|
minWidth = Math.max(minWidth, size.width);
|
|
minHeight += size.height;
|
|
}
|
|
|
|
/* If a scrollbar might appear, factor its minimum
|
|
* size in.
|
|
*/
|
|
|
|
if ((vsb != null) && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
|
|
Dimension size = vsb.getMinimumSize();
|
|
minWidth += size.width;
|
|
minHeight = Math.max(minHeight, size.height);
|
|
}
|
|
|
|
if ((hsb != null) && (hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER)) {
|
|
Dimension size = hsb.getMinimumSize();
|
|
minWidth = Math.max(minWidth, size.width);
|
|
minHeight += size.height;
|
|
}
|
|
|
|
return new Dimension(minWidth, minHeight);
|
|
}
|
|
|
|
|
|
/**
|
|
* Lays out the scrollpane. The positioning of components depends on
|
|
* the following constraints:
|
|
* <ul>
|
|
* <li> The row header, if present and visible, gets its preferred
|
|
* width and the viewport's height.
|
|
*
|
|
* <li> The column header, if present and visible, gets its preferred
|
|
* height and the viewport's width.
|
|
*
|
|
* <li> If a vertical scrollbar is needed, i.e. if the viewport's extent
|
|
* height is smaller than its view height or if the <code>displayPolicy</code>
|
|
* is ALWAYS, it's treated like the row header with respect to its
|
|
* dimensions and is made visible.
|
|
*
|
|
* <li> If a horizontal scrollbar is needed, it is treated like the
|
|
* column header (see the paragraph above regarding the vertical scrollbar).
|
|
*
|
|
* <li> If the scrollpane has a non-<code>null</code>
|
|
* <code>viewportBorder</code>, then space is allocated for that.
|
|
*
|
|
* <li> The viewport gets the space available after accounting for
|
|
* the previous constraints.
|
|
*
|
|
* <li> The corner components, if provided, are aligned with the
|
|
* ends of the scrollbars and headers. If there is a vertical
|
|
* scrollbar, the right corners appear; if there is a horizontal
|
|
* scrollbar, the lower corners appear; a row header gets left
|
|
* corners, and a column header gets upper corners.
|
|
* </ul>
|
|
*
|
|
* @param parent the <code>Container</code> to lay out
|
|
*/
|
|
public void layoutContainer(Container parent)
|
|
{
|
|
/* Sync the (now obsolete) policy fields with the
|
|
* JScrollPane.
|
|
*/
|
|
JScrollPane scrollPane = (JScrollPane)parent;
|
|
vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
|
|
hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
|
|
|
|
Rectangle availR = scrollPane.getBounds();
|
|
availR.x = availR.y = 0;
|
|
|
|
Insets insets = parent.getInsets();
|
|
availR.x = insets.left;
|
|
availR.y = insets.top;
|
|
availR.width -= insets.left + insets.right;
|
|
availR.height -= insets.top + insets.bottom;
|
|
|
|
/* Get the scrollPane's orientation.
|
|
*/
|
|
boolean leftToRight = SwingUtilities.isLeftToRight(scrollPane);
|
|
|
|
/* If there's a visible column header remove the space it
|
|
* needs from the top of availR. The column header is treated
|
|
* as if it were fixed height, arbitrary width.
|
|
*/
|
|
|
|
Rectangle colHeadR = new Rectangle(0, availR.y, 0, 0);
|
|
|
|
if ((colHead != null) && (colHead.isVisible())) {
|
|
int colHeadHeight = Math.min(availR.height,
|
|
colHead.getPreferredSize().height);
|
|
colHeadR.height = colHeadHeight;
|
|
availR.y += colHeadHeight;
|
|
availR.height -= colHeadHeight;
|
|
}
|
|
|
|
/* If there's a visible row header remove the space it needs
|
|
* from the left or right of availR. The row header is treated
|
|
* as if it were fixed width, arbitrary height.
|
|
*/
|
|
|
|
Rectangle rowHeadR = new Rectangle(0, 0, 0, 0);
|
|
|
|
if ((rowHead != null) && (rowHead.isVisible())) {
|
|
int rowHeadWidth = Math.min(availR.width,
|
|
rowHead.getPreferredSize().width);
|
|
rowHeadR.width = rowHeadWidth;
|
|
availR.width -= rowHeadWidth;
|
|
if ( leftToRight ) {
|
|
rowHeadR.x = availR.x;
|
|
availR.x += rowHeadWidth;
|
|
} else {
|
|
rowHeadR.x = availR.x + availR.width;
|
|
}
|
|
}
|
|
|
|
/* If there's a JScrollPane.viewportBorder, remove the
|
|
* space it occupies for availR.
|
|
*/
|
|
|
|
Border viewportBorder = scrollPane.getViewportBorder();
|
|
Insets vpbInsets;
|
|
if (viewportBorder != null) {
|
|
vpbInsets = viewportBorder.getBorderInsets(parent);
|
|
availR.x += vpbInsets.left;
|
|
availR.y += vpbInsets.top;
|
|
availR.width -= vpbInsets.left + vpbInsets.right;
|
|
availR.height -= vpbInsets.top + vpbInsets.bottom;
|
|
}
|
|
else {
|
|
vpbInsets = new Insets(0,0,0,0);
|
|
}
|
|
|
|
|
|
/* At this point availR is the space available for the viewport
|
|
* and scrollbars. rowHeadR is correct except for its height and y
|
|
* and colHeadR is correct except for its width and x. Once we're
|
|
* through computing the dimensions of these three parts we can
|
|
* go back and set the dimensions of rowHeadR.height, rowHeadR.y,
|
|
* colHeadR.width, colHeadR.x and the bounds for the corners.
|
|
*
|
|
* We'll decide about putting up scrollbars by comparing the
|
|
* viewport views preferred size with the viewports extent
|
|
* size (generally just its size). Using the preferredSize is
|
|
* reasonable because layout proceeds top down - so we expect
|
|
* the viewport to be laid out next. And we assume that the
|
|
* viewports layout manager will give the view it's preferred
|
|
* size. One exception to this is when the view implements
|
|
* Scrollable and Scrollable.getViewTracksViewport{Width,Height}
|
|
* methods return true. If the view is tracking the viewports
|
|
* width we don't bother with a horizontal scrollbar, similarly
|
|
* if view.getViewTracksViewport(Height) is true we don't bother
|
|
* with a vertical scrollbar.
|
|
*/
|
|
|
|
Component view = (viewport != null) ? viewport.getView() : null;
|
|
Dimension viewPrefSize =
|
|
(view != null) ? view.getPreferredSize()
|
|
: new Dimension(0,0);
|
|
|
|
Dimension extentSize =
|
|
(viewport != null) ? viewport.toViewCoordinates(availR.getSize())
|
|
: new Dimension(0,0);
|
|
|
|
boolean viewTracksViewportWidth = false;
|
|
boolean viewTracksViewportHeight = false;
|
|
boolean isEmpty = (availR.width < 0 || availR.height < 0);
|
|
Scrollable sv;
|
|
// Don't bother checking the Scrollable methods if there is no room
|
|
// for the viewport, we aren't going to show any scrollbars in this
|
|
// case anyway.
|
|
if (!isEmpty && view instanceof Scrollable) {
|
|
sv = (Scrollable)view;
|
|
viewTracksViewportWidth = sv.getScrollableTracksViewportWidth();
|
|
viewTracksViewportHeight = sv.getScrollableTracksViewportHeight();
|
|
}
|
|
else {
|
|
sv = null;
|
|
}
|
|
|
|
/* If there's a vertical scrollbar and we need one, allocate
|
|
* space for it (we'll make it visible later). A vertical
|
|
* scrollbar is considered to be fixed width, arbitrary height.
|
|
*/
|
|
|
|
Rectangle vsbR = new Rectangle(0, availR.y - vpbInsets.top, 0, 0);
|
|
|
|
boolean vsbNeeded;
|
|
if (isEmpty) {
|
|
vsbNeeded = false;
|
|
}
|
|
else if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
|
|
vsbNeeded = true;
|
|
}
|
|
else if (vsbPolicy == VERTICAL_SCROLLBAR_NEVER) {
|
|
vsbNeeded = false;
|
|
}
|
|
else { // vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED
|
|
vsbNeeded = !viewTracksViewportHeight && (viewPrefSize.height > extentSize.height);
|
|
}
|
|
|
|
|
|
if ((vsb != null) && vsbNeeded) {
|
|
adjustForVSB(true, availR, vsbR, vpbInsets, leftToRight);
|
|
extentSize = viewport.toViewCoordinates(availR.getSize());
|
|
}
|
|
|
|
/* If there's a horizontal scrollbar and we need one, allocate
|
|
* space for it (we'll make it visible later). A horizontal
|
|
* scrollbar is considered to be fixed height, arbitrary width.
|
|
*/
|
|
|
|
Rectangle hsbR = new Rectangle(availR.x - vpbInsets.left, 0, 0, 0);
|
|
boolean hsbNeeded;
|
|
if (isEmpty) {
|
|
hsbNeeded = false;
|
|
}
|
|
else if (hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) {
|
|
hsbNeeded = true;
|
|
}
|
|
else if (hsbPolicy == HORIZONTAL_SCROLLBAR_NEVER) {
|
|
hsbNeeded = false;
|
|
}
|
|
else { // hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED
|
|
hsbNeeded = !viewTracksViewportWidth && (viewPrefSize.width > extentSize.width);
|
|
}
|
|
|
|
if ((hsb != null) && hsbNeeded) {
|
|
adjustForHSB(true, availR, hsbR, vpbInsets);
|
|
|
|
/* If we added the horizontal scrollbar then we've implicitly
|
|
* reduced the vertical space available to the viewport.
|
|
* As a consequence we may have to add the vertical scrollbar,
|
|
* if that hasn't been done so already. Of course we
|
|
* don't bother with any of this if the vsbPolicy is NEVER.
|
|
*/
|
|
if ((vsb != null) && !vsbNeeded &&
|
|
(vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
|
|
|
|
extentSize = viewport.toViewCoordinates(availR.getSize());
|
|
vsbNeeded = viewPrefSize.height > extentSize.height;
|
|
|
|
if (vsbNeeded) {
|
|
adjustForVSB(true, availR, vsbR, vpbInsets, leftToRight);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set the size of the viewport first, and then recheck the Scrollable
|
|
* methods. Some components base their return values for the Scrollable
|
|
* methods on the size of the Viewport, so that if we don't
|
|
* ask after resetting the bounds we may have gotten the wrong
|
|
* answer.
|
|
*/
|
|
|
|
if (viewport != null) {
|
|
viewport.setBounds(availR);
|
|
|
|
if (sv != null) {
|
|
extentSize = viewport.toViewCoordinates(availR.getSize());
|
|
|
|
boolean oldHSBNeeded = hsbNeeded;
|
|
boolean oldVSBNeeded = vsbNeeded;
|
|
viewTracksViewportWidth = sv.
|
|
getScrollableTracksViewportWidth();
|
|
viewTracksViewportHeight = sv.
|
|
getScrollableTracksViewportHeight();
|
|
if (vsb != null && vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED) {
|
|
boolean newVSBNeeded = !viewTracksViewportHeight &&
|
|
(viewPrefSize.height > extentSize.height);
|
|
if (newVSBNeeded != vsbNeeded) {
|
|
vsbNeeded = newVSBNeeded;
|
|
adjustForVSB(vsbNeeded, availR, vsbR, vpbInsets,
|
|
leftToRight);
|
|
extentSize = viewport.toViewCoordinates
|
|
(availR.getSize());
|
|
}
|
|
}
|
|
if (hsb != null && hsbPolicy ==HORIZONTAL_SCROLLBAR_AS_NEEDED){
|
|
boolean newHSBbNeeded = !viewTracksViewportWidth &&
|
|
(viewPrefSize.width > extentSize.width);
|
|
if (newHSBbNeeded != hsbNeeded) {
|
|
hsbNeeded = newHSBbNeeded;
|
|
adjustForHSB(hsbNeeded, availR, hsbR, vpbInsets);
|
|
if ((vsb != null) && !vsbNeeded &&
|
|
(vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
|
|
|
|
extentSize = viewport.toViewCoordinates
|
|
(availR.getSize());
|
|
vsbNeeded = viewPrefSize.height >
|
|
extentSize.height;
|
|
|
|
if (vsbNeeded) {
|
|
adjustForVSB(true, availR, vsbR, vpbInsets,
|
|
leftToRight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (oldHSBNeeded != hsbNeeded ||
|
|
oldVSBNeeded != vsbNeeded) {
|
|
viewport.setBounds(availR);
|
|
// You could argue that we should recheck the
|
|
// Scrollable methods again until they stop changing,
|
|
// but they might never stop changing, so we stop here
|
|
// and don't do any additional checks.
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We now have the final size of the viewport: availR.
|
|
* Now fixup the header and scrollbar widths/heights.
|
|
*/
|
|
vsbR.height = availR.height + vpbInsets.top + vpbInsets.bottom;
|
|
hsbR.width = availR.width + vpbInsets.left + vpbInsets.right;
|
|
rowHeadR.height = availR.height + vpbInsets.top + vpbInsets.bottom;
|
|
rowHeadR.y = availR.y - vpbInsets.top;
|
|
colHeadR.width = availR.width + vpbInsets.left + vpbInsets.right;
|
|
colHeadR.x = availR.x - vpbInsets.left;
|
|
|
|
/* Set the bounds of the remaining components. The scrollbars
|
|
* are made invisible if they're not needed.
|
|
*/
|
|
|
|
if (rowHead != null) {
|
|
rowHead.setBounds(rowHeadR);
|
|
}
|
|
|
|
if (colHead != null) {
|
|
colHead.setBounds(colHeadR);
|
|
}
|
|
|
|
if (vsb != null) {
|
|
if (vsbNeeded) {
|
|
if (colHead != null &&
|
|
UIManager.getBoolean("ScrollPane.fillUpperCorner"))
|
|
{
|
|
if ((leftToRight && upperRight == null) ||
|
|
(!leftToRight && upperLeft == null))
|
|
{
|
|
// This is used primarily for GTK L&F, which needs to
|
|
// extend the vertical scrollbar to fill the upper
|
|
// corner near the column header. Note that we skip
|
|
// this step (and use the default behavior) if the
|
|
// user has set a custom corner component.
|
|
vsbR.y = colHeadR.y;
|
|
vsbR.height += colHeadR.height;
|
|
}
|
|
}
|
|
vsb.setVisible(true);
|
|
vsb.setBounds(vsbR);
|
|
}
|
|
else {
|
|
vsb.setVisible(false);
|
|
}
|
|
}
|
|
|
|
if (hsb != null) {
|
|
if (hsbNeeded) {
|
|
if (rowHead != null &&
|
|
UIManager.getBoolean("ScrollPane.fillLowerCorner"))
|
|
{
|
|
if ((leftToRight && lowerLeft == null) ||
|
|
(!leftToRight && lowerRight == null))
|
|
{
|
|
// This is used primarily for GTK L&F, which needs to
|
|
// extend the horizontal scrollbar to fill the lower
|
|
// corner near the row header. Note that we skip
|
|
// this step (and use the default behavior) if the
|
|
// user has set a custom corner component.
|
|
if (leftToRight) {
|
|
hsbR.x = rowHeadR.x;
|
|
}
|
|
hsbR.width += rowHeadR.width;
|
|
}
|
|
}
|
|
hsb.setVisible(true);
|
|
hsb.setBounds(hsbR);
|
|
}
|
|
else {
|
|
hsb.setVisible(false);
|
|
}
|
|
}
|
|
|
|
if (lowerLeft != null) {
|
|
lowerLeft.setBounds(leftToRight ? rowHeadR.x : vsbR.x,
|
|
hsbR.y,
|
|
leftToRight ? rowHeadR.width : vsbR.width,
|
|
hsbR.height);
|
|
}
|
|
|
|
if (lowerRight != null) {
|
|
lowerRight.setBounds(leftToRight ? vsbR.x : rowHeadR.x,
|
|
hsbR.y,
|
|
leftToRight ? vsbR.width : rowHeadR.width,
|
|
hsbR.height);
|
|
}
|
|
|
|
if (upperLeft != null) {
|
|
upperLeft.setBounds(leftToRight ? rowHeadR.x : vsbR.x,
|
|
colHeadR.y,
|
|
leftToRight ? rowHeadR.width : vsbR.width,
|
|
colHeadR.height);
|
|
}
|
|
|
|
if (upperRight != null) {
|
|
upperRight.setBounds(leftToRight ? vsbR.x : rowHeadR.x,
|
|
colHeadR.y,
|
|
leftToRight ? vsbR.width : rowHeadR.width,
|
|
colHeadR.height);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adjusts the <code>Rectangle</code> <code>available</code> based on if
|
|
* the vertical scrollbar is needed (<code>wantsVSB</code>).
|
|
* The location of the vsb is updated in <code>vsbR</code>, and
|
|
* the viewport border insets (<code>vpbInsets</code>) are used to offset
|
|
* the vsb. This is only called when <code>wantsVSB</code> has
|
|
* changed, eg you shouldn't invoke adjustForVSB(true) twice.
|
|
*/
|
|
private void adjustForVSB(boolean wantsVSB, Rectangle available,
|
|
Rectangle vsbR, Insets vpbInsets,
|
|
boolean leftToRight) {
|
|
int oldWidth = vsbR.width;
|
|
if (wantsVSB) {
|
|
int vsbWidth = Math.max(0, Math.min(vsb.getPreferredSize().width,
|
|
available.width));
|
|
|
|
available.width -= vsbWidth;
|
|
vsbR.width = vsbWidth;
|
|
|
|
if( leftToRight ) {
|
|
vsbR.x = available.x + available.width + vpbInsets.right;
|
|
} else {
|
|
vsbR.x = available.x - vpbInsets.left;
|
|
available.x += vsbWidth;
|
|
}
|
|
}
|
|
else {
|
|
available.width += oldWidth;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adjusts the <code>Rectangle</code> <code>available</code> based on if
|
|
* the horizontal scrollbar is needed (<code>wantsHSB</code>).
|
|
* The location of the hsb is updated in <code>hsbR</code>, and
|
|
* the viewport border insets (<code>vpbInsets</code>) are used to offset
|
|
* the hsb. This is only called when <code>wantsHSB</code> has
|
|
* changed, eg you shouldn't invoked adjustForHSB(true) twice.
|
|
*/
|
|
private void adjustForHSB(boolean wantsHSB, Rectangle available,
|
|
Rectangle hsbR, Insets vpbInsets) {
|
|
int oldHeight = hsbR.height;
|
|
if (wantsHSB) {
|
|
int hsbHeight = Math.max(0, Math.min(available.height,
|
|
hsb.getPreferredSize().height));
|
|
|
|
available.height -= hsbHeight;
|
|
hsbR.y = available.y + available.height + vpbInsets.bottom;
|
|
hsbR.height = hsbHeight;
|
|
}
|
|
else {
|
|
available.height += oldHeight;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Returns the bounds of the border around the specified scroll pane's
|
|
* viewport.
|
|
*
|
|
* @return the size and position of the viewport border
|
|
* @deprecated As of JDK version Swing1.1
|
|
* replaced by <code>JScrollPane.getViewportBorderBounds()</code>.
|
|
*/
|
|
@Deprecated
|
|
public Rectangle getViewportBorderBounds(JScrollPane scrollpane) {
|
|
return scrollpane.getViewportBorderBounds();
|
|
}
|
|
|
|
/**
|
|
* The UI resource version of <code>ScrollPaneLayout</code>.
|
|
*/
|
|
public static class UIResource extends ScrollPaneLayout implements javax.swing.plaf.UIResource {}
|
|
}
|