0001: /*
0002: * @(#)${NAME}.java
0003: *
0004: * Copyright 2002 - 2005 JIDE Software Inc. All rights reserved.
0005: */
0006: package com.jidesoft.swing;
0007:
0008: import javax.swing.*;
0009: import javax.swing.border.Border;
0010: import java.awt.*;
0011:
0012: /**
0013: * The layout manager used by <code>JideScrollPane</code>.
0014: * <code>JideScrollPaneLayout</code> is
0015: * responsible for eleven components: a viewport, two scrollbars,
0016: * a row header, a column header, a row footer, a column footer, and four "corner" components.
0017: */
0018: class JideScrollPaneLayout extends ScrollPaneLayout implements
0019: JideScrollPaneConstants {
0020: /**
0021: * The row footer child. Default is <code>null</code>.
0022: *
0023: * @see JideScrollPane#setRowFooter
0024: */
0025: protected JViewport _rowFoot;
0026:
0027: /**
0028: * The column footer child. Default is <code>null</code>.
0029: *
0030: * @see JideScrollPane#setColumnFooter
0031: */
0032: protected JViewport _colFoot;
0033:
0034: /**
0035: * The component to the left of horizontal scroll bar.
0036: */
0037: protected Component _hLeft;
0038: /**
0039: * The component to the right of horizontal scroll bar.
0040: */
0041: protected Component _hRight;
0042:
0043: /**
0044: * The component to the top of vertical scroll bar.
0045: */
0046: protected Component _vTop;
0047:
0048: /**
0049: * The component to the bottom of vertical scroll bar.
0050: */
0051: protected Component _vBottom;
0052:
0053: @Override
0054: public void syncWithScrollPane(JScrollPane sp) {
0055: super .syncWithScrollPane(sp);
0056: if (sp instanceof JideScrollPane) {
0057: _rowFoot = ((JideScrollPane) sp).getRowFooter();
0058: _colFoot = ((JideScrollPane) sp).getColumnFooter();
0059: _hLeft = ((JideScrollPane) sp)
0060: .getScrollBarCorner(HORIZONTAL_LEFT);
0061: _hRight = ((JideScrollPane) sp)
0062: .getScrollBarCorner(HORIZONTAL_RIGHT);
0063: _vTop = ((JideScrollPane) sp)
0064: .getScrollBarCorner(VERTICAL_TOP);
0065: _vBottom = ((JideScrollPane) sp)
0066: .getScrollBarCorner(VERTICAL_BOTTOM);
0067: }
0068: }
0069:
0070: protected boolean isHsbCoversWholeWidth(JScrollPane sp) {
0071: if (sp instanceof JideScrollPane) {
0072: return ((JideScrollPane) sp)
0073: .isHorizontalScrollBarCoversWholeWidth();
0074: } else {
0075: return false;
0076: }
0077: }
0078:
0079: protected boolean isVsbCoversWholeHeight(JScrollPane sp) {
0080: if (sp instanceof JideScrollPane) {
0081: return ((JideScrollPane) sp)
0082: .isVerticalScrollBarCoversWholeHeight();
0083: } else {
0084: return false;
0085: }
0086: }
0087:
0088: @Override
0089: public void addLayoutComponent(String s, Component c) {
0090: if (s.equals(ROW_FOOTER)) {
0091: _rowFoot = (JViewport) addSingletonComponent(_rowFoot, c);
0092: } else if (s.equals(COLUMN_FOOTER)) {
0093: _colFoot = (JViewport) addSingletonComponent(_colFoot, c);
0094: } else if (s.equals(HORIZONTAL_LEFT)) {
0095: _hLeft = addSingletonComponent(_hLeft, c);
0096: } else if (s.equals(HORIZONTAL_RIGHT)) {
0097: _hRight = addSingletonComponent(_hRight, c);
0098: } else if (s.equals(VERTICAL_TOP)) {
0099: _vTop = addSingletonComponent(_vTop, c);
0100: } else if (s.equals(VERTICAL_BOTTOM)) {
0101: _vBottom = addSingletonComponent(_vBottom, c);
0102: } else {
0103: super .addLayoutComponent(s, c);
0104: }
0105: }
0106:
0107: @Override
0108: public void removeLayoutComponent(Component c) {
0109: if (c == _rowFoot) {
0110: _rowFoot = null;
0111: } else if (c == _colFoot) {
0112: _colFoot = null;
0113: } else if (c == _hLeft) {
0114: _hLeft = null;
0115: } else if (c == _hRight) {
0116: _hRight = null;
0117: } else if (c == _vTop) {
0118: _vTop = null;
0119: } else if (c == _vBottom) {
0120: _vBottom = null;
0121: } else {
0122: super .removeLayoutComponent(c);
0123: }
0124: }
0125:
0126: /**
0127: * Returns the <code>JViewport</code> object that is the row footer.
0128: *
0129: * @return the <code>JViewport</code> object that is the row footer
0130: * @see JideScrollPane#getRowFooter
0131: */
0132: public JViewport getRowFooter() {
0133: return _rowFoot;
0134: }
0135:
0136: /**
0137: * Returns the <code>JViewport</code> object that is the column footer.
0138: *
0139: * @return the <code>JViewport</code> object that is the column footer
0140: * @see JideScrollPane#getColumnFooter
0141: */
0142: public JViewport getColumnFooter() {
0143: return _colFoot;
0144: }
0145:
0146: /**
0147: * Returns the <code>Component</code> at the specified corner.
0148: *
0149: * @param key the <code>String</code> specifying the corner
0150: * @return the <code>Component</code> at the specified corner, as defined in
0151: * {@link ScrollPaneConstants}; if <code>key</code> is not one of the
0152: * four corners, <code>null</code> is returned
0153: * @see JScrollPane#getCorner
0154: */
0155: public Component getScrollBarCorner(String key) {
0156: if (key.equals(HORIZONTAL_LEFT)) {
0157: return _hLeft;
0158: } else if (key.equals(HORIZONTAL_RIGHT)) {
0159: return _hRight;
0160: } else if (key.equals(VERTICAL_BOTTOM)) {
0161: return _vBottom;
0162: } else if (key.equals(VERTICAL_TOP)) {
0163: return _vTop;
0164: } else {
0165: return super .getCorner(key);
0166: }
0167: }
0168:
0169: /**
0170: * The preferred size of a <code>ScrollPane</code> is the size of the insets,
0171: * plus the preferred size of the viewport, plus the preferred size of
0172: * the visible headers, plus the preferred size of the scrollbars
0173: * that will appear given the current view and the current
0174: * scrollbar displayPolicies.
0175: * <p>Note that the rowHeader is calculated as part of the preferred width
0176: * and the colHeader is calculated as part of the preferred size.
0177: *
0178: * @param parent the <code>Container</code> that will be laid out
0179: * @return a <code>Dimension</code> object specifying the preferred size of the
0180: * viewport and any scrollbars
0181: * @see ViewportLayout
0182: * @see LayoutManager
0183: */
0184: @Override
0185: public Dimension preferredLayoutSize(Container parent) {
0186: /* Sync the (now obsolete) policy fields with the
0187: * JScrollPane.
0188: */
0189: JScrollPane scrollPane = (JScrollPane) parent;
0190: vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
0191: hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
0192:
0193: Insets insets = parent.getInsets();
0194: int prefWidth = insets.left + insets.right;
0195: int prefHeight = insets.top + insets.bottom;
0196:
0197: /* Note that viewport.getViewSize() is equivalent to
0198: * viewport.getView().getPreferredSize() modulo a null
0199: * view or a view whose size was explicitly set.
0200: */
0201:
0202: Dimension extentSize = null;
0203: Dimension viewSize = null;
0204: Component view = null;
0205:
0206: if (viewport != null) {
0207: extentSize = viewport.getPreferredSize();
0208: viewSize = viewport.getViewSize();
0209: view = viewport.getView();
0210: }
0211:
0212: /* If there's a viewport add its preferredSize.
0213: */
0214:
0215: if (extentSize != null) {
0216: prefWidth += extentSize.width;
0217: prefHeight += extentSize.height;
0218: }
0219:
0220: /* If there's a JScrollPane.viewportBorder, add its insets.
0221: */
0222:
0223: Border viewportBorder = scrollPane.getViewportBorder();
0224: if (viewportBorder != null) {
0225: Insets vpbInsets = viewportBorder.getBorderInsets(parent);
0226: prefWidth += vpbInsets.left + vpbInsets.right;
0227: prefHeight += vpbInsets.top + vpbInsets.bottom;
0228: }
0229:
0230: /* If a header exists and it's visible, factor its
0231: * preferred size in.
0232: */
0233:
0234: int rowHeaderWidth = 0;
0235: if (rowHead != null && rowHead.isVisible()) {
0236: rowHeaderWidth = rowHead.getPreferredSize().width;
0237: }
0238: if (upperLeft != null && upperLeft.isVisible()) {
0239: rowHeaderWidth = Math.max(rowHeaderWidth, upperLeft
0240: .getPreferredSize().width);
0241: }
0242: if (lowerLeft != null && lowerLeft.isVisible()) {
0243: rowHeaderWidth = Math.max(rowHeaderWidth, lowerLeft
0244: .getPreferredSize().width);
0245: }
0246: prefWidth += rowHeaderWidth;
0247:
0248: int upperHeight = getUpperHeight();
0249:
0250: prefHeight += upperHeight;
0251:
0252: if ((_rowFoot != null) && _rowFoot.isVisible()) {
0253: prefWidth += _rowFoot.getPreferredSize().width;
0254: }
0255:
0256: int lowerHeight = getLowerHeight();
0257: prefHeight += lowerHeight;
0258:
0259: /* If a scrollbar is going to appear, factor its preferred size in.
0260: * If the scrollbars policy is AS_NEEDED, this can be a little
0261: * tricky:
0262: *
0263: * - If the view is a Scrollable then scrollableTracksViewportWidth
0264: * and scrollableTracksViewportHeight can be used to effectively
0265: * disable scrolling (if they're true) in their respective dimensions.
0266: *
0267: * - Assuming that a scrollbar hasn't been disabled by the
0268: * previous constraint, we need to decide if the scrollbar is going
0269: * to appear to correctly compute the JScrollPanes preferred size.
0270: * To do this we compare the preferredSize of the viewport (the
0271: * extentSize) to the preferredSize of the view. Although we're
0272: * not responsible for laying out the view we'll assume that the
0273: * JViewport will always give it its preferredSize.
0274: */
0275:
0276: if ((vsb != null) && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
0277: if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
0278: prefWidth += vsb.getPreferredSize().width;
0279: } else if ((viewSize != null) && (extentSize != null)) {
0280: boolean canScroll = true;
0281: if (view instanceof Scrollable) {
0282: canScroll = !((Scrollable) view)
0283: .getScrollableTracksViewportHeight();
0284: }
0285: if (canScroll && (viewSize.height > extentSize.height)) {
0286: prefWidth += vsb.getPreferredSize().width;
0287: }
0288: }
0289: }
0290:
0291: if ((hsb != null) && (hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER)) {
0292: if (hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) {
0293: prefHeight += hsb.getPreferredSize().height;
0294: } else if ((viewSize != null) && (extentSize != null)) {
0295: boolean canScroll = true;
0296: if (view instanceof Scrollable) {
0297: canScroll = !((Scrollable) view)
0298: .getScrollableTracksViewportWidth();
0299: }
0300: if (canScroll && (viewSize.width > extentSize.width)) {
0301: prefHeight += hsb.getPreferredSize().height;
0302: }
0303: }
0304: }
0305:
0306: return new Dimension(prefWidth, prefHeight);
0307: }
0308:
0309: private int getUpperHeight() {
0310: int upperHeight = 0;
0311:
0312: if ((upperLeft != null) && upperLeft.isVisible()) {
0313: upperHeight = upperLeft.getPreferredSize().height;
0314: }
0315: if ((upperRight != null) && upperRight.isVisible()) {
0316: upperHeight = Math.max(
0317: upperRight.getPreferredSize().height, upperHeight);
0318: }
0319:
0320: if ((colHead != null) && colHead.isVisible()) {
0321: upperHeight = Math.max(colHead.getPreferredSize().height,
0322: upperHeight);
0323: }
0324: return upperHeight;
0325: }
0326:
0327: private int getLowerHeight() {
0328: int lowerHeight = 0;
0329:
0330: if ((lowerLeft != null) && lowerLeft.isVisible()) {
0331: lowerHeight = lowerLeft.getPreferredSize().height;
0332: }
0333: if ((lowerRight != null) && lowerRight.isVisible()) {
0334: lowerHeight = Math.max(
0335: lowerRight.getPreferredSize().height, lowerHeight);
0336: }
0337: if ((_colFoot != null) && _colFoot.isVisible()) {
0338: lowerHeight = Math.max(_colFoot.getPreferredSize().height,
0339: lowerHeight);
0340: }
0341: return lowerHeight;
0342: }
0343:
0344: /**
0345: * The minimum size of a <code>ScrollPane</code> is the size of the insets
0346: * plus minimum size of the viewport, plus the scrollpane's
0347: * viewportBorder insets, plus the minimum size
0348: * of the visible headers, plus the minimum size of the
0349: * scrollbars whose displayPolicy isn't NEVER.
0350: *
0351: * @param parent the <code>Container</code> that will be laid out
0352: * @return a <code>Dimension</code> object specifying the minimum size
0353: */
0354: @Override
0355: public Dimension minimumLayoutSize(Container parent) {
0356: /* Sync the (now obsolete) policy fields with the
0357: * JScrollPane.
0358: */
0359: JScrollPane scrollPane = (JScrollPane) parent;
0360: vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
0361: hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
0362:
0363: Insets insets = parent.getInsets();
0364: int minWidth = insets.left + insets.right;
0365: int minHeight = insets.top + insets.bottom;
0366:
0367: /* If there's a viewport add its minimumSize.
0368: */
0369:
0370: if (viewport != null) {
0371: Dimension size = viewport.getMinimumSize();
0372: minWidth += size.width;
0373: minHeight += size.height;
0374: }
0375:
0376: /* If there's a JScrollPane.viewportBorder, add its insets.
0377: */
0378:
0379: Border viewportBorder = scrollPane.getViewportBorder();
0380: if (viewportBorder != null) {
0381: Insets vpbInsets = viewportBorder.getBorderInsets(parent);
0382: minWidth += vpbInsets.left + vpbInsets.right;
0383: minHeight += vpbInsets.top + vpbInsets.bottom;
0384: }
0385:
0386: /* If a header exists and it's visible, factor its
0387: * minimum size in.
0388: */
0389:
0390: int rowHeaderWidth = 0;
0391: if (rowHead != null && rowHead.isVisible()) {
0392: Dimension size = rowHead.getMinimumSize();
0393: rowHeaderWidth = size.width;
0394: minHeight = Math.max(minHeight, size.height);
0395: }
0396: if (upperLeft != null && upperLeft.isVisible()) {
0397: rowHeaderWidth = Math.max(rowHeaderWidth, upperLeft
0398: .getMinimumSize().width);
0399: }
0400: if (lowerLeft != null && lowerLeft.isVisible()) {
0401: rowHeaderWidth = Math.max(rowHeaderWidth, lowerLeft
0402: .getMinimumSize().width);
0403: }
0404: minWidth += rowHeaderWidth;
0405:
0406: int upperHeight = 0;
0407:
0408: if ((upperLeft != null) && upperLeft.isVisible()) {
0409: upperHeight = upperLeft.getMinimumSize().height;
0410: }
0411: if ((upperRight != null) && upperRight.isVisible()) {
0412: upperHeight = Math.max(upperRight.getMinimumSize().height,
0413: upperHeight);
0414: }
0415:
0416: if ((colHead != null) && colHead.isVisible()) {
0417: Dimension size = colHead.getMinimumSize();
0418: minWidth = Math.max(minWidth, size.width);
0419: upperHeight = Math.max(size.height, upperHeight);
0420: }
0421:
0422: minHeight += upperHeight;
0423:
0424: // JIDE: added for JideScrollPaneLayout
0425: int lowerHeight = 0;
0426:
0427: if ((lowerLeft != null) && lowerLeft.isVisible()) {
0428: lowerHeight = lowerLeft.getMinimumSize().height;
0429: }
0430: if ((lowerRight != null) && lowerRight.isVisible()) {
0431: lowerHeight = Math.max(lowerRight.getMinimumSize().height,
0432: lowerHeight);
0433: }
0434:
0435: if ((_colFoot != null) && _colFoot.isVisible()) {
0436: Dimension size = _colFoot.getMinimumSize();
0437: minWidth = Math.max(minWidth, size.width);
0438: lowerHeight = Math.max(size.height, lowerHeight);
0439: }
0440:
0441: minHeight += lowerHeight;
0442:
0443: if ((_rowFoot != null) && _rowFoot.isVisible()) {
0444: Dimension size = _rowFoot.getMinimumSize();
0445: minWidth = Math.max(minWidth, size.width);
0446: minHeight += size.height;
0447: }
0448: // JIDE: End of added for JideScrollPaneLayout
0449:
0450: /* If a scrollbar might appear, factor its minimum
0451: * size in.
0452: */
0453:
0454: if ((vsb != null) && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
0455: Dimension size = vsb.getMinimumSize();
0456: minWidth += size.width;
0457: minHeight = Math.max(minHeight, size.height);
0458: }
0459:
0460: if ((hsb != null) && (hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER)) {
0461: Dimension size = hsb.getMinimumSize();
0462: minWidth = Math.max(minWidth, size.width);
0463: minHeight += size.height;
0464: }
0465:
0466: return new Dimension(minWidth, minHeight);
0467: }
0468:
0469: /**
0470: * Lays out the scrollpane. The positioning of components depends on
0471: * the following constraints:
0472: * <ul>
0473: * <li> The row header, if present and visible, gets its preferred
0474: * width and the viewport's height.
0475: * <p/>
0476: * <li> The column header, if present and visible, gets its preferred
0477: * height and the viewport's width.
0478: * <p/>
0479: * <li> If a vertical scrollbar is needed, i.e. if the viewport's extent
0480: * height is smaller than its view height or if the <code>displayPolicy</code>
0481: * is ALWAYS, it's treated like the row header with respect to its
0482: * dimensions and is made visible.
0483: * <p/>
0484: * <li> If a horizontal scrollbar is needed, it is treated like the
0485: * column header (see the paragraph above regarding the vertical scrollbar).
0486: * <p/>
0487: * <li> If the scrollpane has a non-<code>null</code>
0488: * <code>viewportBorder</code>, then space is allocated for that.
0489: * <p/>
0490: * <li> The viewport gets the space available after accounting for
0491: * the previous constraints.
0492: * <p/>
0493: * <li> The corner components, if provided, are aligned with the
0494: * ends of the scrollbars and headers. If there is a vertical
0495: * scrollbar, the right corners appear; if there is a horizontal
0496: * scrollbar, the lower corners appear; a row header gets left
0497: * corners, and a column header gets upper corners.
0498: * </ul>
0499: *
0500: * @param parent the <code>Container</code> to lay out
0501: */
0502: @Override
0503: public void layoutContainer(Container parent) {
0504: /* Sync the (now obsolete) policy fields with the
0505: * JScrollPane.
0506: */
0507: JScrollPane scrollPane = (JScrollPane) parent;
0508: vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
0509: hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
0510:
0511: Rectangle availR = scrollPane.getBounds();
0512: availR.x = availR.y = 0;
0513:
0514: Insets insets = parent.getInsets();
0515: availR.x = insets.left;
0516: availR.y = insets.top;
0517: availR.width -= insets.left + insets.right;
0518: availR.height -= insets.top + insets.bottom;
0519:
0520: /* Get the scrollPane's orientation.
0521: */
0522: boolean leftToRight = scrollPane.getComponentOrientation()
0523: .isLeftToRight();
0524:
0525: /* If there's a visible column header remove the space it
0526: * needs from the top of availR. The column header is treated
0527: * as if it were fixed height, arbitrary width.
0528: */
0529:
0530: Rectangle colHeadR = new Rectangle(0, availR.y, 0, 0);
0531:
0532: int upperHeight = getUpperHeight();
0533:
0534: if ((colHead != null) && (colHead.isVisible())) {
0535: int colHeadHeight = Math.min(availR.height, upperHeight);
0536: colHeadR.height = colHeadHeight;
0537: availR.y += colHeadHeight;
0538: availR.height -= colHeadHeight;
0539: }
0540:
0541: /* If there's a visible row header remove the space it needs
0542: * from the left or right of availR. The row header is treated
0543: * as if it were fixed width, arbitrary height.
0544: */
0545:
0546: Rectangle rowHeadR = new Rectangle(0, 0, 0, 0);
0547:
0548: if ((rowHead != null) && (rowHead.isVisible())) {
0549: int rowHeadWidth = rowHead.getPreferredSize().width;
0550: if (upperLeft != null && upperLeft.isVisible()) {
0551: rowHeadWidth = Math.max(rowHeadWidth, upperLeft
0552: .getPreferredSize().width);
0553: }
0554: if (lowerLeft != null && lowerLeft.isVisible()) {
0555: rowHeadWidth = Math.max(rowHeadWidth, lowerLeft
0556: .getPreferredSize().width);
0557: }
0558: rowHeadWidth = Math.min(availR.width, rowHeadWidth);
0559:
0560: rowHeadR.width = rowHeadWidth;
0561: availR.width -= rowHeadWidth;
0562: if (leftToRight) {
0563: rowHeadR.x = availR.x;
0564: availR.x += rowHeadWidth;
0565: } else {
0566: rowHeadR.x = availR.x + availR.width;
0567: }
0568: }
0569:
0570: /* If there's a JScrollPane.viewportBorder, remove the
0571: * space it occupies for availR.
0572: */
0573:
0574: Border viewportBorder = scrollPane.getViewportBorder();
0575: Insets vpbInsets;
0576: if (viewportBorder != null) {
0577: vpbInsets = viewportBorder.getBorderInsets(parent);
0578: availR.x += vpbInsets.left;
0579: availR.y += vpbInsets.top;
0580: availR.width -= vpbInsets.left + vpbInsets.right;
0581: availR.height -= vpbInsets.top + vpbInsets.bottom;
0582: } else {
0583: vpbInsets = new Insets(0, 0, 0, 0);
0584: }
0585:
0586: /* If there's a visible row footer remove the space it needs
0587: * from the left or right of availR. The row footer is treated
0588: * as if it were fixed width, arbitrary height.
0589: */
0590:
0591: Rectangle rowFootR = new Rectangle(0, 0, 0, 0);
0592:
0593: if ((_rowFoot != null) && (_rowFoot.isVisible())) {
0594: int rowFootWidth = Math.min(availR.width, _rowFoot
0595: .getPreferredSize().width);
0596: rowFootR.width = rowFootWidth;
0597: availR.width -= rowFootWidth;
0598: if (leftToRight) {
0599: rowFootR.x = availR.x + availR.width;
0600: } else {
0601: rowFootR.x = availR.x;
0602: availR.x += rowFootWidth;
0603: }
0604: }
0605:
0606: /* If there's a visible column footer remove the space it
0607: * needs from the top of availR. The column footer is treated
0608: * as if it were fixed height, arbitrary width.
0609: */
0610:
0611: Rectangle colFootR = new Rectangle(0, availR.y, 0, 0);
0612:
0613: int lowerHeight = getLowerHeight();
0614:
0615: if ((_colFoot != null) && (_colFoot.isVisible())) {
0616: int colFootHeight = Math.min(availR.height, lowerHeight);
0617: colFootR.height = colFootHeight;
0618: availR.height -= colFootHeight;
0619: colFootR.y = availR.y + availR.height;
0620: }
0621:
0622: /* At this point availR is the space available for the viewport
0623: * and scrollbars. rowHeadR is correct except for its height and y
0624: * and colHeadR is correct except for its width and x. Once we're
0625: * through computing the dimensions of these three parts we can
0626: * go back and set the dimensions of rowHeadR.height, rowHeadR.y,
0627: * colHeadR.width, colHeadR.x and the bounds for the corners.
0628: *
0629: * We'll decide about putting up scrollbars by comparing the
0630: * viewport views preferred size with the viewports extent
0631: * size (generally just its size). Using the preferredSize is
0632: * reasonable because layout proceeds top down - so we expect
0633: * the viewport to be laid out next. And we assume that the
0634: * viewports layout manager will give the view it's preferred
0635: * size. One exception to this is when the view implements
0636: * Scrollable and Scrollable.getViewTracksViewport{Width,Height}
0637: * methods return true. If the view is tracking the viewports
0638: * width we don't bother with a horizontal scrollbar, similarly
0639: * if view.getViewTracksViewport(Height) is true we don't bother
0640: * with a vertical scrollbar.
0641: */
0642:
0643: Component view = (viewport != null) ? viewport.getView() : null;
0644: Dimension viewPrefSize = (view != null) ? view
0645: .getPreferredSize() : new Dimension(0, 0);
0646:
0647: Dimension extentSize = (viewport != null) ? viewport
0648: .toViewCoordinates(availR.getSize()) : new Dimension(0,
0649: 0);
0650:
0651: boolean viewTracksViewportWidth = false;
0652: boolean viewTracksViewportHeight = false;
0653: boolean isEmpty = (availR.width < 0 || availR.height < 0);
0654: Scrollable sv;
0655: // Don't bother checking the Scrollable methods if there is no room
0656: // for the viewport, we aren't going to show any scrollbars in this
0657: // case anyway.
0658: if (!isEmpty && view instanceof Scrollable) {
0659: sv = (Scrollable) view;
0660: viewTracksViewportWidth = sv
0661: .getScrollableTracksViewportWidth();
0662: viewTracksViewportHeight = sv
0663: .getScrollableTracksViewportHeight();
0664: } else {
0665: sv = null;
0666: }
0667:
0668: /* If there's a vertical scrollbar and we need one, allocate
0669: * space for it (we'll make it visible later). A vertical
0670: * scrollbar is considered to be fixed width, arbitrary height.
0671: */
0672:
0673: Rectangle vsbR = new Rectangle(0,
0674: isVsbCoversWholeHeight(scrollPane) ? -vpbInsets.top
0675: : availR.y - vpbInsets.top, 0, 0);
0676:
0677: boolean vsbNeeded;
0678: if (isEmpty) {
0679: vsbNeeded = false;
0680: } else if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
0681: vsbNeeded = true;
0682: } else if (vsbPolicy == VERTICAL_SCROLLBAR_NEVER) {
0683: vsbNeeded = false;
0684: } else { // vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED
0685: vsbNeeded = !viewTracksViewportHeight
0686: && (viewPrefSize.height > extentSize.height || (rowHead != null && rowHead
0687: .getView().getPreferredSize().height > extentSize.height));
0688: }
0689:
0690: if ((vsb != null) && vsbNeeded) {
0691: adjustForVSB(true, availR, vsbR, vpbInsets, leftToRight);
0692: extentSize = viewport.toViewCoordinates(availR.getSize());
0693: }
0694:
0695: /* If there's a horizontal scrollbar and we need one, allocate
0696: * space for it (we'll make it visible later). A horizontal
0697: * scrollbar is considered to be fixed height, arbitrary width.
0698: */
0699:
0700: Rectangle hsbR = new Rectangle(
0701: isHsbCoversWholeWidth(scrollPane) ? -vpbInsets.left
0702: : availR.x - vpbInsets.left, 0, 0, 0);
0703: boolean hsbNeeded;
0704: if (isEmpty) {
0705: hsbNeeded = false;
0706: } else if (hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) {
0707: hsbNeeded = true;
0708: } else if (hsbPolicy == HORIZONTAL_SCROLLBAR_NEVER) {
0709: hsbNeeded = false;
0710: } else { // hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED
0711: hsbNeeded = !viewTracksViewportWidth
0712: && (viewPrefSize.width > extentSize.width || (colHead != null && colHead
0713: .getView().getPreferredSize().width > extentSize.width));
0714: }
0715:
0716: if ((hsb != null) && hsbNeeded) {
0717: adjustForHSB(true, availR, hsbR, vpbInsets);
0718:
0719: /* If we added the horizontal scrollbar then we've implicitly
0720: * reduced the vertical space available to the viewport.
0721: * As a consequence we may have to add the vertical scrollbar,
0722: * if that hasn't been done so already. Of course we
0723: * don't bother with any of this if the vsbPolicy is NEVER.
0724: */
0725: if ((vsb != null) && !vsbNeeded
0726: && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
0727:
0728: extentSize = viewport.toViewCoordinates(availR
0729: .getSize());
0730: vsbNeeded = viewPrefSize.height > extentSize.height;
0731:
0732: if (vsbNeeded) {
0733: adjustForVSB(true, availR, vsbR, vpbInsets,
0734: leftToRight);
0735: }
0736: }
0737: }
0738:
0739: /* Set the size of the viewport first, and then recheck the Scrollable
0740: * methods. Some components base their return values for the Scrollable
0741: * methods on the size of the Viewport, so that if we don't
0742: * ask after resetting the bounds we may have gotten the wrong
0743: * answer.
0744: */
0745:
0746: if (viewport != null) {
0747: viewport.setBounds(availR);
0748: // viewport.setViewSize(availR.getSize()); // to fix the strange scroll bar problem reported on http://www.jidesoft.com/forum/viewtopic.php?p=20526#20526
0749:
0750: if (sv != null) {
0751: extentSize = viewport.toViewCoordinates(availR
0752: .getSize());
0753:
0754: boolean oldHSBNeeded = hsbNeeded;
0755: boolean oldVSBNeeded = vsbNeeded;
0756: viewTracksViewportWidth = sv
0757: .getScrollableTracksViewportWidth();
0758: viewTracksViewportHeight = sv
0759: .getScrollableTracksViewportHeight();
0760: if (vsb != null
0761: && vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED) {
0762: boolean newVSBNeeded = !viewTracksViewportHeight
0763: && (viewPrefSize.height > extentSize.height || (rowHead != null && rowHead
0764: .getView().getPreferredSize().height > extentSize.height));
0765: if (newVSBNeeded != vsbNeeded) {
0766: vsbNeeded = newVSBNeeded;
0767: adjustForVSB(vsbNeeded, availR, vsbR,
0768: vpbInsets, leftToRight);
0769: extentSize = viewport.toViewCoordinates(availR
0770: .getSize());
0771: }
0772: }
0773: if (hsb != null
0774: && hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED) {
0775: boolean newHSBbNeeded = !viewTracksViewportWidth
0776: && (viewPrefSize.width > extentSize.width || (colHead != null && colHead
0777: .getView().getPreferredSize().width > extentSize.width));
0778: if (newHSBbNeeded != hsbNeeded) {
0779: hsbNeeded = newHSBbNeeded;
0780: adjustForHSB(hsbNeeded, availR, hsbR, vpbInsets);
0781: if ((vsb != null)
0782: && !vsbNeeded
0783: && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
0784:
0785: extentSize = viewport
0786: .toViewCoordinates(availR.getSize());
0787: vsbNeeded = viewPrefSize.height > extentSize.height;
0788:
0789: if (vsbNeeded) {
0790: adjustForVSB(true, availR, vsbR,
0791: vpbInsets, leftToRight);
0792: }
0793: }
0794: if (_rowFoot != null && _rowFoot.isVisible()) {
0795: vsbR.x += rowFootR.width;
0796: }
0797: }
0798: }
0799: if (oldHSBNeeded != hsbNeeded
0800: || oldVSBNeeded != vsbNeeded) {
0801: viewport.setBounds(availR);
0802: // You could argue that we should recheck the
0803: // Scrollable methods again until they stop changing,
0804: // but they might never stop changing, so we stop here
0805: // and don't do any additional checks.
0806: }
0807: }
0808: }
0809:
0810: /* We now have the final size of the viewport: availR.
0811: * Now fixup the header and scrollbar widths/heights.
0812: */
0813: vsbR.height = isVsbCoversWholeHeight(scrollPane) ? scrollPane
0814: .getHeight() - 1 : availR.height + vpbInsets.top
0815: + vpbInsets.bottom;
0816: hsbR.width = isHsbCoversWholeWidth(scrollPane) ? scrollPane
0817: .getWidth()
0818: - vsbR.width : availR.width + vpbInsets.left
0819: + vpbInsets.right;
0820: rowHeadR.height = availR.height + vpbInsets.top
0821: + vpbInsets.bottom;
0822: rowHeadR.y = availR.y - vpbInsets.top;
0823: colHeadR.width = availR.width + vpbInsets.left
0824: + vpbInsets.right;
0825: colHeadR.x = availR.x - vpbInsets.left;
0826:
0827: colFootR.x = availR.x;
0828: colFootR.y = rowHeadR.y + rowHeadR.height;
0829: colFootR.width = availR.width;
0830: rowFootR.x = availR.x + availR.width;
0831: rowFootR.y = availR.y;
0832: rowFootR.height = availR.height;
0833:
0834: vsbR.x += rowFootR.width;
0835: hsbR.y += colFootR.height;
0836:
0837: /* Set the bounds of the remaining components. The scrollbars
0838: * are made invisible if they're not needed.
0839: */
0840:
0841: if (rowHead != null) {
0842: rowHead.setBounds(rowHeadR);
0843: }
0844:
0845: if (_rowFoot != null) {
0846: _rowFoot.setBounds(rowFootR);
0847: }
0848:
0849: if (colHead != null) {
0850: int height = Math.min(colHeadR.height, colHead
0851: .getPreferredSize().height);
0852: colHead
0853: .setBounds(new Rectangle(colHeadR.x, colHeadR.y
0854: + colHeadR.height - height, colHeadR.width,
0855: height));
0856: }
0857:
0858: if (_colFoot != null) {
0859: int height = Math.min(colFootR.height, _colFoot
0860: .getPreferredSize().height);
0861: _colFoot.setBounds(new Rectangle(colFootR.x, colFootR.y,
0862: colFootR.width, height));
0863: }
0864:
0865: if (vsb != null) {
0866: if (vsbNeeded) {
0867: vsb.setVisible(true);
0868: if (_vTop == null && _vBottom == null)
0869: vsb.setBounds(vsbR);
0870: else {
0871: Rectangle rect = new Rectangle(vsbR);
0872: if (_vTop != null) {
0873: Dimension dim = _vTop.getPreferredSize();
0874: rect.y += dim.height;
0875: rect.height -= dim.height;
0876: _vTop.setVisible(true);
0877: _vTop.setBounds(vsbR.x, vsbR.y, vsbR.width,
0878: dim.height);
0879: }
0880: if (_vBottom != null) {
0881: Dimension dim = _vBottom.getPreferredSize();
0882: rect.height -= dim.height;
0883: _vBottom.setVisible(true);
0884: _vBottom.setBounds(vsbR.x, vsbR.y + vsbR.height
0885: - dim.height, vsbR.width, dim.height);
0886: }
0887: vsb.setBounds(rect);
0888: }
0889: } else {
0890: if (viewPrefSize.height > extentSize.height) {
0891: vsb.setVisible(true);
0892: vsb.setBounds(vsbR.x, vsbR.y, 0, vsbR.height);
0893: } else {
0894: vsb.setVisible(false);
0895: }
0896: if (_vTop != null)
0897: _vTop.setVisible(false);
0898: if (_vBottom != null)
0899: _vBottom.setVisible(false);
0900: }
0901: }
0902:
0903: if (hsb != null) {
0904: if (hsbNeeded) {
0905: hsb.setVisible(true);
0906: if (_hLeft == null && _hRight == null)
0907: hsb.setBounds(hsbR);
0908: else {
0909: Rectangle rect = new Rectangle(hsbR);
0910: if (_hLeft != null) {
0911: Dimension dim = _hLeft.getPreferredSize();
0912: rect.x += dim.width;
0913: rect.width -= dim.width;
0914: _hLeft.setVisible(true);
0915: _hLeft.setBounds(hsbR.x, hsbR.y, dim.width,
0916: hsbR.height);
0917: _hLeft.doLayout();
0918: }
0919: if (_hRight != null) {
0920: Dimension dim = _hRight.getPreferredSize();
0921: rect.width -= dim.width;
0922: _hRight.setVisible(true);
0923: _hRight.setBounds(hsbR.x + hsbR.width
0924: - dim.width, hsbR.y, dim.width,
0925: hsbR.height);
0926: }
0927: hsb.setBounds(rect);
0928: }
0929: } else {
0930: if (viewPrefSize.width > extentSize.width) {
0931: hsb.setVisible(true);
0932: hsb.setBounds(hsbR.x, hsbR.y, hsbR.width, 0);
0933: } else {
0934: hsb.setVisible(false);
0935: }
0936: if (_hLeft != null)
0937: _hLeft.setVisible(false);
0938: if (_hRight != null)
0939: _hRight.setVisible(false);
0940: }
0941: }
0942:
0943: if (lowerLeft != null && lowerLeft.isVisible()) {
0944: int height = Math.min(lowerLeft.getPreferredSize().height,
0945: colFootR.height);
0946: lowerLeft.setBounds(leftToRight ? rowHeadR.x : vsbR.x,
0947: colFootR.y != 0 ? colFootR.y : hsbR.y,
0948: leftToRight ? rowHeadR.width : vsbR.width, height);
0949: }
0950:
0951: if (lowerRight != null && lowerRight.isVisible()) {
0952: int height = Math.min(lowerRight.getPreferredSize().height,
0953: colFootR.height);
0954: lowerRight.setBounds(leftToRight ? rowFootR.x : rowHeadR.x,
0955: colFootR.y != 0 ? colFootR.y : hsbR.y,
0956: leftToRight ? rowFootR.width
0957: + (isVsbCoversWholeHeight(scrollPane) ? 0
0958: : vsbR.width) : rowHeadR.width,
0959: height);
0960: }
0961:
0962: if (upperLeft != null && upperLeft.isVisible()) {
0963: int height = Math.min(upperLeft.getPreferredSize().height,
0964: colHeadR.height);
0965: upperLeft.setBounds(leftToRight ? rowHeadR.x : vsbR.x,
0966: colHeadR.y + colHeadR.height - height,
0967: leftToRight ? rowHeadR.width : vsbR.width, height);
0968: }
0969:
0970: if (upperRight != null && upperRight.isVisible()) {
0971: int height = Math.min(upperRight.getPreferredSize().height,
0972: colHeadR.height);
0973: upperRight.setBounds(leftToRight ? rowFootR.x : rowHeadR.x,
0974: colHeadR.y + colHeadR.height - height,
0975: leftToRight ? rowFootR.width
0976: + (isVsbCoversWholeHeight(scrollPane) ? 0
0977: : vsbR.width) : rowHeadR.width,
0978: height);
0979: }
0980: }
0981:
0982: /**
0983: * Adjusts the <code>Rectangle</code> <code>available</code> based on if
0984: * the vertical scrollbar is needed (<code>wantsVSB</code>).
0985: * The location of the vsb is updated in <code>vsbR</code>, and
0986: * the viewport border insets (<code>vpbInsets</code>) are used to offset
0987: * the vsb. This is only called when <code>wantsVSB</code> has
0988: * changed, eg you shouldn't invoke adjustForVSB(true) twice.
0989: */
0990: private void adjustForVSB(boolean wantsVSB, Rectangle available,
0991: Rectangle vsbR, Insets vpbInsets, boolean leftToRight) {
0992: int oldWidth = vsbR.width;
0993: if (wantsVSB) {
0994: int vsbWidth = Math.max(0, Math.min(
0995: vsb.getPreferredSize().width, available.width));
0996:
0997: available.width -= vsbWidth;
0998: vsbR.width = vsbWidth;
0999:
1000: if (leftToRight) {
1001: vsbR.x = available.x + available.width
1002: + vpbInsets.right;
1003: } else {
1004: vsbR.x = available.x - vpbInsets.left;
1005: available.x += vsbWidth;
1006: }
1007: } else {
1008: available.width += oldWidth;
1009: }
1010: }
1011:
1012: /**
1013: * Adjusts the <code>Rectangle</code> <code>available</code> based on if
1014: * the horizontal scrollbar is needed (<code>wantsHSB</code>).
1015: * The location of the hsb is updated in <code>hsbR</code>, and
1016: * the viewport border insets (<code>vpbInsets</code>) are used to offset
1017: * the hsb. This is only called when <code>wantsHSB</code> has
1018: * changed, eg you shouldn't invoked adjustForHSB(true) twice.
1019: */
1020: private void adjustForHSB(boolean wantsHSB, Rectangle available,
1021: Rectangle hsbR, Insets vpbInsets) {
1022: int oldHeight = hsbR.height;
1023: if (wantsHSB) {
1024: int hsbHeight = Math.max(0, Math.min(available.height, hsb
1025: .getPreferredSize().height));
1026:
1027: available.height -= hsbHeight;
1028: hsbR.y = available.y + available.height + vpbInsets.bottom;
1029: hsbR.height = hsbHeight;
1030: } else {
1031: available.height += oldHeight;
1032: }
1033: }
1034:
1035: /**
1036: * The UI resource version of <code>ScrollPaneLayout</code>.
1037: */
1038: static class UIResource extends JideScrollPaneLayout implements
1039: javax.swing.plaf.UIResource {
1040: }
1041: }
|