1 /*
2 * Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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<sup><font size="-2">TM</font></sup>
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 }