/*
* Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
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 {}
}