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 }