Java tutorial
/* * Copyright (c) 1997, 2014, 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 * @since 1.2 */ @SuppressWarnings("serial") // Same-version serialization only 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> * * @param sp an instance of the {@code JScrollPane} */ 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 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 (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 (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. * * @param scrollpane an instance of the {@code JScrollPane} * @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 { } }