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