blob: 34aa1cae62948fa278fb31c504754f771041e2a3 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
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
26package javax.swing;
27
28
29import javax.swing.border.*;
30
31import java.awt.LayoutManager;
32import java.awt.Component;
33import java.awt.Container;
34import java.awt.Rectangle;
35import java.awt.Dimension;
36import java.awt.Insets;
37import 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 */
60public 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 viewSize = view.getPreferredSize();
495 }
496
497 /* If there's a viewport add its preferredSize.
498 */
499
500 if (extentSize != null) {
501 prefWidth += extentSize.width;
502 prefHeight += extentSize.height;
503 }
504
505 /* If there's a JScrollPane.viewportBorder, add its insets.
506 */
507
508 Border viewportBorder = scrollPane.getViewportBorder();
509 if (viewportBorder != null) {
510 Insets vpbInsets = viewportBorder.getBorderInsets(parent);
511 prefWidth += vpbInsets.left + vpbInsets.right;
512 prefHeight += vpbInsets.top + vpbInsets.bottom;
513 }
514
515 /* If a header exists and it's visible, factor its
516 * preferred size in.
517 */
518
519 if ((rowHead != null) && rowHead.isVisible()) {
520 prefWidth += rowHead.getPreferredSize().width;
521 }
522
523 if ((colHead != null) && colHead.isVisible()) {
524 prefHeight += colHead.getPreferredSize().height;
525 }
526
527 /* If a scrollbar is going to appear, factor its preferred size in.
528 * If the scrollbars policy is AS_NEEDED, this can be a little
529 * tricky:
530 *
531 * - If the view is a Scrollable then scrollableTracksViewportWidth
532 * and scrollableTracksViewportHeight can be used to effectively
533 * disable scrolling (if they're true) in their respective dimensions.
534 *
535 * - Assuming that a scrollbar hasn't been disabled by the
536 * previous constraint, we need to decide if the scrollbar is going
537 * to appear to correctly compute the JScrollPanes preferred size.
538 * To do this we compare the preferredSize of the viewport (the
539 * extentSize) to the preferredSize of the view. Although we're
540 * not responsible for laying out the view we'll assume that the
541 * JViewport will always give it its preferredSize.
542 */
543
544 if ((vsb != null) && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
545 if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
546 prefWidth += vsb.getPreferredSize().width;
547 }
548 else if ((viewSize != null) && (extentSize != null)) {
549 boolean canScroll = true;
550 if (view instanceof Scrollable) {
551 canScroll = !((Scrollable)view).getScrollableTracksViewportHeight();
552 }
553 if (canScroll && (viewSize.height > extentSize.height)) {
554 prefWidth += vsb.getPreferredSize().width;
555 }
556 }
557 }
558
559 if ((hsb != null) && (hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER)) {
560 if (hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) {
561 prefHeight += hsb.getPreferredSize().height;
562 }
563 else if ((viewSize != null) && (extentSize != null)) {
564 boolean canScroll = true;
565 if (view instanceof Scrollable) {
566 canScroll = !((Scrollable)view).getScrollableTracksViewportWidth();
567 }
568 if (canScroll && (viewSize.width > extentSize.width)) {
569 prefHeight += hsb.getPreferredSize().height;
570 }
571 }
572 }
573
574 return new Dimension(prefWidth, prefHeight);
575 }
576
577
578 /**
579 * The minimum size of a <code>ScrollPane</code> is the size of the insets
580 * plus minimum size of the viewport, plus the scrollpane's
581 * viewportBorder insets, plus the minimum size
582 * of the visible headers, plus the minimum size of the
583 * scrollbars whose displayPolicy isn't NEVER.
584 *
585 * @param parent the <code>Container</code> that will be laid out
586 * @return a <code>Dimension</code> object specifying the minimum size
587 */
588 public Dimension minimumLayoutSize(Container parent)
589 {
590 /* Sync the (now obsolete) policy fields with the
591 * JScrollPane.
592 */
593 JScrollPane scrollPane = (JScrollPane)parent;
594 vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
595 hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
596
597 Insets insets = parent.getInsets();
598 int minWidth = insets.left + insets.right;
599 int minHeight = insets.top + insets.bottom;
600
601 /* If there's a viewport add its minimumSize.
602 */
603
604 if (viewport != null) {
605 Dimension size = viewport.getMinimumSize();
606 minWidth += size.width;
607 minHeight += size.height;
608 }
609
610 /* If there's a JScrollPane.viewportBorder, add its insets.
611 */
612
613 Border viewportBorder = scrollPane.getViewportBorder();
614 if (viewportBorder != null) {
615 Insets vpbInsets = viewportBorder.getBorderInsets(parent);
616 minWidth += vpbInsets.left + vpbInsets.right;
617 minHeight += vpbInsets.top + vpbInsets.bottom;
618 }
619
620 /* If a header exists and it's visible, factor its
621 * minimum size in.
622 */
623
624 if ((rowHead != null) && rowHead.isVisible()) {
625 Dimension size = rowHead.getMinimumSize();
626 minWidth += size.width;
627 minHeight = Math.max(minHeight, size.height);
628 }
629
630 if ((colHead != null) && colHead.isVisible()) {
631 Dimension size = colHead.getMinimumSize();
632 minWidth = Math.max(minWidth, size.width);
633 minHeight += size.height;
634 }
635
636 /* If a scrollbar might appear, factor its minimum
637 * size in.
638 */
639
640 if ((vsb != null) && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
641 Dimension size = vsb.getMinimumSize();
642 minWidth += size.width;
643 minHeight = Math.max(minHeight, size.height);
644 }
645
646 if ((hsb != null) && (hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER)) {
647 Dimension size = hsb.getMinimumSize();
648 minWidth = Math.max(minWidth, size.width);
649 minHeight += size.height;
650 }
651
652 return new Dimension(minWidth, minHeight);
653 }
654
655
656 /**
657 * Lays out the scrollpane. The positioning of components depends on
658 * the following constraints:
659 * <ul>
660 * <li> The row header, if present and visible, gets its preferred
661 * width and the viewport's height.
662 *
663 * <li> The column header, if present and visible, gets its preferred
664 * height and the viewport's width.
665 *
666 * <li> If a vertical scrollbar is needed, i.e. if the viewport's extent
667 * height is smaller than its view height or if the <code>displayPolicy</code>
668 * is ALWAYS, it's treated like the row header with respect to its
669 * dimensions and is made visible.
670 *
671 * <li> If a horizontal scrollbar is needed, it is treated like the
672 * column header (see the paragraph above regarding the vertical scrollbar).
673 *
674 * <li> If the scrollpane has a non-<code>null</code>
675 * <code>viewportBorder</code>, then space is allocated for that.
676 *
677 * <li> The viewport gets the space available after accounting for
678 * the previous constraints.
679 *
680 * <li> The corner components, if provided, are aligned with the
681 * ends of the scrollbars and headers. If there is a vertical
682 * scrollbar, the right corners appear; if there is a horizontal
683 * scrollbar, the lower corners appear; a row header gets left
684 * corners, and a column header gets upper corners.
685 * </ul>
686 *
687 * @param parent the <code>Container</code> to lay out
688 */
689 public void layoutContainer(Container parent)
690 {
691 /* Sync the (now obsolete) policy fields with the
692 * JScrollPane.
693 */
694 JScrollPane scrollPane = (JScrollPane)parent;
695 vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
696 hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
697
698 Rectangle availR = scrollPane.getBounds();
699 availR.x = availR.y = 0;
700
701 Insets insets = parent.getInsets();
702 availR.x = insets.left;
703 availR.y = insets.top;
704 availR.width -= insets.left + insets.right;
705 availR.height -= insets.top + insets.bottom;
706
707 /* Get the scrollPane's orientation.
708 */
709 boolean leftToRight = SwingUtilities.isLeftToRight(scrollPane);
710
711 /* If there's a visible column header remove the space it
712 * needs from the top of availR. The column header is treated
713 * as if it were fixed height, arbitrary width.
714 */
715
716 Rectangle colHeadR = new Rectangle(0, availR.y, 0, 0);
717
718 if ((colHead != null) && (colHead.isVisible())) {
719 int colHeadHeight = Math.min(availR.height,
720 colHead.getPreferredSize().height);
721 colHeadR.height = colHeadHeight;
722 availR.y += colHeadHeight;
723 availR.height -= colHeadHeight;
724 }
725
726 /* If there's a visible row header remove the space it needs
727 * from the left or right of availR. The row header is treated
728 * as if it were fixed width, arbitrary height.
729 */
730
731 Rectangle rowHeadR = new Rectangle(0, 0, 0, 0);
732
733 if ((rowHead != null) && (rowHead.isVisible())) {
734 int rowHeadWidth = Math.min(availR.width,
735 rowHead.getPreferredSize().width);
736 rowHeadR.width = rowHeadWidth;
737 availR.width -= rowHeadWidth;
738 if ( leftToRight ) {
739 rowHeadR.x = availR.x;
740 availR.x += rowHeadWidth;
741 } else {
742 rowHeadR.x = availR.x + availR.width;
743 }
744 }
745
746 /* If there's a JScrollPane.viewportBorder, remove the
747 * space it occupies for availR.
748 */
749
750 Border viewportBorder = scrollPane.getViewportBorder();
751 Insets vpbInsets;
752 if (viewportBorder != null) {
753 vpbInsets = viewportBorder.getBorderInsets(parent);
754 availR.x += vpbInsets.left;
755 availR.y += vpbInsets.top;
756 availR.width -= vpbInsets.left + vpbInsets.right;
757 availR.height -= vpbInsets.top + vpbInsets.bottom;
758 }
759 else {
760 vpbInsets = new Insets(0,0,0,0);
761 }
762
763
764 /* At this point availR is the space available for the viewport
765 * and scrollbars. rowHeadR is correct except for its height and y
766 * and colHeadR is correct except for its width and x. Once we're
767 * through computing the dimensions of these three parts we can
768 * go back and set the dimensions of rowHeadR.height, rowHeadR.y,
769 * colHeadR.width, colHeadR.x and the bounds for the corners.
770 *
771 * We'll decide about putting up scrollbars by comparing the
772 * viewport views preferred size with the viewports extent
773 * size (generally just its size). Using the preferredSize is
774 * reasonable because layout proceeds top down - so we expect
775 * the viewport to be laid out next. And we assume that the
776 * viewports layout manager will give the view it's preferred
777 * size. One exception to this is when the view implements
778 * Scrollable and Scrollable.getViewTracksViewport{Width,Height}
779 * methods return true. If the view is tracking the viewports
780 * width we don't bother with a horizontal scrollbar, similarly
781 * if view.getViewTracksViewport(Height) is true we don't bother
782 * with a vertical scrollbar.
783 */
784
785 Component view = (viewport != null) ? viewport.getView() : null;
786 Dimension viewPrefSize =
787 (view != null) ? view.getPreferredSize()
788 : new Dimension(0,0);
789
790 Dimension extentSize =
791 (viewport != null) ? viewport.toViewCoordinates(availR.getSize())
792 : new Dimension(0,0);
793
794 boolean viewTracksViewportWidth = false;
795 boolean viewTracksViewportHeight = false;
796 boolean isEmpty = (availR.width < 0 || availR.height < 0);
797 Scrollable sv;
798 // Don't bother checking the Scrollable methods if there is no room
799 // for the viewport, we aren't going to show any scrollbars in this
800 // case anyway.
801 if (!isEmpty && view instanceof Scrollable) {
802 sv = (Scrollable)view;
803 viewTracksViewportWidth = sv.getScrollableTracksViewportWidth();
804 viewTracksViewportHeight = sv.getScrollableTracksViewportHeight();
805 }
806 else {
807 sv = null;
808 }
809
810 /* If there's a vertical scrollbar and we need one, allocate
811 * space for it (we'll make it visible later). A vertical
812 * scrollbar is considered to be fixed width, arbitrary height.
813 */
814
815 Rectangle vsbR = new Rectangle(0, availR.y - vpbInsets.top, 0, 0);
816
817 boolean vsbNeeded;
818 if (isEmpty) {
819 vsbNeeded = false;
820 }
821 else if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
822 vsbNeeded = true;
823 }
824 else if (vsbPolicy == VERTICAL_SCROLLBAR_NEVER) {
825 vsbNeeded = false;
826 }
827 else { // vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED
828 vsbNeeded = !viewTracksViewportHeight && (viewPrefSize.height > extentSize.height);
829 }
830
831
832 if ((vsb != null) && vsbNeeded) {
833 adjustForVSB(true, availR, vsbR, vpbInsets, leftToRight);
834 extentSize = viewport.toViewCoordinates(availR.getSize());
835 }
836
837 /* If there's a horizontal scrollbar and we need one, allocate
838 * space for it (we'll make it visible later). A horizontal
839 * scrollbar is considered to be fixed height, arbitrary width.
840 */
841
842 Rectangle hsbR = new Rectangle(availR.x - vpbInsets.left, 0, 0, 0);
843 boolean hsbNeeded;
844 if (isEmpty) {
845 hsbNeeded = false;
846 }
847 else if (hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) {
848 hsbNeeded = true;
849 }
850 else if (hsbPolicy == HORIZONTAL_SCROLLBAR_NEVER) {
851 hsbNeeded = false;
852 }
853 else { // hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED
854 hsbNeeded = !viewTracksViewportWidth && (viewPrefSize.width > extentSize.width);
855 }
856
857 if ((hsb != null) && hsbNeeded) {
858 adjustForHSB(true, availR, hsbR, vpbInsets);
859
860 /* If we added the horizontal scrollbar then we've implicitly
861 * reduced the vertical space available to the viewport.
862 * As a consequence we may have to add the vertical scrollbar,
863 * if that hasn't been done so already. Of course we
864 * don't bother with any of this if the vsbPolicy is NEVER.
865 */
866 if ((vsb != null) && !vsbNeeded &&
867 (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
868
869 extentSize = viewport.toViewCoordinates(availR.getSize());
870 vsbNeeded = viewPrefSize.height > extentSize.height;
871
872 if (vsbNeeded) {
873 adjustForVSB(true, availR, vsbR, vpbInsets, leftToRight);
874 }
875 }
876 }
877
878 /* Set the size of the viewport first, and then recheck the Scrollable
879 * methods. Some components base their return values for the Scrollable
880 * methods on the size of the Viewport, so that if we don't
881 * ask after resetting the bounds we may have gotten the wrong
882 * answer.
883 */
884
885 if (viewport != null) {
886 viewport.setBounds(availR);
887
888 if (sv != null) {
889 extentSize = viewport.toViewCoordinates(availR.getSize());
890
891 boolean oldHSBNeeded = hsbNeeded;
892 boolean oldVSBNeeded = vsbNeeded;
893 viewTracksViewportWidth = sv.
894 getScrollableTracksViewportWidth();
895 viewTracksViewportHeight = sv.
896 getScrollableTracksViewportHeight();
897 if (vsb != null && vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED) {
898 boolean newVSBNeeded = !viewTracksViewportHeight &&
899 (viewPrefSize.height > extentSize.height);
900 if (newVSBNeeded != vsbNeeded) {
901 vsbNeeded = newVSBNeeded;
902 adjustForVSB(vsbNeeded, availR, vsbR, vpbInsets,
903 leftToRight);
904 extentSize = viewport.toViewCoordinates
905 (availR.getSize());
906 }
907 }
908 if (hsb != null && hsbPolicy ==HORIZONTAL_SCROLLBAR_AS_NEEDED){
909 boolean newHSBbNeeded = !viewTracksViewportWidth &&
910 (viewPrefSize.width > extentSize.width);
911 if (newHSBbNeeded != hsbNeeded) {
912 hsbNeeded = newHSBbNeeded;
913 adjustForHSB(hsbNeeded, availR, hsbR, vpbInsets);
914 if ((vsb != null) && !vsbNeeded &&
915 (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
916
917 extentSize = viewport.toViewCoordinates
918 (availR.getSize());
919 vsbNeeded = viewPrefSize.height >
920 extentSize.height;
921
922 if (vsbNeeded) {
923 adjustForVSB(true, availR, vsbR, vpbInsets,
924 leftToRight);
925 }
926 }
927 }
928 }
929 if (oldHSBNeeded != hsbNeeded ||
930 oldVSBNeeded != vsbNeeded) {
931 viewport.setBounds(availR);
932 // You could argue that we should recheck the
933 // Scrollable methods again until they stop changing,
934 // but they might never stop changing, so we stop here
935 // and don't do any additional checks.
936 }
937 }
938 }
939
940 /* We now have the final size of the viewport: availR.
941 * Now fixup the header and scrollbar widths/heights.
942 */
943 vsbR.height = availR.height + vpbInsets.top + vpbInsets.bottom;
944 hsbR.width = availR.width + vpbInsets.left + vpbInsets.right;
945 rowHeadR.height = availR.height + vpbInsets.top + vpbInsets.bottom;
946 rowHeadR.y = availR.y - vpbInsets.top;
947 colHeadR.width = availR.width + vpbInsets.left + vpbInsets.right;
948 colHeadR.x = availR.x - vpbInsets.left;
949
950 /* Set the bounds of the remaining components. The scrollbars
951 * are made invisible if they're not needed.
952 */
953
954 if (rowHead != null) {
955 rowHead.setBounds(rowHeadR);
956 }
957
958 if (colHead != null) {
959 colHead.setBounds(colHeadR);
960 }
961
962 if (vsb != null) {
963 if (vsbNeeded) {
964 if (colHead != null &&
965 UIManager.getBoolean("ScrollPane.fillUpperCorner"))
966 {
967 if ((leftToRight && upperRight == null) ||
968 (!leftToRight && upperLeft == null))
969 {
970 // This is used primarily for GTK L&F, which needs to
971 // extend the vertical scrollbar to fill the upper
972 // corner near the column header. Note that we skip
973 // this step (and use the default behavior) if the
974 // user has set a custom corner component.
975 vsbR.y = colHeadR.y;
976 vsbR.height += colHeadR.height;
977 }
978 }
979 vsb.setVisible(true);
980 vsb.setBounds(vsbR);
981 }
982 else {
983 vsb.setVisible(false);
984 }
985 }
986
987 if (hsb != null) {
988 if (hsbNeeded) {
989 if (rowHead != null &&
990 UIManager.getBoolean("ScrollPane.fillLowerCorner"))
991 {
992 if ((leftToRight && lowerLeft == null) ||
993 (!leftToRight && lowerRight == null))
994 {
995 // This is used primarily for GTK L&F, which needs to
996 // extend the horizontal scrollbar to fill the lower
997 // corner near the row header. Note that we skip
998 // this step (and use the default behavior) if the
999 // user has set a custom corner component.
1000 if (leftToRight) {
1001 hsbR.x = rowHeadR.x;
1002 }
1003 hsbR.width += rowHeadR.width;
1004 }
1005 }
1006 hsb.setVisible(true);
1007 hsb.setBounds(hsbR);
1008 }
1009 else {
1010 hsb.setVisible(false);
1011 }
1012 }
1013
1014 if (lowerLeft != null) {
1015 lowerLeft.setBounds(leftToRight ? rowHeadR.x : vsbR.x,
1016 hsbR.y,
1017 leftToRight ? rowHeadR.width : vsbR.width,
1018 hsbR.height);
1019 }
1020
1021 if (lowerRight != null) {
1022 lowerRight.setBounds(leftToRight ? vsbR.x : rowHeadR.x,
1023 hsbR.y,
1024 leftToRight ? vsbR.width : rowHeadR.width,
1025 hsbR.height);
1026 }
1027
1028 if (upperLeft != null) {
1029 upperLeft.setBounds(leftToRight ? rowHeadR.x : vsbR.x,
1030 colHeadR.y,
1031 leftToRight ? rowHeadR.width : vsbR.width,
1032 colHeadR.height);
1033 }
1034
1035 if (upperRight != null) {
1036 upperRight.setBounds(leftToRight ? vsbR.x : rowHeadR.x,
1037 colHeadR.y,
1038 leftToRight ? vsbR.width : rowHeadR.width,
1039 colHeadR.height);
1040 }
1041 }
1042
1043 /**
1044 * Adjusts the <code>Rectangle</code> <code>available</code> based on if
1045 * the vertical scrollbar is needed (<code>wantsVSB</code>).
1046 * The location of the vsb is updated in <code>vsbR</code>, and
1047 * the viewport border insets (<code>vpbInsets</code>) are used to offset
1048 * the vsb. This is only called when <code>wantsVSB</code> has
1049 * changed, eg you shouldn't invoke adjustForVSB(true) twice.
1050 */
1051 private void adjustForVSB(boolean wantsVSB, Rectangle available,
1052 Rectangle vsbR, Insets vpbInsets,
1053 boolean leftToRight) {
1054 int oldWidth = vsbR.width;
1055 if (wantsVSB) {
1056 int vsbWidth = Math.max(0, Math.min(vsb.getPreferredSize().width,
1057 available.width));
1058
1059 available.width -= vsbWidth;
1060 vsbR.width = vsbWidth;
1061
1062 if( leftToRight ) {
1063 vsbR.x = available.x + available.width + vpbInsets.right;
1064 } else {
1065 vsbR.x = available.x - vpbInsets.left;
1066 available.x += vsbWidth;
1067 }
1068 }
1069 else {
1070 available.width += oldWidth;
1071 }
1072 }
1073
1074 /**
1075 * Adjusts the <code>Rectangle</code> <code>available</code> based on if
1076 * the horizontal scrollbar is needed (<code>wantsHSB</code>).
1077 * The location of the hsb is updated in <code>hsbR</code>, and
1078 * the viewport border insets (<code>vpbInsets</code>) are used to offset
1079 * the hsb. This is only called when <code>wantsHSB</code> has
1080 * changed, eg you shouldn't invoked adjustForHSB(true) twice.
1081 */
1082 private void adjustForHSB(boolean wantsHSB, Rectangle available,
1083 Rectangle hsbR, Insets vpbInsets) {
1084 int oldHeight = hsbR.height;
1085 if (wantsHSB) {
1086 int hsbHeight = Math.max(0, Math.min(available.height,
1087 hsb.getPreferredSize().height));
1088
1089 available.height -= hsbHeight;
1090 hsbR.y = available.y + available.height + vpbInsets.bottom;
1091 hsbR.height = hsbHeight;
1092 }
1093 else {
1094 available.height += oldHeight;
1095 }
1096 }
1097
1098
1099
1100 /**
1101 * Returns the bounds of the border around the specified scroll pane's
1102 * viewport.
1103 *
1104 * @return the size and position of the viewport border
1105 * @deprecated As of JDK version Swing1.1
1106 * replaced by <code>JScrollPane.getViewportBorderBounds()</code>.
1107 */
1108 @Deprecated
1109 public Rectangle getViewportBorderBounds(JScrollPane scrollpane) {
1110 return scrollpane.getViewportBorderBounds();
1111 }
1112
1113 /**
1114 * The UI resource version of <code>ScrollPaneLayout</code>.
1115 */
1116 public static class UIResource extends ScrollPaneLayout implements javax.swing.plaf.UIResource {}
1117}