1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing;
  27 
  28 
  29 import javax.swing.border.*;
  30 
  31 import java.awt.LayoutManager;
  32 import java.awt.Component;
  33 import java.awt.Container;
  34 import java.awt.Rectangle;
  35 import java.awt.Dimension;
  36 import java.awt.Insets;
  37 import java.io.Serializable;
  38 
  39 
  40 /**
  41  * The layout manager used by <code>JScrollPane</code>.
  42  * <code>JScrollPaneLayout</code> is
  43  * responsible for nine components: a viewport, two scrollbars,
  44  * a row header, a column header, and four "corner" components.
  45  * <p>
  46  * <strong>Warning:</strong>
  47  * Serialized objects of this class will not be compatible with
  48  * future Swing releases. The current serialization support is
  49  * appropriate for short term storage or RMI between applications running
  50  * the same version of Swing.  As of 1.4, support for long term storage
  51  * of all JavaBeans
  52  * has been added to the <code>java.beans</code> package.
  53  * Please see {@link java.beans.XMLEncoder}.
  54  *
  55  * @see JScrollPane
  56  * @see JViewport
  57  *
  58  * @author Hans Muller
  59  * @since 1.2
  60  */
  61 @SuppressWarnings("serial") // Same-version serialization only
  62 public class ScrollPaneLayout
  63     implements LayoutManager, ScrollPaneConstants, Serializable
  64 {
  65 
  66     /**
  67      * The scrollpane's viewport child.
  68      * Default is an empty <code>JViewport</code>.
  69      * @see JScrollPane#setViewport
  70      */
  71     protected JViewport viewport;
  72 
  73 
  74     /**
  75      * The scrollpane's vertical scrollbar child.
  76      * Default is a <code>JScrollBar</code>.
  77      * @see JScrollPane#setVerticalScrollBar
  78      */
  79     protected JScrollBar vsb;
  80 
  81 
  82     /**
  83      * The scrollpane's horizontal scrollbar child.
  84      * Default is a <code>JScrollBar</code>.
  85      * @see JScrollPane#setHorizontalScrollBar
  86      */
  87     protected JScrollBar hsb;
  88 
  89 
  90     /**
  91      * The row header child.  Default is <code>null</code>.
  92      * @see JScrollPane#setRowHeader
  93      */
  94     protected JViewport rowHead;
  95 
  96 
  97     /**
  98      * The column header child.  Default is <code>null</code>.
  99      * @see JScrollPane#setColumnHeader
 100      */
 101     protected JViewport colHead;
 102 
 103 
 104     /**
 105      * The component to display in the lower left corner.
 106      * Default is <code>null</code>.
 107      * @see JScrollPane#setCorner
 108      */
 109     protected Component lowerLeft;
 110 
 111 
 112     /**
 113      * The component to display in the lower right corner.
 114      * Default is <code>null</code>.
 115      * @see JScrollPane#setCorner
 116      */
 117     protected Component lowerRight;
 118 
 119 
 120     /**
 121      * The component to display in the upper left corner.
 122      * Default is <code>null</code>.
 123      * @see JScrollPane#setCorner
 124      */
 125     protected Component upperLeft;
 126 
 127 
 128     /**
 129      * The component to display in the upper right corner.
 130      * Default is <code>null</code>.
 131      * @see JScrollPane#setCorner
 132      */
 133     protected Component upperRight;
 134 
 135 
 136     /**
 137      * The display policy for the vertical scrollbar.
 138      * The default is <code>ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED</code>.
 139      * <p>
 140      * This field is obsolete, please use the <code>JScrollPane</code> field instead.
 141      *
 142      * @see JScrollPane#setVerticalScrollBarPolicy
 143      */
 144     protected int vsbPolicy = VERTICAL_SCROLLBAR_AS_NEEDED;
 145 
 146 
 147     /**
 148      * The display policy for the horizontal scrollbar.
 149      * The default is <code>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED</code>.
 150      * <p>
 151      * This field is obsolete, please use the <code>JScrollPane</code> field instead.
 152      *
 153      * @see JScrollPane#setHorizontalScrollBarPolicy
 154      */
 155     protected int hsbPolicy = HORIZONTAL_SCROLLBAR_AS_NEEDED;
 156 
 157 
 158     /**
 159      * This method is invoked after the ScrollPaneLayout is set as the
 160      * LayoutManager of a <code>JScrollPane</code>.
 161      * It initializes all of the internal fields that
 162      * are ordinarily set by <code>addLayoutComponent</code>.  For example:
 163      * <pre>
 164      * ScrollPaneLayout mySPLayout = new ScrollPanelLayout() {
 165      *     public void layoutContainer(Container p) {
 166      *         super.layoutContainer(p);
 167      *         // do some extra work here ...
 168      *     }
 169      * };
 170      * scrollpane.setLayout(mySPLayout):
 171      * </pre>
 172      *
 173      * @param sp an instance of the {@code JScrollPane}
 174      */
 175     public void syncWithScrollPane(JScrollPane sp) {
 176         viewport = sp.getViewport();
 177         vsb = sp.getVerticalScrollBar();
 178         hsb = sp.getHorizontalScrollBar();
 179         rowHead = sp.getRowHeader();
 180         colHead = sp.getColumnHeader();
 181         lowerLeft = sp.getCorner(LOWER_LEFT_CORNER);
 182         lowerRight = sp.getCorner(LOWER_RIGHT_CORNER);
 183         upperLeft = sp.getCorner(UPPER_LEFT_CORNER);
 184         upperRight = sp.getCorner(UPPER_RIGHT_CORNER);
 185         vsbPolicy = sp.getVerticalScrollBarPolicy();
 186         hsbPolicy = sp.getHorizontalScrollBarPolicy();
 187     }
 188 
 189 
 190     /**
 191      * Removes an existing component.  When a new component, such as
 192      * the left corner, or vertical scrollbar, is added, the old one,
 193      * if it exists, must be removed.
 194      * <p>
 195      * This method returns <code>newC</code>. If <code>oldC</code> is
 196      * not equal to <code>newC</code> and is non-<code>null</code>,
 197      * it will be removed from its parent.
 198      *
 199      * @param oldC the <code>Component</code> to replace
 200      * @param newC the <code>Component</code> to add
 201      * @return the <code>newC</code>
 202      */
 203     protected Component addSingletonComponent(Component oldC, Component newC)
 204     {
 205         if ((oldC != null) && (oldC != newC)) {
 206             oldC.getParent().remove(oldC);
 207         }
 208         return newC;
 209     }
 210 
 211 
 212     /**
 213      * Adds the specified component to the layout. The layout is
 214      * identified using one of:
 215      * <ul>
 216      * <li>ScrollPaneConstants.VIEWPORT
 217      * <li>ScrollPaneConstants.VERTICAL_SCROLLBAR
 218      * <li>ScrollPaneConstants.HORIZONTAL_SCROLLBAR
 219      * <li>ScrollPaneConstants.ROW_HEADER
 220      * <li>ScrollPaneConstants.COLUMN_HEADER
 221      * <li>ScrollPaneConstants.LOWER_LEFT_CORNER
 222      * <li>ScrollPaneConstants.LOWER_RIGHT_CORNER
 223      * <li>ScrollPaneConstants.UPPER_LEFT_CORNER
 224      * <li>ScrollPaneConstants.UPPER_RIGHT_CORNER
 225      * </ul>
 226      *
 227      * @param s the component identifier
 228      * @param c the component to be added
 229      * @exception IllegalArgumentException if <code>s</code> is an invalid key
 230      */
 231     public void addLayoutComponent(String s, Component c)
 232     {
 233         if (s.equals(VIEWPORT)) {
 234             viewport = (JViewport)addSingletonComponent(viewport, c);
 235         }
 236         else if (s.equals(VERTICAL_SCROLLBAR)) {
 237             vsb = (JScrollBar)addSingletonComponent(vsb, c);
 238         }
 239         else if (s.equals(HORIZONTAL_SCROLLBAR)) {
 240             hsb = (JScrollBar)addSingletonComponent(hsb, c);
 241         }
 242         else if (s.equals(ROW_HEADER)) {
 243             rowHead = (JViewport)addSingletonComponent(rowHead, c);
 244         }
 245         else if (s.equals(COLUMN_HEADER)) {
 246             colHead = (JViewport)addSingletonComponent(colHead, c);
 247         }
 248         else if (s.equals(LOWER_LEFT_CORNER)) {
 249             lowerLeft = addSingletonComponent(lowerLeft, c);
 250         }
 251         else if (s.equals(LOWER_RIGHT_CORNER)) {
 252             lowerRight = addSingletonComponent(lowerRight, c);
 253         }
 254         else if (s.equals(UPPER_LEFT_CORNER)) {
 255             upperLeft = addSingletonComponent(upperLeft, c);
 256         }
 257         else if (s.equals(UPPER_RIGHT_CORNER)) {
 258             upperRight = addSingletonComponent(upperRight, c);
 259         }
 260         else {
 261             throw new IllegalArgumentException("invalid layout key " + s);
 262         }
 263     }
 264 
 265 
 266     /**
 267      * Removes the specified component from the layout.
 268      *
 269      * @param c the component to remove
 270      */
 271     public void removeLayoutComponent(Component c)
 272     {
 273         if (c == viewport) {
 274             viewport = null;
 275         }
 276         else if (c == vsb) {
 277             vsb = null;
 278         }
 279         else if (c == hsb) {
 280             hsb = null;
 281         }
 282         else if (c == rowHead) {
 283             rowHead = null;
 284         }
 285         else if (c == colHead) {
 286             colHead = null;
 287         }
 288         else if (c == lowerLeft) {
 289             lowerLeft = null;
 290         }
 291         else if (c == lowerRight) {
 292             lowerRight = null;
 293         }
 294         else if (c == upperLeft) {
 295             upperLeft = null;
 296         }
 297         else if (c == upperRight) {
 298             upperRight = null;
 299         }
 300     }
 301 
 302 
 303     /**
 304      * Returns the vertical scrollbar-display policy.
 305      *
 306      * @return an integer giving the display policy
 307      * @see #setVerticalScrollBarPolicy
 308      */
 309     public int getVerticalScrollBarPolicy() {
 310         return vsbPolicy;
 311     }
 312 
 313 
 314     /**
 315      * Sets the vertical scrollbar-display policy. The options
 316      * are:
 317      * <ul>
 318      * <li>ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED
 319      * <li>ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER
 320      * <li>ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS
 321      * </ul>
 322      * Note: Applications should use the <code>JScrollPane</code> version
 323      * of this method.  It only exists for backwards compatibility
 324      * with the Swing 1.0.2 (and earlier) versions of this class.
 325      *
 326      * @param x an integer giving the display policy
 327      * @exception IllegalArgumentException if <code>x</code> is an invalid
 328      *          vertical scroll bar policy, as listed above
 329      */
 330     public void setVerticalScrollBarPolicy(int x) {
 331         switch (x) {
 332         case VERTICAL_SCROLLBAR_AS_NEEDED:
 333         case VERTICAL_SCROLLBAR_NEVER:
 334         case VERTICAL_SCROLLBAR_ALWAYS:
 335                 vsbPolicy = x;
 336                 break;
 337         default:
 338             throw new IllegalArgumentException("invalid verticalScrollBarPolicy");
 339         }
 340     }
 341 
 342 
 343     /**
 344      * Returns the horizontal scrollbar-display policy.
 345      *
 346      * @return an integer giving the display policy
 347      * @see #setHorizontalScrollBarPolicy
 348      */
 349     public int getHorizontalScrollBarPolicy() {
 350         return hsbPolicy;
 351     }
 352 
 353     /**
 354      * Sets the horizontal scrollbar-display policy.
 355      * The options are:<ul>
 356      * <li>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED
 357      * <li>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
 358      * <li>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS
 359      * </ul>
 360      * Note: Applications should use the <code>JScrollPane</code> version
 361      * of this method.  It only exists for backwards compatibility
 362      * with the Swing 1.0.2 (and earlier) versions of this class.
 363      *
 364      * @param x an int giving the display policy
 365      * @exception IllegalArgumentException if <code>x</code> is not a valid
 366      *          horizontal scrollbar policy, as listed above
 367      */
 368     public void setHorizontalScrollBarPolicy(int x) {
 369         switch (x) {
 370         case HORIZONTAL_SCROLLBAR_AS_NEEDED:
 371         case HORIZONTAL_SCROLLBAR_NEVER:
 372         case HORIZONTAL_SCROLLBAR_ALWAYS:
 373                 hsbPolicy = x;
 374                 break;
 375         default:
 376             throw new IllegalArgumentException("invalid horizontalScrollBarPolicy");
 377         }
 378     }
 379 
 380 
 381     /**
 382      * Returns the <code>JViewport</code> object that displays the
 383      * scrollable contents.
 384      * @return the <code>JViewport</code> object that displays the scrollable contents
 385      * @see JScrollPane#getViewport
 386      */
 387     public JViewport getViewport() {
 388         return viewport;
 389     }
 390 
 391 
 392     /**
 393      * Returns the <code>JScrollBar</code> object that handles horizontal scrolling.
 394      * @return the <code>JScrollBar</code> object that handles horizontal scrolling
 395      * @see JScrollPane#getHorizontalScrollBar
 396      */
 397     public JScrollBar getHorizontalScrollBar() {
 398         return hsb;
 399     }
 400 
 401     /**
 402      * Returns the <code>JScrollBar</code> object that handles vertical scrolling.
 403      * @return the <code>JScrollBar</code> object that handles vertical scrolling
 404      * @see JScrollPane#getVerticalScrollBar
 405      */
 406     public JScrollBar getVerticalScrollBar() {
 407         return vsb;
 408     }
 409 
 410 
 411     /**
 412      * Returns the <code>JViewport</code> object that is the row header.
 413      * @return the <code>JViewport</code> object that is the row header
 414      * @see JScrollPane#getRowHeader
 415      */
 416     public JViewport getRowHeader() {
 417         return rowHead;
 418     }
 419 
 420 
 421     /**
 422      * Returns the <code>JViewport</code> object that is the column header.
 423      * @return the <code>JViewport</code> object that is the column header
 424      * @see JScrollPane#getColumnHeader
 425      */
 426     public JViewport getColumnHeader() {
 427         return colHead;
 428     }
 429 
 430 
 431     /**
 432      * Returns the <code>Component</code> at the specified corner.
 433      * @param key the <code>String</code> specifying the corner
 434      * @return the <code>Component</code> at the specified corner, as defined in
 435      *         {@link ScrollPaneConstants}; if <code>key</code> is not one of the
 436      *          four corners, <code>null</code> is returned
 437      * @see JScrollPane#getCorner
 438      */
 439     public Component getCorner(String key) {
 440         if (key.equals(LOWER_LEFT_CORNER)) {
 441             return lowerLeft;
 442         }
 443         else if (key.equals(LOWER_RIGHT_CORNER)) {
 444             return lowerRight;
 445         }
 446         else if (key.equals(UPPER_LEFT_CORNER)) {
 447             return upperLeft;
 448         }
 449         else if (key.equals(UPPER_RIGHT_CORNER)) {
 450             return upperRight;
 451         }
 452         else {
 453             return null;
 454         }
 455     }
 456 
 457 
 458     /**
 459      * The preferred size of a <code>ScrollPane</code> is the size of the insets,
 460      * plus the preferred size of the viewport, plus the preferred size of
 461      * the visible headers, plus the preferred size of the scrollbars
 462      * that will appear given the current view and the current
 463      * scrollbar displayPolicies.
 464      * <p>Note that the rowHeader is calculated as part of the preferred width
 465      * and the colHeader is calculated as part of the preferred size.
 466      *
 467      * @param parent the <code>Container</code> that will be laid out
 468      * @return a <code>Dimension</code> object specifying the preferred size of the
 469      *         viewport and any scrollbars
 470      * @see ViewportLayout
 471      * @see LayoutManager
 472      */
 473     public Dimension preferredLayoutSize(Container parent)
 474     {
 475         /* Sync the (now obsolete) policy fields with the
 476          * JScrollPane.
 477          */
 478         JScrollPane scrollPane = (JScrollPane)parent;
 479         vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
 480         hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
 481 
 482         Insets insets = parent.getInsets();
 483         int prefWidth = insets.left + insets.right;
 484         int prefHeight = insets.top + insets.bottom;
 485 
 486         /* Note that viewport.getViewSize() is equivalent to
 487          * viewport.getView().getPreferredSize() modulo a null
 488          * view or a view whose size was explicitly set.
 489          */
 490 
 491         Dimension extentSize = null;
 492         Dimension viewSize = null;
 493         Component view = null;
 494 
 495         if (viewport != null) {
 496             extentSize = viewport.getPreferredSize();
 497             view = viewport.getView();
 498             if (view != null) {
 499                 viewSize = view.getPreferredSize();
 500             } else {
 501                 viewSize = new Dimension(0, 0);
 502             }
 503         }
 504 
 505         /* If there's a viewport add its preferredSize.
 506          */
 507 
 508         if (extentSize != null) {
 509             prefWidth += extentSize.width;
 510             prefHeight += extentSize.height;
 511         }
 512 
 513         /* If there's a JScrollPane.viewportBorder, add its insets.
 514          */
 515 
 516         Border viewportBorder = scrollPane.getViewportBorder();
 517         if (viewportBorder != null) {
 518             Insets vpbInsets = viewportBorder.getBorderInsets(parent);
 519             prefWidth += vpbInsets.left + vpbInsets.right;
 520             prefHeight += vpbInsets.top + vpbInsets.bottom;
 521         }
 522 
 523         /* If a header exists and it's visible, factor its
 524          * preferred size in.
 525          */
 526 
 527         if ((rowHead != null) && rowHead.isVisible()) {
 528             prefWidth += rowHead.getPreferredSize().width;
 529         }
 530 
 531         if ((colHead != null) && colHead.isVisible()) {
 532             prefHeight += colHead.getPreferredSize().height;
 533         }
 534 
 535         /* If a scrollbar is going to appear, factor its preferred size in.
 536          * If the scrollbars policy is AS_NEEDED, this can be a little
 537          * tricky:
 538          *
 539          * - If the view is a Scrollable then scrollableTracksViewportWidth
 540          * and scrollableTracksViewportHeight can be used to effectively
 541          * disable scrolling (if they're true) in their respective dimensions.
 542          *
 543          * - Assuming that a scrollbar hasn't been disabled by the
 544          * previous constraint, we need to decide if the scrollbar is going
 545          * to appear to correctly compute the JScrollPanes preferred size.
 546          * To do this we compare the preferredSize of the viewport (the
 547          * extentSize) to the preferredSize of the view.  Although we're
 548          * not responsible for laying out the view we'll assume that the
 549          * JViewport will always give it its preferredSize.
 550          */
 551 
 552         if ((vsb != null) && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
 553             if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
 554                 prefWidth += vsb.getPreferredSize().width;
 555             }
 556             else if ((viewSize != null) && (extentSize != null)) {
 557                 boolean canScroll = true;
 558                 if (view instanceof Scrollable) {
 559                     canScroll = !((Scrollable)view).getScrollableTracksViewportHeight();
 560                 }
 561                 if (canScroll && (viewSize.height > extentSize.height)) {
 562                     prefWidth += vsb.getPreferredSize().width;
 563                 }
 564             }
 565         }
 566 
 567         if ((hsb != null) && (hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER)) {
 568             if (hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) {
 569                 prefHeight += hsb.getPreferredSize().height;
 570             }
 571             else if ((viewSize != null) && (extentSize != null)) {
 572                 boolean canScroll = true;
 573                 if (view instanceof Scrollable) {
 574                     canScroll = !((Scrollable)view).getScrollableTracksViewportWidth();
 575                 }
 576                 if (canScroll && (viewSize.width > extentSize.width)) {
 577                     prefHeight += hsb.getPreferredSize().height;
 578                 }
 579             }
 580         }
 581 
 582         return new Dimension(prefWidth, prefHeight);
 583     }
 584 
 585 
 586     /**
 587      * The minimum size of a <code>ScrollPane</code> is the size of the insets
 588      * plus minimum size of the viewport, plus the scrollpane's
 589      * viewportBorder insets, plus the minimum size
 590      * of the visible headers, plus the minimum size of the
 591      * scrollbars whose displayPolicy isn't NEVER.
 592      *
 593      * @param parent the <code>Container</code> that will be laid out
 594      * @return a <code>Dimension</code> object specifying the minimum size
 595      */
 596     public Dimension minimumLayoutSize(Container parent)
 597     {
 598         /* Sync the (now obsolete) policy fields with the
 599          * JScrollPane.
 600          */
 601         JScrollPane scrollPane = (JScrollPane)parent;
 602         vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
 603         hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
 604 
 605         Insets insets = parent.getInsets();
 606         int minWidth = insets.left + insets.right;
 607         int minHeight = insets.top + insets.bottom;
 608 
 609         /* If there's a viewport add its minimumSize.
 610          */
 611 
 612         if (viewport != null) {
 613             Dimension size = viewport.getMinimumSize();
 614             minWidth += size.width;
 615             minHeight += size.height;
 616         }
 617 
 618         /* If there's a JScrollPane.viewportBorder, add its insets.
 619          */
 620 
 621         Border viewportBorder = scrollPane.getViewportBorder();
 622         if (viewportBorder != null) {
 623             Insets vpbInsets = viewportBorder.getBorderInsets(parent);
 624             minWidth += vpbInsets.left + vpbInsets.right;
 625             minHeight += vpbInsets.top + vpbInsets.bottom;
 626         }
 627 
 628         /* If a header exists and it's visible, factor its
 629          * minimum size in.
 630          */
 631 
 632         if ((rowHead != null) && rowHead.isVisible()) {
 633             Dimension size = rowHead.getMinimumSize();
 634             minWidth += size.width;
 635             minHeight = Math.max(minHeight, size.height);
 636         }
 637 
 638         if ((colHead != null) && colHead.isVisible()) {
 639             Dimension size = colHead.getMinimumSize();
 640             minWidth = Math.max(minWidth, size.width);
 641             minHeight += size.height;
 642         }
 643 
 644         /* If a scrollbar might appear, factor its minimum
 645          * size in.
 646          */
 647 
 648         if ((vsb != null) && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
 649             Dimension size = vsb.getMinimumSize();
 650             minWidth += size.width;
 651             minHeight = Math.max(minHeight, size.height);
 652         }
 653 
 654         if ((hsb != null) && (hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER)) {
 655             Dimension size = hsb.getMinimumSize();
 656             minWidth = Math.max(minWidth, size.width);
 657             minHeight += size.height;
 658         }
 659 
 660         return new Dimension(minWidth, minHeight);
 661     }
 662 
 663 
 664     /**
 665      * Lays out the scrollpane. The positioning of components depends on
 666      * the following constraints:
 667      * <ul>
 668      * <li> The row header, if present and visible, gets its preferred
 669      * width and the viewport's height.
 670      *
 671      * <li> The column header, if present and visible, gets its preferred
 672      * height and the viewport's width.
 673      *
 674      * <li> If a vertical scrollbar is needed, i.e. if the viewport's extent
 675      * height is smaller than its view height or if the <code>displayPolicy</code>
 676      * is ALWAYS, it's treated like the row header with respect to its
 677      * dimensions and is made visible.
 678      *
 679      * <li> If a horizontal scrollbar is needed, it is treated like the
 680      * column header (see the paragraph above regarding the vertical scrollbar).
 681      *
 682      * <li> If the scrollpane has a non-<code>null</code>
 683      * <code>viewportBorder</code>, then space is allocated for that.
 684      *
 685      * <li> The viewport gets the space available after accounting for
 686      * the previous constraints.
 687      *
 688      * <li> The corner components, if provided, are aligned with the
 689      * ends of the scrollbars and headers. If there is a vertical
 690      * scrollbar, the right corners appear; if there is a horizontal
 691      * scrollbar, the lower corners appear; a row header gets left
 692      * corners, and a column header gets upper corners.
 693      * </ul>
 694      *
 695      * @param parent the <code>Container</code> to lay out
 696      */
 697     public void layoutContainer(Container parent)
 698     {
 699         /* Sync the (now obsolete) policy fields with the
 700          * JScrollPane.
 701          */
 702         JScrollPane scrollPane = (JScrollPane)parent;
 703         vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
 704         hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
 705 
 706         Rectangle availR = scrollPane.getBounds();
 707         availR.x = availR.y = 0;
 708 
 709         Insets insets = parent.getInsets();
 710         availR.x = insets.left;
 711         availR.y = insets.top;
 712         availR.width -= insets.left + insets.right;
 713         availR.height -= insets.top + insets.bottom;
 714 
 715         /* Get the scrollPane's orientation.
 716          */
 717         boolean leftToRight = SwingUtilities.isLeftToRight(scrollPane);
 718 
 719         /* If there's a visible column header remove the space it
 720          * needs from the top of availR.  The column header is treated
 721          * as if it were fixed height, arbitrary width.
 722          */
 723 
 724         Rectangle colHeadR = new Rectangle(0, availR.y, 0, 0);
 725 
 726         if ((colHead != null) && (colHead.isVisible())) {
 727             int colHeadHeight = Math.min(availR.height,
 728                                          colHead.getPreferredSize().height);
 729             colHeadR.height = colHeadHeight;
 730             availR.y += colHeadHeight;
 731             availR.height -= colHeadHeight;
 732         }
 733 
 734         /* If there's a visible row header remove the space it needs
 735          * from the left or right of availR.  The row header is treated
 736          * as if it were fixed width, arbitrary height.
 737          */
 738 
 739         Rectangle rowHeadR = new Rectangle(0, 0, 0, 0);
 740 
 741         if ((rowHead != null) && (rowHead.isVisible())) {
 742             int rowHeadWidth = Math.min(availR.width,
 743                                         rowHead.getPreferredSize().width);
 744             rowHeadR.width = rowHeadWidth;
 745             availR.width -= rowHeadWidth;
 746             if ( leftToRight ) {
 747                 rowHeadR.x = availR.x;
 748                 availR.x += rowHeadWidth;
 749             } else {
 750                 rowHeadR.x = availR.x + availR.width;
 751             }
 752         }
 753 
 754         /* If there's a JScrollPane.viewportBorder, remove the
 755          * space it occupies for availR.
 756          */
 757 
 758         Border viewportBorder = scrollPane.getViewportBorder();
 759         Insets vpbInsets;
 760         if (viewportBorder != null) {
 761             vpbInsets = viewportBorder.getBorderInsets(parent);
 762             availR.x += vpbInsets.left;
 763             availR.y += vpbInsets.top;
 764             availR.width -= vpbInsets.left + vpbInsets.right;
 765             availR.height -= vpbInsets.top + vpbInsets.bottom;
 766         }
 767         else {
 768             vpbInsets = new Insets(0,0,0,0);
 769         }
 770 
 771 
 772         /* At this point availR is the space available for the viewport
 773          * and scrollbars. rowHeadR is correct except for its height and y
 774          * and colHeadR is correct except for its width and x.  Once we're
 775          * through computing the dimensions  of these three parts we can
 776          * go back and set the dimensions of rowHeadR.height, rowHeadR.y,
 777          * colHeadR.width, colHeadR.x and the bounds for the corners.
 778          *
 779          * We'll decide about putting up scrollbars by comparing the
 780          * viewport views preferred size with the viewports extent
 781          * size (generally just its size).  Using the preferredSize is
 782          * reasonable because layout proceeds top down - so we expect
 783          * the viewport to be laid out next.  And we assume that the
 784          * viewports layout manager will give the view it's preferred
 785          * size.  One exception to this is when the view implements
 786          * Scrollable and Scrollable.getViewTracksViewport{Width,Height}
 787          * methods return true.  If the view is tracking the viewports
 788          * width we don't bother with a horizontal scrollbar, similarly
 789          * if view.getViewTracksViewport(Height) is true we don't bother
 790          * with a vertical scrollbar.
 791          */
 792 
 793         Component view = (viewport != null) ? viewport.getView() : null;
 794         Dimension viewPrefSize =
 795             (view != null) ? view.getPreferredSize()
 796                            : new Dimension(0,0);
 797 
 798         Dimension extentSize =
 799             (viewport != null) ? viewport.toViewCoordinates(availR.getSize())
 800                                : new Dimension(0,0);
 801 
 802         boolean viewTracksViewportWidth = false;
 803         boolean viewTracksViewportHeight = false;
 804         boolean isEmpty = (availR.width < 0 || availR.height < 0);
 805         Scrollable sv;
 806         // Don't bother checking the Scrollable methods if there is no room
 807         // for the viewport, we aren't going to show any scrollbars in this
 808         // case anyway.
 809         if (!isEmpty && view instanceof Scrollable) {
 810             sv = (Scrollable)view;
 811             viewTracksViewportWidth = sv.getScrollableTracksViewportWidth();
 812             viewTracksViewportHeight = sv.getScrollableTracksViewportHeight();
 813         }
 814         else {
 815             sv = null;
 816         }
 817 
 818         /* If there's a vertical scrollbar and we need one, allocate
 819          * space for it (we'll make it visible later). A vertical
 820          * scrollbar is considered to be fixed width, arbitrary height.
 821          */
 822 
 823         Rectangle vsbR = new Rectangle(0, availR.y - vpbInsets.top, 0, 0);
 824 
 825         boolean vsbNeeded;
 826         if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
 827             vsbNeeded = true;
 828         }
 829         else if (vsbPolicy == VERTICAL_SCROLLBAR_NEVER) {
 830             vsbNeeded = false;
 831         }
 832         else {  // vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED
 833             vsbNeeded = !viewTracksViewportHeight && (viewPrefSize.height > extentSize.height);
 834         }
 835 
 836 
 837         if ((vsb != null) && vsbNeeded) {
 838             adjustForVSB(true, availR, vsbR, vpbInsets, leftToRight);
 839             extentSize = viewport.toViewCoordinates(availR.getSize());
 840         }
 841 
 842         /* If there's a horizontal scrollbar and we need one, allocate
 843          * space for it (we'll make it visible later). A horizontal
 844          * scrollbar is considered to be fixed height, arbitrary width.
 845          */
 846 
 847         Rectangle hsbR = new Rectangle(availR.x - vpbInsets.left, 0, 0, 0);
 848         boolean hsbNeeded;
 849         if (hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) {
 850             hsbNeeded = true;
 851         }
 852         else if (hsbPolicy == HORIZONTAL_SCROLLBAR_NEVER) {
 853             hsbNeeded = false;
 854         }
 855         else {  // hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED
 856             hsbNeeded = !viewTracksViewportWidth && (viewPrefSize.width > extentSize.width);
 857         }
 858 
 859         if ((hsb != null) && hsbNeeded) {
 860             adjustForHSB(true, availR, hsbR, vpbInsets);
 861 
 862             /* If we added the horizontal scrollbar then we've implicitly
 863              * reduced  the vertical space available to the viewport.
 864              * As a consequence we may have to add the vertical scrollbar,
 865              * if that hasn't been done so already.  Of course we
 866              * don't bother with any of this if the vsbPolicy is NEVER.
 867              */
 868             if ((vsb != null) && !vsbNeeded &&
 869                 (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
 870 
 871                 extentSize = viewport.toViewCoordinates(availR.getSize());
 872                 vsbNeeded = viewPrefSize.height > extentSize.height;
 873 
 874                 if (vsbNeeded) {
 875                     adjustForVSB(true, availR, vsbR, vpbInsets, leftToRight);
 876                 }
 877             }
 878         }
 879 
 880         /* Set the size of the viewport first, and then recheck the Scrollable
 881          * methods. Some components base their return values for the Scrollable
 882          * methods on the size of the Viewport, so that if we don't
 883          * ask after resetting the bounds we may have gotten the wrong
 884          * answer.
 885          */
 886 
 887         if (viewport != null) {
 888             viewport.setBounds(availR);
 889 
 890             if (sv != null) {
 891                 extentSize = viewport.toViewCoordinates(availR.getSize());
 892 
 893                 boolean oldHSBNeeded = hsbNeeded;
 894                 boolean oldVSBNeeded = vsbNeeded;
 895                 viewTracksViewportWidth = sv.
 896                                           getScrollableTracksViewportWidth();
 897                 viewTracksViewportHeight = sv.
 898                                           getScrollableTracksViewportHeight();
 899                 if (vsb != null && vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED) {
 900                     boolean newVSBNeeded = !viewTracksViewportHeight &&
 901                                      (viewPrefSize.height > extentSize.height);
 902                     if (newVSBNeeded != vsbNeeded) {
 903                         vsbNeeded = newVSBNeeded;
 904                         adjustForVSB(vsbNeeded, availR, vsbR, vpbInsets,
 905                                      leftToRight);
 906                         extentSize = viewport.toViewCoordinates
 907                                               (availR.getSize());
 908                     }
 909                 }
 910                 if (hsb != null && hsbPolicy ==HORIZONTAL_SCROLLBAR_AS_NEEDED){
 911                     boolean newHSBbNeeded = !viewTracksViewportWidth &&
 912                                        (viewPrefSize.width > extentSize.width);
 913                     if (newHSBbNeeded != hsbNeeded) {
 914                         hsbNeeded = newHSBbNeeded;
 915                         adjustForHSB(hsbNeeded, availR, hsbR, vpbInsets);
 916                         if ((vsb != null) && !vsbNeeded &&
 917                             (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
 918 
 919                             extentSize = viewport.toViewCoordinates
 920                                          (availR.getSize());
 921                             vsbNeeded = viewPrefSize.height >
 922                                         extentSize.height;
 923 
 924                             if (vsbNeeded) {
 925                                 adjustForVSB(true, availR, vsbR, vpbInsets,
 926                                              leftToRight);
 927                             }
 928                         }
 929                     }
 930                 }
 931                 if (oldHSBNeeded != hsbNeeded ||
 932                     oldVSBNeeded != vsbNeeded) {
 933                     viewport.setBounds(availR);
 934                     // You could argue that we should recheck the
 935                     // Scrollable methods again until they stop changing,
 936                     // but they might never stop changing, so we stop here
 937                     // and don't do any additional checks.
 938                 }
 939             }
 940         }
 941 
 942         /* We now have the final size of the viewport: availR.
 943          * Now fixup the header and scrollbar widths/heights.
 944          */
 945         vsbR.height = availR.height + vpbInsets.top + vpbInsets.bottom;
 946         hsbR.width = availR.width + vpbInsets.left + vpbInsets.right;
 947         rowHeadR.height = availR.height + vpbInsets.top + vpbInsets.bottom;
 948         rowHeadR.y = availR.y - vpbInsets.top;
 949         colHeadR.width = availR.width + vpbInsets.left + vpbInsets.right;
 950         colHeadR.x = availR.x - vpbInsets.left;
 951 
 952         /* Set the bounds of the remaining components.  The scrollbars
 953          * are made invisible if they're not needed.
 954          */
 955 
 956         if (rowHead != null) {
 957             rowHead.setBounds(rowHeadR);
 958         }
 959 
 960         if (colHead != null) {
 961             colHead.setBounds(colHeadR);
 962         }
 963 
 964         if (vsb != null) {
 965             if (vsbNeeded) {
 966                 if (colHead != null &&
 967                     UIManager.getBoolean("ScrollPane.fillUpperCorner"))
 968                 {
 969                     if ((leftToRight && upperRight == null) ||
 970                         (!leftToRight && upperLeft == null))
 971                     {
 972                         // This is used primarily for GTK L&F, which needs to
 973                         // extend the vertical scrollbar to fill the upper
 974                         // corner near the column header.  Note that we skip
 975                         // this step (and use the default behavior) if the
 976                         // user has set a custom corner component.
 977                         vsbR.y = colHeadR.y;
 978                         vsbR.height += colHeadR.height;
 979                     }
 980                 }
 981                 vsb.setVisible(true);
 982                 vsb.setBounds(vsbR);
 983             }
 984             else {
 985                 vsb.setVisible(false);
 986             }
 987         }
 988 
 989         if (hsb != null) {
 990             if (hsbNeeded) {
 991                 if (rowHead != null &&
 992                     UIManager.getBoolean("ScrollPane.fillLowerCorner"))
 993                 {
 994                     if ((leftToRight && lowerLeft == null) ||
 995                         (!leftToRight && lowerRight == null))
 996                     {
 997                         // This is used primarily for GTK L&F, which needs to
 998                         // extend the horizontal scrollbar to fill the lower
 999                         // corner near the row header.  Note that we skip
1000                         // this step (and use the default behavior) if the
1001                         // user has set a custom corner component.
1002                         if (leftToRight) {
1003                             hsbR.x = rowHeadR.x;
1004                         }
1005                         hsbR.width += rowHeadR.width;
1006                     }
1007                 }
1008                 hsb.setVisible(true);
1009                 hsb.setBounds(hsbR);
1010             }
1011             else {
1012                 hsb.setVisible(false);
1013             }
1014         }
1015 
1016         if (lowerLeft != null) {
1017             lowerLeft.setBounds(leftToRight ? rowHeadR.x : vsbR.x,
1018                                 hsbR.y,
1019                                 leftToRight ? rowHeadR.width : vsbR.width,
1020                                 hsbR.height);
1021         }
1022 
1023         if (lowerRight != null) {
1024             lowerRight.setBounds(leftToRight ? vsbR.x : rowHeadR.x,
1025                                  hsbR.y,
1026                                  leftToRight ? vsbR.width : rowHeadR.width,
1027                                  hsbR.height);
1028         }
1029 
1030         if (upperLeft != null) {
1031             upperLeft.setBounds(leftToRight ? rowHeadR.x : vsbR.x,
1032                                 colHeadR.y,
1033                                 leftToRight ? rowHeadR.width : vsbR.width,
1034                                 colHeadR.height);
1035         }
1036 
1037         if (upperRight != null) {
1038             upperRight.setBounds(leftToRight ? vsbR.x : rowHeadR.x,
1039                                  colHeadR.y,
1040                                  leftToRight ? vsbR.width : rowHeadR.width,
1041                                  colHeadR.height);
1042         }
1043     }
1044 
1045     /**
1046      * Adjusts the <code>Rectangle</code> <code>available</code> based on if
1047      * the vertical scrollbar is needed (<code>wantsVSB</code>).
1048      * The location of the vsb is updated in <code>vsbR</code>, and
1049      * the viewport border insets (<code>vpbInsets</code>) are used to offset
1050      * the vsb. This is only called when <code>wantsVSB</code> has
1051      * changed, eg you shouldn't invoke adjustForVSB(true) twice.
1052      */
1053     private void adjustForVSB(boolean wantsVSB, Rectangle available,
1054                               Rectangle vsbR, Insets vpbInsets,
1055                               boolean leftToRight) {
1056         int oldWidth = vsbR.width;
1057         if (wantsVSB) {
1058             int vsbWidth = Math.max(0, Math.min(vsb.getPreferredSize().width,
1059                                                 available.width));
1060 
1061             available.width -= vsbWidth;
1062             vsbR.width = vsbWidth;
1063 
1064             if( leftToRight ) {
1065                 vsbR.x = available.x + available.width + vpbInsets.right;
1066             } else {
1067                 vsbR.x = available.x - vpbInsets.left;
1068                 available.x += vsbWidth;
1069             }
1070         }
1071         else {
1072             available.width += oldWidth;
1073         }
1074     }
1075 
1076     /**
1077      * Adjusts the <code>Rectangle</code> <code>available</code> based on if
1078      * the horizontal scrollbar is needed (<code>wantsHSB</code>).
1079      * The location of the hsb is updated in <code>hsbR</code>, and
1080      * the viewport border insets (<code>vpbInsets</code>) are used to offset
1081      * the hsb.  This is only called when <code>wantsHSB</code> has
1082      * changed, eg you shouldn't invoked adjustForHSB(true) twice.
1083      */
1084     private void adjustForHSB(boolean wantsHSB, Rectangle available,
1085                               Rectangle hsbR, Insets vpbInsets) {
1086         int oldHeight = hsbR.height;
1087         if (wantsHSB) {
1088             int hsbHeight = Math.max(0, Math.min(available.height,
1089                                               hsb.getPreferredSize().height));
1090 
1091             available.height -= hsbHeight;
1092             hsbR.y = available.y + available.height + vpbInsets.bottom;
1093             hsbR.height = hsbHeight;
1094         }
1095         else {
1096             available.height += oldHeight;
1097         }
1098     }
1099 
1100 
1101 
1102     /**
1103      * Returns the bounds of the border around the specified scroll pane's
1104      * viewport.
1105      *
1106      * @param scrollpane an instance of the {@code JScrollPane}
1107      * @return the size and position of the viewport border
1108      * @deprecated As of JDK version Swing1.1
1109      *    replaced by <code>JScrollPane.getViewportBorderBounds()</code>.
1110      */
1111     @Deprecated
1112     public Rectangle getViewportBorderBounds(JScrollPane scrollpane) {
1113         return scrollpane.getViewportBorderBounds();
1114     }
1115 
1116     /**
1117      * The UI resource version of <code>ScrollPaneLayout</code>.
1118      */
1119     public static class UIResource extends ScrollPaneLayout implements javax.swing.plaf.UIResource {}
1120 }