1 /* 2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing; 27 28 29 import javax.swing.border.*; 30 31 import java.awt.LayoutManager; 32 import java.awt.Component; 33 import java.awt.Container; 34 import java.awt.Rectangle; 35 import java.awt.Dimension; 36 import java.awt.Insets; 37 import java.io.Serializable; 38 39 40 /** 41 * The layout manager used by <code>JScrollPane</code>. 42 * <code>JScrollPaneLayout</code> is 43 * responsible for nine components: a viewport, two scrollbars, 44 * a row header, a column header, and four "corner" components. 45 * <p> 46 * <strong>Warning:</strong> 47 * Serialized objects of this class will not be compatible with 48 * future Swing releases. The current serialization support is 49 * appropriate for short term storage or RMI between applications running 50 * the same version of Swing. As of 1.4, support for long term storage 51 * of all JavaBeans™ 52 * has been added to the <code>java.beans</code> package. 53 * Please see {@link java.beans.XMLEncoder}. 54 * 55 * @see JScrollPane 56 * @see JViewport 57 * 58 * @author Hans Muller 59 * @since 1.2 60 */ 61 @SuppressWarnings("serial") // Same-version serialization only 62 public class ScrollPaneLayout 63 implements LayoutManager, ScrollPaneConstants, Serializable 64 { 65 66 /** 67 * The scrollpane's viewport child. 68 * Default is an empty <code>JViewport</code>. 69 * @see JScrollPane#setViewport 70 */ 71 protected JViewport viewport; 72 73 74 /** 75 * The scrollpane's vertical scrollbar child. 76 * Default is a <code>JScrollBar</code>. 77 * @see JScrollPane#setVerticalScrollBar 78 */ 79 protected JScrollBar vsb; 80 81 82 /** 83 * The scrollpane's horizontal scrollbar child. 84 * Default is a <code>JScrollBar</code>. 85 * @see JScrollPane#setHorizontalScrollBar 86 */ 87 protected JScrollBar hsb; 88 89 90 /** 91 * The row header child. Default is <code>null</code>. 92 * @see JScrollPane#setRowHeader 93 */ 94 protected JViewport rowHead; 95 96 97 /** 98 * The column header child. Default is <code>null</code>. 99 * @see JScrollPane#setColumnHeader 100 */ 101 protected JViewport colHead; 102 103 104 /** 105 * The component to display in the lower left corner. 106 * Default is <code>null</code>. 107 * @see JScrollPane#setCorner 108 */ 109 protected Component lowerLeft; 110 111 112 /** 113 * The component to display in the lower right corner. 114 * Default is <code>null</code>. 115 * @see JScrollPane#setCorner 116 */ 117 protected Component lowerRight; 118 119 120 /** 121 * The component to display in the upper left corner. 122 * Default is <code>null</code>. 123 * @see JScrollPane#setCorner 124 */ 125 protected Component upperLeft; 126 127 128 /** 129 * The component to display in the upper right corner. 130 * Default is <code>null</code>. 131 * @see JScrollPane#setCorner 132 */ 133 protected Component upperRight; 134 135 136 /** 137 * The display policy for the vertical scrollbar. 138 * The default is <code>ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED</code>. 139 * <p> 140 * This field is obsolete, please use the <code>JScrollPane</code> field instead. 141 * 142 * @see JScrollPane#setVerticalScrollBarPolicy 143 */ 144 protected int vsbPolicy = VERTICAL_SCROLLBAR_AS_NEEDED; 145 146 147 /** 148 * The display policy for the horizontal scrollbar. 149 * The default is <code>ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED</code>. 150 * <p> 151 * This field is obsolete, please use the <code>JScrollPane</code> field instead. 152 * 153 * @see JScrollPane#setHorizontalScrollBarPolicy 154 */ 155 protected int hsbPolicy = HORIZONTAL_SCROLLBAR_AS_NEEDED; 156 157 158 /** 159 * This method is invoked after the ScrollPaneLayout is set as the 160 * LayoutManager of a <code>JScrollPane</code>. 161 * It initializes all of the internal fields that 162 * are ordinarily set by <code>addLayoutComponent</code>. For example: 163 * <pre> 164 * ScrollPaneLayout mySPLayout = new ScrollPanelLayout() { 165 * public void layoutContainer(Container p) { 166 * super.layoutContainer(p); 167 * // do some extra work here ... 168 * } 169 * }; 170 * scrollpane.setLayout(mySPLayout): 171 * </pre> 172 */ 173 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 }