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™ 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 }