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