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