0001 /*
0002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.swing;
0027
0028 import javax.swing.border.*;
0029
0030 import java.awt.LayoutManager;
0031 import java.awt.Component;
0032 import java.awt.Container;
0033 import java.awt.Rectangle;
0034 import java.awt.Dimension;
0035 import java.awt.Insets;
0036 import java.io.Serializable;
0037
0038 /**
0039 * The layout manager used by <code>JScrollPane</code>.
0040 * <code>JScrollPaneLayout</code> is
0041 * responsible for nine components: a viewport, two scrollbars,
0042 * a row header, a column header, and four "corner" components.
0043 * <p>
0044 * <strong>Warning:</strong>
0045 * Serialized objects of this class will not be compatible with
0046 * future Swing releases. The current serialization support is
0047 * appropriate for short term storage or RMI between applications running
0048 * the same version of Swing. As of 1.4, support for long term storage
0049 * of all JavaBeans<sup><font size="-2">TM</font></sup>
0050 * has been added to the <code>java.beans</code> package.
0051 * Please see {@link java.beans.XMLEncoder}.
0052 *
0053 * @see JScrollPane
0054 * @see JViewport
0055 *
0056 * @version 1.70 05/05/07
0057 * @author Hans Muller
0058 */
0059 public class ScrollPaneLayout implements LayoutManager,
0060 ScrollPaneConstants, Serializable {
0061
0062 /**
0063 * The scrollpane's viewport child.
0064 * Default is an empty <code>JViewport</code>.
0065 * @see JScrollPane#setViewport
0066 */
0067 protected JViewport viewport;
0068
0069 /**
0070 * The scrollpane's vertical scrollbar child.
0071 * Default is a <code>JScrollBar</code>.
0072 * @see JScrollPane#setVerticalScrollBar
0073 */
0074 protected JScrollBar vsb;
0075
0076 /**
0077 * The scrollpane's horizontal scrollbar child.
0078 * Default is a <code>JScrollBar</code>.
0079 * @see JScrollPane#setHorizontalScrollBar
0080 */
0081 protected JScrollBar hsb;
0082
0083 /**
0084 * The row header child. Default is <code>null</code>.
0085 * @see JScrollPane#setRowHeader
0086 */
0087 protected JViewport rowHead;
0088
0089 /**
0090 * The column header child. Default is <code>null</code>.
0091 * @see JScrollPane#setColumnHeader
0092 */
0093 protected JViewport colHead;
0094
0095 /**
0096 * The component to display in the lower left corner.
0097 * Default is <code>null</code>.
0098 * @see JScrollPane#setCorner
0099 */
0100 protected Component lowerLeft;
0101
0102 /**
0103 * The component to display in the lower right corner.
0104 * Default is <code>null</code>.
0105 * @see JScrollPane#setCorner
0106 */
0107 protected Component lowerRight;
0108
0109 /**
0110 * The component to display in the upper left corner.
0111 * Default is <code>null</code>.
0112 * @see JScrollPane#setCorner
0113 */
0114 protected Component upperLeft;
0115
0116 /**
0117 * The component to display in the upper right corner.
0118 * Default is <code>null</code>.
0119 * @see JScrollPane#setCorner
0120 */
0121 protected Component upperRight;
0122
0123 /**
0124 * The display policy for the vertical scrollbar.
0125 * The default is <code>ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED</code>.
0126 * <p>
0127 * This field is obsolete, please use the <code>JScrollPane</code> field instead.
0128 *
0129 * @see JScrollPane#setVerticalScrollBarPolicy
0130 */
0131 protected int vsbPolicy = VERTICAL_SCROLLBAR_AS_NEEDED;
0132
0133 /**
0134 * The display policy for the horizontal scrollbar.
0135 * The default is <code>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED</code>.
0136 * <p>
0137 * This field is obsolete, please use the <code>JScrollPane</code> field instead.
0138 *
0139 * @see JScrollPane#setHorizontalScrollBarPolicy
0140 */
0141 protected int hsbPolicy = HORIZONTAL_SCROLLBAR_AS_NEEDED;
0142
0143 /**
0144 * This method is invoked after the ScrollPaneLayout is set as the
0145 * LayoutManager of a <code>JScrollPane</code>.
0146 * It initializes all of the internal fields that
0147 * are ordinarily set by <code>addLayoutComponent</code>. For example:
0148 * <pre>
0149 * ScrollPaneLayout mySPLayout = new ScrollPanelLayout() {
0150 * public void layoutContainer(Container p) {
0151 * super.layoutContainer(p);
0152 * // do some extra work here ...
0153 * }
0154 * };
0155 * scrollpane.setLayout(mySPLayout):
0156 * </pre>
0157 */
0158 public void syncWithScrollPane(JScrollPane sp) {
0159 viewport = sp.getViewport();
0160 vsb = sp.getVerticalScrollBar();
0161 hsb = sp.getHorizontalScrollBar();
0162 rowHead = sp.getRowHeader();
0163 colHead = sp.getColumnHeader();
0164 lowerLeft = sp.getCorner(LOWER_LEFT_CORNER);
0165 lowerRight = sp.getCorner(LOWER_RIGHT_CORNER);
0166 upperLeft = sp.getCorner(UPPER_LEFT_CORNER);
0167 upperRight = sp.getCorner(UPPER_RIGHT_CORNER);
0168 vsbPolicy = sp.getVerticalScrollBarPolicy();
0169 hsbPolicy = sp.getHorizontalScrollBarPolicy();
0170 }
0171
0172 /**
0173 * Removes an existing component. When a new component, such as
0174 * the left corner, or vertical scrollbar, is added, the old one,
0175 * if it exists, must be removed.
0176 * <p>
0177 * This method returns <code>newC</code>. If <code>oldC</code> is
0178 * not equal to <code>newC</code> and is non-<code>null</code>,
0179 * it will be removed from its parent.
0180 *
0181 * @param oldC the <code>Component</code> to replace
0182 * @param newC the <code>Component</code> to add
0183 * @return the <code>newC</code>
0184 */
0185 protected Component addSingletonComponent(Component oldC,
0186 Component newC) {
0187 if ((oldC != null) && (oldC != newC)) {
0188 oldC.getParent().remove(oldC);
0189 }
0190 return newC;
0191 }
0192
0193 /**
0194 * Adds the specified component to the layout. The layout is
0195 * identified using one of:
0196 * <ul>
0197 * <li>ScrollPaneConstants.VIEWPORT
0198 * <li>ScrollPaneConstants.VERTICAL_SCROLLBAR
0199 * <li>ScrollPaneConstants.HORIZONTAL_SCROLLBAR
0200 * <li>ScrollPaneConstants.ROW_HEADER
0201 * <li>ScrollPaneConstants.COLUMN_HEADER
0202 * <li>ScrollPaneConstants.LOWER_LEFT_CORNER
0203 * <li>ScrollPaneConstants.LOWER_RIGHT_CORNER
0204 * <li>ScrollPaneConstants.UPPER_LEFT_CORNER
0205 * <li>ScrollPaneConstants.UPPER_RIGHT_CORNER
0206 * </ul>
0207 *
0208 * @param s the component identifier
0209 * @param c the the component to be added
0210 * @exception IllegalArgumentException if <code>s</code> is an invalid key
0211 */
0212 public void addLayoutComponent(String s, Component c) {
0213 if (s.equals(VIEWPORT)) {
0214 viewport = (JViewport) addSingletonComponent(viewport, c);
0215 } else if (s.equals(VERTICAL_SCROLLBAR)) {
0216 vsb = (JScrollBar) addSingletonComponent(vsb, c);
0217 } else if (s.equals(HORIZONTAL_SCROLLBAR)) {
0218 hsb = (JScrollBar) addSingletonComponent(hsb, c);
0219 } else if (s.equals(ROW_HEADER)) {
0220 rowHead = (JViewport) addSingletonComponent(rowHead, c);
0221 } else if (s.equals(COLUMN_HEADER)) {
0222 colHead = (JViewport) addSingletonComponent(colHead, c);
0223 } else if (s.equals(LOWER_LEFT_CORNER)) {
0224 lowerLeft = addSingletonComponent(lowerLeft, c);
0225 } else if (s.equals(LOWER_RIGHT_CORNER)) {
0226 lowerRight = addSingletonComponent(lowerRight, c);
0227 } else if (s.equals(UPPER_LEFT_CORNER)) {
0228 upperLeft = addSingletonComponent(upperLeft, c);
0229 } else if (s.equals(UPPER_RIGHT_CORNER)) {
0230 upperRight = addSingletonComponent(upperRight, c);
0231 } else {
0232 throw new IllegalArgumentException("invalid layout key "
0233 + s);
0234 }
0235 }
0236
0237 /**
0238 * Removes the specified component from the layout.
0239 *
0240 * @param c the component to remove
0241 */
0242 public void removeLayoutComponent(Component c) {
0243 if (c == viewport) {
0244 viewport = null;
0245 } else if (c == vsb) {
0246 vsb = null;
0247 } else if (c == hsb) {
0248 hsb = null;
0249 } else if (c == rowHead) {
0250 rowHead = null;
0251 } else if (c == colHead) {
0252 colHead = null;
0253 } else if (c == lowerLeft) {
0254 lowerLeft = null;
0255 } else if (c == lowerRight) {
0256 lowerRight = null;
0257 } else if (c == upperLeft) {
0258 upperLeft = null;
0259 } else if (c == upperRight) {
0260 upperRight = null;
0261 }
0262 }
0263
0264 /**
0265 * Returns the vertical scrollbar-display policy.
0266 *
0267 * @return an integer giving the display policy
0268 * @see #setVerticalScrollBarPolicy
0269 */
0270 public int getVerticalScrollBarPolicy() {
0271 return vsbPolicy;
0272 }
0273
0274 /**
0275 * Sets the vertical scrollbar-display policy. The options
0276 * are:
0277 * <ul>
0278 * <li>ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED
0279 * <li>ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER
0280 * <li>ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS
0281 * </ul>
0282 * Note: Applications should use the <code>JScrollPane</code> version
0283 * of this method. It only exists for backwards compatibility
0284 * with the Swing 1.0.2 (and earlier) versions of this class.
0285 *
0286 * @param x an integer giving the display policy
0287 * @exception IllegalArgumentException if <code>x</code> is an invalid
0288 * vertical scroll bar policy, as listed above
0289 */
0290 public void setVerticalScrollBarPolicy(int x) {
0291 switch (x) {
0292 case VERTICAL_SCROLLBAR_AS_NEEDED:
0293 case VERTICAL_SCROLLBAR_NEVER:
0294 case VERTICAL_SCROLLBAR_ALWAYS:
0295 vsbPolicy = x;
0296 break;
0297 default:
0298 throw new IllegalArgumentException(
0299 "invalid verticalScrollBarPolicy");
0300 }
0301 }
0302
0303 /**
0304 * Returns the horizontal scrollbar-display policy.
0305 *
0306 * @return an integer giving the display policy
0307 * @see #setHorizontalScrollBarPolicy
0308 */
0309 public int getHorizontalScrollBarPolicy() {
0310 return hsbPolicy;
0311 }
0312
0313 /**
0314 * Sets the horizontal scrollbar-display policy.
0315 * The options are:<ul>
0316 * <li>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED
0317 * <li>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
0318 * <li>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS
0319 * </ul>
0320 * Note: Applications should use the <code>JScrollPane</code> version
0321 * of this method. It only exists for backwards compatibility
0322 * with the Swing 1.0.2 (and earlier) versions of this class.
0323 *
0324 * @param x an int giving the display policy
0325 * @exception IllegalArgumentException if <code>x</code> is not a valid
0326 * horizontal scrollbar policy, as listed above
0327 */
0328 public void setHorizontalScrollBarPolicy(int x) {
0329 switch (x) {
0330 case HORIZONTAL_SCROLLBAR_AS_NEEDED:
0331 case HORIZONTAL_SCROLLBAR_NEVER:
0332 case HORIZONTAL_SCROLLBAR_ALWAYS:
0333 hsbPolicy = x;
0334 break;
0335 default:
0336 throw new IllegalArgumentException(
0337 "invalid horizontalScrollBarPolicy");
0338 }
0339 }
0340
0341 /**
0342 * Returns the <code>JViewport</code> object that displays the
0343 * scrollable contents.
0344 * @return the <code>JViewport</code> object that displays the scrollable contents
0345 * @see JScrollPane#getViewport
0346 */
0347 public JViewport getViewport() {
0348 return viewport;
0349 }
0350
0351 /**
0352 * Returns the <code>JScrollBar</code> object that handles horizontal scrolling.
0353 * @return the <code>JScrollBar</code> object that handles horizontal scrolling
0354 * @see JScrollPane#getHorizontalScrollBar
0355 */
0356 public JScrollBar getHorizontalScrollBar() {
0357 return hsb;
0358 }
0359
0360 /**
0361 * Returns the <code>JScrollBar</code> object that handles vertical scrolling.
0362 * @return the <code>JScrollBar</code> object that handles vertical scrolling
0363 * @see JScrollPane#getVerticalScrollBar
0364 */
0365 public JScrollBar getVerticalScrollBar() {
0366 return vsb;
0367 }
0368
0369 /**
0370 * Returns the <code>JViewport</code> object that is the row header.
0371 * @return the <code>JViewport</code> object that is the row header
0372 * @see JScrollPane#getRowHeader
0373 */
0374 public JViewport getRowHeader() {
0375 return rowHead;
0376 }
0377
0378 /**
0379 * Returns the <code>JViewport</code> object that is the column header.
0380 * @return the <code>JViewport</code> object that is the column header
0381 * @see JScrollPane#getColumnHeader
0382 */
0383 public JViewport getColumnHeader() {
0384 return colHead;
0385 }
0386
0387 /**
0388 * Returns the <code>Component</code> at the specified corner.
0389 * @param key the <code>String</code> specifying the corner
0390 * @return the <code>Component</code> at the specified corner, as defined in
0391 * {@link ScrollPaneConstants}; if <code>key</code> is not one of the
0392 * four corners, <code>null</code> is returned
0393 * @see JScrollPane#getCorner
0394 */
0395 public Component getCorner(String key) {
0396 if (key.equals(LOWER_LEFT_CORNER)) {
0397 return lowerLeft;
0398 } else if (key.equals(LOWER_RIGHT_CORNER)) {
0399 return lowerRight;
0400 } else if (key.equals(UPPER_LEFT_CORNER)) {
0401 return upperLeft;
0402 } else if (key.equals(UPPER_RIGHT_CORNER)) {
0403 return upperRight;
0404 } else {
0405 return null;
0406 }
0407 }
0408
0409 /**
0410 * The preferred size of a <code>ScrollPane</code> is the size of the insets,
0411 * plus the preferred size of the viewport, plus the preferred size of
0412 * the visible headers, plus the preferred size of the scrollbars
0413 * that will appear given the current view and the current
0414 * scrollbar displayPolicies.
0415 * <p>Note that the rowHeader is calculated as part of the preferred width
0416 * and the colHeader is calculated as part of the preferred size.
0417 *
0418 * @param parent the <code>Container</code> that will be laid out
0419 * @return a <code>Dimension</code> object specifying the preferred size of the
0420 * viewport and any scrollbars
0421 * @see ViewportLayout
0422 * @see LayoutManager
0423 */
0424 public Dimension preferredLayoutSize(Container parent) {
0425 /* Sync the (now obsolete) policy fields with the
0426 * JScrollPane.
0427 */
0428 JScrollPane scrollPane = (JScrollPane) parent;
0429 vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
0430 hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
0431
0432 Insets insets = parent.getInsets();
0433 int prefWidth = insets.left + insets.right;
0434 int prefHeight = insets.top + insets.bottom;
0435
0436 /* Note that viewport.getViewSize() is equivalent to
0437 * viewport.getView().getPreferredSize() modulo a null
0438 * view or a view whose size was explicitly set.
0439 */
0440
0441 Dimension extentSize = null;
0442 Dimension viewSize = null;
0443 Component view = null;
0444
0445 if (viewport != null) {
0446 extentSize = viewport.getPreferredSize();
0447 viewSize = viewport.getViewSize();
0448 view = viewport.getView();
0449 }
0450
0451 /* If there's a viewport add its preferredSize.
0452 */
0453
0454 if (extentSize != null) {
0455 prefWidth += extentSize.width;
0456 prefHeight += extentSize.height;
0457 }
0458
0459 /* If there's a JScrollPane.viewportBorder, add its insets.
0460 */
0461
0462 Border viewportBorder = scrollPane.getViewportBorder();
0463 if (viewportBorder != null) {
0464 Insets vpbInsets = viewportBorder.getBorderInsets(parent);
0465 prefWidth += vpbInsets.left + vpbInsets.right;
0466 prefHeight += vpbInsets.top + vpbInsets.bottom;
0467 }
0468
0469 /* If a header exists and it's visible, factor its
0470 * preferred size in.
0471 */
0472
0473 if ((rowHead != null) && rowHead.isVisible()) {
0474 prefWidth += rowHead.getPreferredSize().width;
0475 }
0476
0477 if ((colHead != null) && colHead.isVisible()) {
0478 prefHeight += colHead.getPreferredSize().height;
0479 }
0480
0481 /* If a scrollbar is going to appear, factor its preferred size in.
0482 * If the scrollbars policy is AS_NEEDED, this can be a little
0483 * tricky:
0484 *
0485 * - If the view is a Scrollable then scrollableTracksViewportWidth
0486 * and scrollableTracksViewportHeight can be used to effectively
0487 * disable scrolling (if they're true) in their respective dimensions.
0488 *
0489 * - Assuming that a scrollbar hasn't been disabled by the
0490 * previous constraint, we need to decide if the scrollbar is going
0491 * to appear to correctly compute the JScrollPanes preferred size.
0492 * To do this we compare the preferredSize of the viewport (the
0493 * extentSize) to the preferredSize of the view. Although we're
0494 * not responsible for laying out the view we'll assume that the
0495 * JViewport will always give it its preferredSize.
0496 */
0497
0498 if ((vsb != null) && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
0499 if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
0500 prefWidth += vsb.getPreferredSize().width;
0501 } else if ((viewSize != null) && (extentSize != null)) {
0502 boolean canScroll = true;
0503 if (view instanceof Scrollable) {
0504 canScroll = !((Scrollable) view)
0505 .getScrollableTracksViewportHeight();
0506 }
0507 if (canScroll && (viewSize.height > extentSize.height)) {
0508 prefWidth += vsb.getPreferredSize().width;
0509 }
0510 }
0511 }
0512
0513 if ((hsb != null) && (hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER)) {
0514 if (hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) {
0515 prefHeight += hsb.getPreferredSize().height;
0516 } else if ((viewSize != null) && (extentSize != null)) {
0517 boolean canScroll = true;
0518 if (view instanceof Scrollable) {
0519 canScroll = !((Scrollable) view)
0520 .getScrollableTracksViewportWidth();
0521 }
0522 if (canScroll && (viewSize.width > extentSize.width)) {
0523 prefHeight += hsb.getPreferredSize().height;
0524 }
0525 }
0526 }
0527
0528 return new Dimension(prefWidth, prefHeight);
0529 }
0530
0531 /**
0532 * The minimum size of a <code>ScrollPane</code> is the size of the insets
0533 * plus minimum size of the viewport, plus the scrollpane's
0534 * viewportBorder insets, plus the minimum size
0535 * of the visible headers, plus the minimum size of the
0536 * scrollbars whose displayPolicy isn't NEVER.
0537 *
0538 * @param parent the <code>Container</code> that will be laid out
0539 * @return a <code>Dimension</code> object specifying the minimum size
0540 */
0541 public Dimension minimumLayoutSize(Container parent) {
0542 /* Sync the (now obsolete) policy fields with the
0543 * JScrollPane.
0544 */
0545 JScrollPane scrollPane = (JScrollPane) parent;
0546 vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
0547 hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
0548
0549 Insets insets = parent.getInsets();
0550 int minWidth = insets.left + insets.right;
0551 int minHeight = insets.top + insets.bottom;
0552
0553 /* If there's a viewport add its minimumSize.
0554 */
0555
0556 if (viewport != null) {
0557 Dimension size = viewport.getMinimumSize();
0558 minWidth += size.width;
0559 minHeight += size.height;
0560 }
0561
0562 /* If there's a JScrollPane.viewportBorder, add its insets.
0563 */
0564
0565 Border viewportBorder = scrollPane.getViewportBorder();
0566 if (viewportBorder != null) {
0567 Insets vpbInsets = viewportBorder.getBorderInsets(parent);
0568 minWidth += vpbInsets.left + vpbInsets.right;
0569 minHeight += vpbInsets.top + vpbInsets.bottom;
0570 }
0571
0572 /* If a header exists and it's visible, factor its
0573 * minimum size in.
0574 */
0575
0576 if ((rowHead != null) && rowHead.isVisible()) {
0577 Dimension size = rowHead.getMinimumSize();
0578 minWidth += size.width;
0579 minHeight = Math.max(minHeight, size.height);
0580 }
0581
0582 if ((colHead != null) && colHead.isVisible()) {
0583 Dimension size = colHead.getMinimumSize();
0584 minWidth = Math.max(minWidth, size.width);
0585 minHeight += size.height;
0586 }
0587
0588 /* If a scrollbar might appear, factor its minimum
0589 * size in.
0590 */
0591
0592 if ((vsb != null) && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
0593 Dimension size = vsb.getMinimumSize();
0594 minWidth += size.width;
0595 minHeight = Math.max(minHeight, size.height);
0596 }
0597
0598 if ((hsb != null) && (hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER)) {
0599 Dimension size = hsb.getMinimumSize();
0600 minWidth = Math.max(minWidth, size.width);
0601 minHeight += size.height;
0602 }
0603
0604 return new Dimension(minWidth, minHeight);
0605 }
0606
0607 /**
0608 * Lays out the scrollpane. The positioning of components depends on
0609 * the following constraints:
0610 * <ul>
0611 * <li> The row header, if present and visible, gets its preferred
0612 * width and the viewport's height.
0613 *
0614 * <li> The column header, if present and visible, gets its preferred
0615 * height and the viewport's width.
0616 *
0617 * <li> If a vertical scrollbar is needed, i.e. if the viewport's extent
0618 * height is smaller than its view height or if the <code>displayPolicy</code>
0619 * is ALWAYS, it's treated like the row header with respect to its
0620 * dimensions and is made visible.
0621 *
0622 * <li> If a horizontal scrollbar is needed, it is treated like the
0623 * column header (see the paragraph above regarding the vertical scrollbar).
0624 *
0625 * <li> If the scrollpane has a non-<code>null</code>
0626 * <code>viewportBorder</code>, then space is allocated for that.
0627 *
0628 * <li> The viewport gets the space available after accounting for
0629 * the previous constraints.
0630 *
0631 * <li> The corner components, if provided, are aligned with the
0632 * ends of the scrollbars and headers. If there is a vertical
0633 * scrollbar, the right corners appear; if there is a horizontal
0634 * scrollbar, the lower corners appear; a row header gets left
0635 * corners, and a column header gets upper corners.
0636 * </ul>
0637 *
0638 * @param parent the <code>Container</code> to lay out
0639 */
0640 public void layoutContainer(Container parent) {
0641 /* Sync the (now obsolete) policy fields with the
0642 * JScrollPane.
0643 */
0644 JScrollPane scrollPane = (JScrollPane) parent;
0645 vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
0646 hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
0647
0648 Rectangle availR = scrollPane.getBounds();
0649 availR.x = availR.y = 0;
0650
0651 Insets insets = parent.getInsets();
0652 availR.x = insets.left;
0653 availR.y = insets.top;
0654 availR.width -= insets.left + insets.right;
0655 availR.height -= insets.top + insets.bottom;
0656
0657 /* Get the scrollPane's orientation.
0658 */
0659 boolean leftToRight = SwingUtilities.isLeftToRight(scrollPane);
0660
0661 /* If there's a visible column header remove the space it
0662 * needs from the top of availR. The column header is treated
0663 * as if it were fixed height, arbitrary width.
0664 */
0665
0666 Rectangle colHeadR = new Rectangle(0, availR.y, 0, 0);
0667
0668 if ((colHead != null) && (colHead.isVisible())) {
0669 int colHeadHeight = Math.min(availR.height, colHead
0670 .getPreferredSize().height);
0671 colHeadR.height = colHeadHeight;
0672 availR.y += colHeadHeight;
0673 availR.height -= colHeadHeight;
0674 }
0675
0676 /* If there's a visible row header remove the space it needs
0677 * from the left or right of availR. The row header is treated
0678 * as if it were fixed width, arbitrary height.
0679 */
0680
0681 Rectangle rowHeadR = new Rectangle(0, 0, 0, 0);
0682
0683 if ((rowHead != null) && (rowHead.isVisible())) {
0684 int rowHeadWidth = Math.min(availR.width, rowHead
0685 .getPreferredSize().width);
0686 rowHeadR.width = rowHeadWidth;
0687 availR.width -= rowHeadWidth;
0688 if (leftToRight) {
0689 rowHeadR.x = availR.x;
0690 availR.x += rowHeadWidth;
0691 } else {
0692 rowHeadR.x = availR.x + availR.width;
0693 }
0694 }
0695
0696 /* If there's a JScrollPane.viewportBorder, remove the
0697 * space it occupies for availR.
0698 */
0699
0700 Border viewportBorder = scrollPane.getViewportBorder();
0701 Insets vpbInsets;
0702 if (viewportBorder != null) {
0703 vpbInsets = viewportBorder.getBorderInsets(parent);
0704 availR.x += vpbInsets.left;
0705 availR.y += vpbInsets.top;
0706 availR.width -= vpbInsets.left + vpbInsets.right;
0707 availR.height -= vpbInsets.top + vpbInsets.bottom;
0708 } else {
0709 vpbInsets = new Insets(0, 0, 0, 0);
0710 }
0711
0712 /* At this point availR is the space available for the viewport
0713 * and scrollbars. rowHeadR is correct except for its height and y
0714 * and colHeadR is correct except for its width and x. Once we're
0715 * through computing the dimensions of these three parts we can
0716 * go back and set the dimensions of rowHeadR.height, rowHeadR.y,
0717 * colHeadR.width, colHeadR.x and the bounds for the corners.
0718 *
0719 * We'll decide about putting up scrollbars by comparing the
0720 * viewport views preferred size with the viewports extent
0721 * size (generally just its size). Using the preferredSize is
0722 * reasonable because layout proceeds top down - so we expect
0723 * the viewport to be laid out next. And we assume that the
0724 * viewports layout manager will give the view it's preferred
0725 * size. One exception to this is when the view implements
0726 * Scrollable and Scrollable.getViewTracksViewport{Width,Height}
0727 * methods return true. If the view is tracking the viewports
0728 * width we don't bother with a horizontal scrollbar, similarly
0729 * if view.getViewTracksViewport(Height) is true we don't bother
0730 * with a vertical scrollbar.
0731 */
0732
0733 Component view = (viewport != null) ? viewport.getView() : null;
0734 Dimension viewPrefSize = (view != null) ? view
0735 .getPreferredSize() : new Dimension(0, 0);
0736
0737 Dimension extentSize = (viewport != null) ? viewport
0738 .toViewCoordinates(availR.getSize()) : new Dimension(0,
0739 0);
0740
0741 boolean viewTracksViewportWidth = false;
0742 boolean viewTracksViewportHeight = false;
0743 boolean isEmpty = (availR.width < 0 || availR.height < 0);
0744 Scrollable sv;
0745 // Don't bother checking the Scrollable methods if there is no room
0746 // for the viewport, we aren't going to show any scrollbars in this
0747 // case anyway.
0748 if (!isEmpty && view instanceof Scrollable) {
0749 sv = (Scrollable) view;
0750 viewTracksViewportWidth = sv
0751 .getScrollableTracksViewportWidth();
0752 viewTracksViewportHeight = sv
0753 .getScrollableTracksViewportHeight();
0754 } else {
0755 sv = null;
0756 }
0757
0758 /* If there's a vertical scrollbar and we need one, allocate
0759 * space for it (we'll make it visible later). A vertical
0760 * scrollbar is considered to be fixed width, arbitrary height.
0761 */
0762
0763 Rectangle vsbR = new Rectangle(0, availR.y - vpbInsets.top, 0,
0764 0);
0765
0766 boolean vsbNeeded;
0767 if (isEmpty) {
0768 vsbNeeded = false;
0769 } else if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
0770 vsbNeeded = true;
0771 } else if (vsbPolicy == VERTICAL_SCROLLBAR_NEVER) {
0772 vsbNeeded = false;
0773 } else { // vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED
0774 vsbNeeded = !viewTracksViewportHeight
0775 && (viewPrefSize.height > extentSize.height);
0776 }
0777
0778 if ((vsb != null) && vsbNeeded) {
0779 adjustForVSB(true, availR, vsbR, vpbInsets, leftToRight);
0780 extentSize = viewport.toViewCoordinates(availR.getSize());
0781 }
0782
0783 /* If there's a horizontal scrollbar and we need one, allocate
0784 * space for it (we'll make it visible later). A horizontal
0785 * scrollbar is considered to be fixed height, arbitrary width.
0786 */
0787
0788 Rectangle hsbR = new Rectangle(availR.x - vpbInsets.left, 0, 0,
0789 0);
0790 boolean hsbNeeded;
0791 if (isEmpty) {
0792 hsbNeeded = false;
0793 } else if (hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) {
0794 hsbNeeded = true;
0795 } else if (hsbPolicy == HORIZONTAL_SCROLLBAR_NEVER) {
0796 hsbNeeded = false;
0797 } else { // hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED
0798 hsbNeeded = !viewTracksViewportWidth
0799 && (viewPrefSize.width > extentSize.width);
0800 }
0801
0802 if ((hsb != null) && hsbNeeded) {
0803 adjustForHSB(true, availR, hsbR, vpbInsets);
0804
0805 /* If we added the horizontal scrollbar then we've implicitly
0806 * reduced the vertical space available to the viewport.
0807 * As a consequence we may have to add the vertical scrollbar,
0808 * if that hasn't been done so already. Of course we
0809 * don't bother with any of this if the vsbPolicy is NEVER.
0810 */
0811 if ((vsb != null) && !vsbNeeded
0812 && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
0813
0814 extentSize = viewport.toViewCoordinates(availR
0815 .getSize());
0816 vsbNeeded = viewPrefSize.height > extentSize.height;
0817
0818 if (vsbNeeded) {
0819 adjustForVSB(true, availR, vsbR, vpbInsets,
0820 leftToRight);
0821 }
0822 }
0823 }
0824
0825 /* Set the size of the viewport first, and then recheck the Scrollable
0826 * methods. Some components base their return values for the Scrollable
0827 * methods on the size of the Viewport, so that if we don't
0828 * ask after resetting the bounds we may have gotten the wrong
0829 * answer.
0830 */
0831
0832 if (viewport != null) {
0833 viewport.setBounds(availR);
0834
0835 if (sv != null) {
0836 extentSize = viewport.toViewCoordinates(availR
0837 .getSize());
0838
0839 boolean oldHSBNeeded = hsbNeeded;
0840 boolean oldVSBNeeded = vsbNeeded;
0841 viewTracksViewportWidth = sv
0842 .getScrollableTracksViewportWidth();
0843 viewTracksViewportHeight = sv
0844 .getScrollableTracksViewportHeight();
0845 if (vsb != null
0846 && vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED) {
0847 boolean newVSBNeeded = !viewTracksViewportHeight
0848 && (viewPrefSize.height > extentSize.height);
0849 if (newVSBNeeded != vsbNeeded) {
0850 vsbNeeded = newVSBNeeded;
0851 adjustForVSB(vsbNeeded, availR, vsbR,
0852 vpbInsets, leftToRight);
0853 extentSize = viewport.toViewCoordinates(availR
0854 .getSize());
0855 }
0856 }
0857 if (hsb != null
0858 && hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED) {
0859 boolean newHSBbNeeded = !viewTracksViewportWidth
0860 && (viewPrefSize.width > extentSize.width);
0861 if (newHSBbNeeded != hsbNeeded) {
0862 hsbNeeded = newHSBbNeeded;
0863 adjustForHSB(hsbNeeded, availR, hsbR, vpbInsets);
0864 if ((vsb != null)
0865 && !vsbNeeded
0866 && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
0867
0868 extentSize = viewport
0869 .toViewCoordinates(availR.getSize());
0870 vsbNeeded = viewPrefSize.height > extentSize.height;
0871
0872 if (vsbNeeded) {
0873 adjustForVSB(true, availR, vsbR,
0874 vpbInsets, leftToRight);
0875 }
0876 }
0877 }
0878 }
0879 if (oldHSBNeeded != hsbNeeded
0880 || oldVSBNeeded != vsbNeeded) {
0881 viewport.setBounds(availR);
0882 // You could argue that we should recheck the
0883 // Scrollable methods again until they stop changing,
0884 // but they might never stop changing, so we stop here
0885 // and don't do any additional checks.
0886 }
0887 }
0888 }
0889
0890 /* We now have the final size of the viewport: availR.
0891 * Now fixup the header and scrollbar widths/heights.
0892 */
0893 vsbR.height = availR.height + vpbInsets.top + vpbInsets.bottom;
0894 hsbR.width = availR.width + vpbInsets.left + vpbInsets.right;
0895 rowHeadR.height = availR.height + vpbInsets.top
0896 + vpbInsets.bottom;
0897 rowHeadR.y = availR.y - vpbInsets.top;
0898 colHeadR.width = availR.width + vpbInsets.left
0899 + vpbInsets.right;
0900 colHeadR.x = availR.x - vpbInsets.left;
0901
0902 /* Set the bounds of the remaining components. The scrollbars
0903 * are made invisible if they're not needed.
0904 */
0905
0906 if (rowHead != null) {
0907 rowHead.setBounds(rowHeadR);
0908 }
0909
0910 if (colHead != null) {
0911 colHead.setBounds(colHeadR);
0912 }
0913
0914 if (vsb != null) {
0915 if (vsbNeeded) {
0916 if (colHead != null
0917 && UIManager
0918 .getBoolean("ScrollPane.fillUpperCorner")) {
0919 if ((leftToRight && upperRight == null)
0920 || (!leftToRight && upperLeft == null)) {
0921 // This is used primarily for GTK L&F, which needs to
0922 // extend the vertical scrollbar to fill the upper
0923 // corner near the column header. Note that we skip
0924 // this step (and use the default behavior) if the
0925 // user has set a custom corner component.
0926 vsbR.y = colHeadR.y;
0927 vsbR.height += colHeadR.height;
0928 }
0929 }
0930 vsb.setVisible(true);
0931 vsb.setBounds(vsbR);
0932 } else {
0933 vsb.setVisible(false);
0934 }
0935 }
0936
0937 if (hsb != null) {
0938 if (hsbNeeded) {
0939 if (rowHead != null
0940 && UIManager
0941 .getBoolean("ScrollPane.fillLowerCorner")) {
0942 if ((leftToRight && lowerLeft == null)
0943 || (!leftToRight && lowerRight == null)) {
0944 // This is used primarily for GTK L&F, which needs to
0945 // extend the horizontal scrollbar to fill the lower
0946 // corner near the row header. Note that we skip
0947 // this step (and use the default behavior) if the
0948 // user has set a custom corner component.
0949 if (leftToRight) {
0950 hsbR.x = rowHeadR.x;
0951 }
0952 hsbR.width += rowHeadR.width;
0953 }
0954 }
0955 hsb.setVisible(true);
0956 hsb.setBounds(hsbR);
0957 } else {
0958 hsb.setVisible(false);
0959 }
0960 }
0961
0962 if (lowerLeft != null) {
0963 lowerLeft.setBounds(leftToRight ? rowHeadR.x : vsbR.x,
0964 hsbR.y, leftToRight ? rowHeadR.width : vsbR.width,
0965 hsbR.height);
0966 }
0967
0968 if (lowerRight != null) {
0969 lowerRight.setBounds(leftToRight ? vsbR.x : rowHeadR.x,
0970 hsbR.y, leftToRight ? vsbR.width : rowHeadR.width,
0971 hsbR.height);
0972 }
0973
0974 if (upperLeft != null) {
0975 upperLeft.setBounds(leftToRight ? rowHeadR.x : vsbR.x,
0976 colHeadR.y, leftToRight ? rowHeadR.width
0977 : vsbR.width, colHeadR.height);
0978 }
0979
0980 if (upperRight != null) {
0981 upperRight.setBounds(leftToRight ? vsbR.x : rowHeadR.x,
0982 colHeadR.y, leftToRight ? vsbR.width
0983 : rowHeadR.width, colHeadR.height);
0984 }
0985 }
0986
0987 /**
0988 * Adjusts the <code>Rectangle</code> <code>available</code> based on if
0989 * the vertical scrollbar is needed (<code>wantsVSB</code>).
0990 * The location of the vsb is updated in <code>vsbR</code>, and
0991 * the viewport border insets (<code>vpbInsets</code>) are used to offset
0992 * the vsb. This is only called when <code>wantsVSB</code> has
0993 * changed, eg you shouldn't invoke adjustForVSB(true) twice.
0994 */
0995 private void adjustForVSB(boolean wantsVSB, Rectangle available,
0996 Rectangle vsbR, Insets vpbInsets, boolean leftToRight) {
0997 int oldWidth = vsbR.width;
0998 if (wantsVSB) {
0999 int vsbWidth = Math.max(0, Math.min(
1000 vsb.getPreferredSize().width, available.width));
1001
1002 available.width -= vsbWidth;
1003 vsbR.width = vsbWidth;
1004
1005 if (leftToRight) {
1006 vsbR.x = available.x + available.width
1007 + vpbInsets.right;
1008 } else {
1009 vsbR.x = available.x - vpbInsets.left;
1010 available.x += vsbWidth;
1011 }
1012 } else {
1013 available.width += oldWidth;
1014 }
1015 }
1016
1017 /**
1018 * Adjusts the <code>Rectangle</code> <code>available</code> based on if
1019 * the horizontal scrollbar is needed (<code>wantsHSB</code>).
1020 * The location of the hsb is updated in <code>hsbR</code>, and
1021 * the viewport border insets (<code>vpbInsets</code>) are used to offset
1022 * the hsb. This is only called when <code>wantsHSB</code> has
1023 * changed, eg you shouldn't invoked adjustForHSB(true) twice.
1024 */
1025 private void adjustForHSB(boolean wantsHSB, Rectangle available,
1026 Rectangle hsbR, Insets vpbInsets) {
1027 int oldHeight = hsbR.height;
1028 if (wantsHSB) {
1029 int hsbHeight = Math.max(0, Math.min(available.height, hsb
1030 .getPreferredSize().height));
1031
1032 available.height -= hsbHeight;
1033 hsbR.y = available.y + available.height + vpbInsets.bottom;
1034 hsbR.height = hsbHeight;
1035 } else {
1036 available.height += oldHeight;
1037 }
1038 }
1039
1040 /**
1041 * Returns the bounds of the border around the specified scroll pane's
1042 * viewport.
1043 *
1044 * @return the size and position of the viewport border
1045 * @deprecated As of JDK version Swing1.1
1046 * replaced by <code>JScrollPane.getViewportBorderBounds()</code>.
1047 */
1048 @Deprecated
1049 public Rectangle getViewportBorderBounds(JScrollPane scrollpane) {
1050 return scrollpane.getViewportBorderBounds();
1051 }
1052
1053 /**
1054 * The UI resource version of <code>ScrollPaneLayout</code>.
1055 */
1056 public static class UIResource extends ScrollPaneLayout implements
1057 javax.swing.plaf.UIResource {
1058 }
1059 }
|