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