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