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