1. /*
  2. * @(#)BasicSplitPaneDivider.java 1.46 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.plaf.basic;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import javax.swing.*;
  11. import javax.swing.event.*;
  12. import javax.swing.plaf.*;
  13. import javax.swing.border.Border;
  14. import java.beans.*;
  15. import java.io.*;
  16. /**
  17. * Divider used by BasicSplitPaneUI. Subclassers may wish to override
  18. * paint to do something more interesting.
  19. * The border effect is drawn in BasicSplitPaneUI, so if you don't like
  20. * that border, reset it there.
  21. * To conditionally drag from certain areas subclass mousePressed and
  22. * call super when you wish the dragging to begin.
  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
  27. * appropriate for short term storage or RMI between applications running
  28. * the same version of Swing. As of 1.4, support for long term storage
  29. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  30. * has been added to the <code>java.beans</code> package.
  31. * Please see {@link java.beans.XMLEncoder}.
  32. *
  33. * @version 1.46 01/23/03
  34. * @author Scott Violet
  35. */
  36. public class BasicSplitPaneDivider extends Container
  37. implements PropertyChangeListener
  38. {
  39. /**
  40. * Width or height of the divider based on orientation
  41. * BasicSplitPaneUI adds two to this.
  42. */
  43. protected static final int ONE_TOUCH_SIZE = 6;
  44. protected static final int ONE_TOUCH_OFFSET = 2;
  45. /**
  46. * Handles mouse dragging message to do the actual dragging.
  47. */
  48. protected DragController dragger;
  49. /**
  50. * UI this instance was created from.
  51. */
  52. protected BasicSplitPaneUI splitPaneUI;
  53. /**
  54. * Size of the divider.
  55. */
  56. protected int dividerSize = 0; // default - SET TO 0???
  57. /**
  58. * Divider that is used for noncontinuous layout mode.
  59. */
  60. protected Component hiddenDivider;
  61. /**
  62. * JSplitPane the receiver is contained in.
  63. */
  64. protected JSplitPane splitPane;
  65. /**
  66. * Handles mouse events from both this class, and the split pane.
  67. * Mouse events are handled for the splitpane since you want to be able
  68. * to drag when clicking on the border of the divider, which is not
  69. * drawn by the divider.
  70. */
  71. protected MouseHandler mouseHandler;
  72. /**
  73. * Orientation of the JSplitPane.
  74. */
  75. protected int orientation;
  76. /**
  77. * Button for quickly toggling the left component.
  78. */
  79. protected JButton leftButton;
  80. /**
  81. * Button for quickly toggling the right component.
  82. */
  83. protected JButton rightButton;
  84. /**
  85. * Cursor used for HORIZONTAL_SPLIT splitpanes.
  86. */
  87. static final Cursor horizontalCursor =
  88. Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
  89. /**
  90. * Cursor used for VERTICAL_SPLIT splitpanes.
  91. */
  92. static final Cursor verticalCursor =
  93. Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR);
  94. /**
  95. * Default cursor.
  96. */
  97. static final Cursor defaultCursor =
  98. Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
  99. /** Border. */
  100. private Border border;
  101. /**
  102. * Creates an instance of BasicSplitPaneDivider. Registers this
  103. * instance for mouse events and mouse dragged events.
  104. */
  105. public BasicSplitPaneDivider(BasicSplitPaneUI ui) {
  106. setLayout(new DividerLayout());
  107. setBasicSplitPaneUI(ui);
  108. orientation = splitPane.getOrientation();
  109. setCursor((orientation == JSplitPane.HORIZONTAL_SPLIT) ?
  110. horizontalCursor : verticalCursor);
  111. setBackground(UIManager.getColor("SplitPane.background"));
  112. }
  113. /**
  114. * Sets the SplitPaneUI that is using the receiver.
  115. */
  116. public void setBasicSplitPaneUI(BasicSplitPaneUI newUI) {
  117. if (splitPane != null) {
  118. splitPane.removePropertyChangeListener(this);
  119. if (mouseHandler != null) {
  120. splitPane.removeMouseListener(mouseHandler);
  121. splitPane.removeMouseMotionListener(mouseHandler);
  122. removeMouseListener(mouseHandler);
  123. removeMouseMotionListener(mouseHandler);
  124. mouseHandler = null;
  125. }
  126. }
  127. splitPaneUI = newUI;
  128. if (newUI != null) {
  129. splitPane = newUI.getSplitPane();
  130. if (splitPane != null) {
  131. if (mouseHandler == null) mouseHandler = new MouseHandler();
  132. splitPane.addMouseListener(mouseHandler);
  133. splitPane.addMouseMotionListener(mouseHandler);
  134. addMouseListener(mouseHandler);
  135. addMouseMotionListener(mouseHandler);
  136. splitPane.addPropertyChangeListener(this);
  137. if (splitPane.isOneTouchExpandable()) {
  138. oneTouchExpandableChanged();
  139. }
  140. }
  141. }
  142. else {
  143. splitPane = null;
  144. }
  145. }
  146. /**
  147. * Returns the <code>SplitPaneUI</code> the receiver is currently
  148. * in.
  149. */
  150. public BasicSplitPaneUI getBasicSplitPaneUI() {
  151. return splitPaneUI;
  152. }
  153. /**
  154. * Sets the size of the divider to <code>newSize</code>. That is
  155. * the width if the splitpane is <code>HORIZONTAL_SPLIT</code>, or
  156. * the height of <code>VERTICAL_SPLIT</code>.
  157. */
  158. public void setDividerSize(int newSize) {
  159. dividerSize = newSize;
  160. }
  161. /**
  162. * Returns the size of the divider, that is the width if the splitpane
  163. * is HORIZONTAL_SPLIT, or the height of VERTICAL_SPLIT.
  164. */
  165. public int getDividerSize() {
  166. return dividerSize;
  167. }
  168. /**
  169. * Sets the border of this component.
  170. * @since 1.3
  171. */
  172. public void setBorder(Border border) {
  173. Border oldBorder = this.border;
  174. this.border = border;
  175. }
  176. /**
  177. * Returns the border of this component or null if no border is
  178. * currently set.
  179. *
  180. * @return the border object for this component
  181. * @see #setBorder
  182. * @since 1.3
  183. */
  184. public Border getBorder() {
  185. return border;
  186. }
  187. /**
  188. * If a border has been set on this component, returns the
  189. * border's insets, else calls super.getInsets.
  190. *
  191. * @return the value of the insets property.
  192. * @see #setBorder
  193. */
  194. public Insets getInsets() {
  195. Border border = getBorder();
  196. if (border != null) {
  197. return border.getBorderInsets(this);
  198. }
  199. return super.getInsets();
  200. }
  201. /**
  202. * Returns dividerSize x dividerSize
  203. */
  204. public Dimension getPreferredSize() {
  205. // Ideally this would return the size from the layout manager,
  206. // but that could result in the layed out size being different from
  207. // the dividerSize, which may break developers as well as
  208. // BasicSplitPaneUI.
  209. if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
  210. return new Dimension(getDividerSize(), 1);
  211. }
  212. return new Dimension(1, getDividerSize());
  213. }
  214. /**
  215. * Returns dividerSize x dividerSize
  216. */
  217. public Dimension getMinimumSize() {
  218. return getPreferredSize();
  219. }
  220. /**
  221. * Property change event, presumably from the JSplitPane, will message
  222. * updateOrientation if necessary.
  223. */
  224. public void propertyChange(PropertyChangeEvent e) {
  225. if (e.getSource() == splitPane) {
  226. if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY)) {
  227. orientation = splitPane.getOrientation();
  228. setCursor((orientation == JSplitPane.HORIZONTAL_SPLIT) ?
  229. horizontalCursor : verticalCursor);
  230. invalidate();
  231. validate();
  232. }
  233. else if (e.getPropertyName().equals(JSplitPane.
  234. ONE_TOUCH_EXPANDABLE_PROPERTY)) {
  235. oneTouchExpandableChanged();
  236. }
  237. }
  238. }
  239. /**
  240. * Paints the divider.
  241. */
  242. public void paint(Graphics g) {
  243. super.paint(g);
  244. // Paint the border.
  245. Border border = getBorder();
  246. if (border != null) {
  247. Dimension size = getSize();
  248. border.paintBorder(this, g, 0, 0, size.width, size.height);
  249. }
  250. }
  251. /**
  252. * Messaged when the oneTouchExpandable value of the JSplitPane the
  253. * receiver is contained in changes. Will create the
  254. * <code>leftButton</code> and <code>rightButton</code> if they
  255. * are null. invalidates the receiver as well.
  256. */
  257. protected void oneTouchExpandableChanged() {
  258. if (splitPane.isOneTouchExpandable() &&
  259. leftButton == null &&
  260. rightButton == null) {
  261. /* Create the left button and add an action listener to
  262. expand/collapse it. */
  263. leftButton = createLeftOneTouchButton();
  264. if (leftButton != null)
  265. leftButton.addActionListener(new OneTouchActionHandler(true));
  266. /* Create the right button and add an action listener to
  267. expand/collapse it. */
  268. rightButton = createRightOneTouchButton();
  269. if (rightButton != null)
  270. rightButton.addActionListener(new OneTouchActionHandler
  271. (false));
  272. if (leftButton != null && rightButton != null) {
  273. add(leftButton);
  274. add(rightButton);
  275. }
  276. }
  277. invalidate();
  278. validate();
  279. }
  280. /**
  281. * Creates and return an instance of JButton that can be used to
  282. * collapse the left component in the split pane.
  283. */
  284. protected JButton createLeftOneTouchButton() {
  285. JButton b = new JButton() {
  286. public void setBorder(Border b) {
  287. }
  288. public void paint(Graphics g) {
  289. if (splitPane != null) {
  290. int[] xs = new int[3];
  291. int[] ys = new int[3];
  292. int blockSize;
  293. // Fill the background first ...
  294. g.setColor(this.getBackground());
  295. g.fillRect(0, 0, this.getWidth(),
  296. this.getHeight());
  297. // ... then draw the arrow.
  298. g.setColor(Color.black);
  299. if (orientation == JSplitPane.VERTICAL_SPLIT) {
  300. blockSize = Math.min(getHeight(), ONE_TOUCH_SIZE);
  301. xs[0] = blockSize;
  302. xs[1] = 0;
  303. xs[2] = blockSize << 1;
  304. ys[0] = 0;
  305. ys[1] = ys[2] = blockSize;
  306. g.drawPolygon(xs, ys, 3); // Little trick to make the
  307. // arrows of equal size
  308. }
  309. else {
  310. blockSize = Math.min(getWidth(), ONE_TOUCH_SIZE);
  311. xs[0] = xs[2] = blockSize;
  312. xs[1] = 0;
  313. ys[0] = 0;
  314. ys[1] = blockSize;
  315. ys[2] = blockSize << 1;
  316. }
  317. g.fillPolygon(xs, ys, 3);
  318. }
  319. }
  320. // Don't want the button to participate in focus traversable.
  321. public boolean isFocusTraversable() {
  322. return false;
  323. }
  324. };
  325. b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
  326. b.setCursor(defaultCursor);
  327. b.setFocusPainted(false);
  328. b.setBorderPainted(false);
  329. b.setRequestFocusEnabled(false);
  330. return b;
  331. }
  332. /**
  333. * Creates and return an instance of JButton that can be used to
  334. * collapse the right component in the split pane.
  335. */
  336. protected JButton createRightOneTouchButton() {
  337. JButton b = new JButton() {
  338. public void setBorder(Border border) {
  339. }
  340. public void paint(Graphics g) {
  341. if (splitPane != null) {
  342. int[] xs = new int[3];
  343. int[] ys = new int[3];
  344. int blockSize;
  345. // Fill the background first ...
  346. g.setColor(this.getBackground());
  347. g.fillRect(0, 0, this.getWidth(),
  348. this.getHeight());
  349. // ... then draw the arrow.
  350. if (orientation == JSplitPane.VERTICAL_SPLIT) {
  351. blockSize = Math.min(getHeight(), ONE_TOUCH_SIZE);
  352. xs[0] = blockSize;
  353. xs[1] = blockSize << 1;
  354. xs[2] = 0;
  355. ys[0] = blockSize;
  356. ys[1] = ys[2] = 0;
  357. }
  358. else {
  359. blockSize = Math.min(getWidth(), ONE_TOUCH_SIZE);
  360. xs[0] = xs[2] = 0;
  361. xs[1] = blockSize;
  362. ys[0] = 0;
  363. ys[1] = blockSize;
  364. ys[2] = blockSize << 1;
  365. }
  366. g.setColor(Color.black);
  367. g.fillPolygon(xs, ys, 3);
  368. }
  369. }
  370. // Don't want the button to participate in focus traversable.
  371. public boolean isFocusTraversable() {
  372. return false;
  373. }
  374. };
  375. b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
  376. b.setCursor(defaultCursor);
  377. b.setFocusPainted(false);
  378. b.setBorderPainted(false);
  379. b.setRequestFocusEnabled(false);
  380. return b;
  381. }
  382. /**
  383. * Message to prepare for dragging. This messages the BasicSplitPaneUI
  384. * with startDragging.
  385. */
  386. protected void prepareForDragging() {
  387. splitPaneUI.startDragging();
  388. }
  389. /**
  390. * Messages the BasicSplitPaneUI with dragDividerTo that this instance
  391. * is contained in.
  392. */
  393. protected void dragDividerTo(int location) {
  394. splitPaneUI.dragDividerTo(location);
  395. }
  396. /**
  397. * Messages the BasicSplitPaneUI with finishDraggingTo that this instance
  398. * is contained in.
  399. */
  400. protected void finishDraggingTo(int location) {
  401. splitPaneUI.finishDraggingTo(location);
  402. }
  403. /**
  404. * MouseHandler is responsible for converting mouse events
  405. * (released, dragged...) into the appropriate DragController
  406. * methods.
  407. * <p>
  408. */
  409. protected class MouseHandler extends MouseAdapter
  410. implements MouseMotionListener
  411. {
  412. /**
  413. * Starts the dragging session by creating the appropriate instance
  414. * of DragController.
  415. */
  416. public void mousePressed(MouseEvent e) {
  417. if ((e.getSource() == BasicSplitPaneDivider.this ||
  418. e.getSource() == splitPane) &&
  419. dragger == null &&splitPane.isEnabled()) {
  420. Component newHiddenDivider = splitPaneUI.
  421. getNonContinuousLayoutDivider();
  422. if (hiddenDivider != newHiddenDivider) {
  423. if (hiddenDivider != null) {
  424. hiddenDivider.removeMouseListener(this);
  425. hiddenDivider.removeMouseMotionListener(this);
  426. }
  427. hiddenDivider = newHiddenDivider;
  428. if (hiddenDivider != null) {
  429. hiddenDivider.addMouseMotionListener(this);
  430. hiddenDivider.addMouseListener(this);
  431. }
  432. }
  433. if (splitPane.getLeftComponent() != null &&
  434. splitPane.getRightComponent() != null) {
  435. if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
  436. dragger = new DragController(e);
  437. }
  438. else {
  439. dragger = new VerticalDragController(e);
  440. }
  441. if (!dragger.isValid()) {
  442. dragger = null;
  443. }
  444. else {
  445. prepareForDragging();
  446. dragger.continueDrag(e);
  447. }
  448. }
  449. e.consume();
  450. }
  451. }
  452. /**
  453. * If dragger is not null it is messaged with completeDrag.
  454. */
  455. public void mouseReleased(MouseEvent e) {
  456. if (dragger != null) {
  457. if (e.getSource() == splitPane) {
  458. dragger.completeDrag(e.getX(), e.getY());
  459. }
  460. else if (e.getSource() == BasicSplitPaneDivider.this) {
  461. Point ourLoc = getLocation();
  462. dragger.completeDrag(e.getX() + ourLoc.x,
  463. e.getY() + ourLoc.y);
  464. }
  465. else if (e.getSource() == hiddenDivider) {
  466. Point hDividerLoc = hiddenDivider.getLocation();
  467. int ourX = e.getX() + hDividerLoc.x;
  468. int ourY = e.getY() + hDividerLoc.y;
  469. dragger.completeDrag(ourX, ourY);
  470. }
  471. dragger = null;
  472. e.consume();
  473. }
  474. }
  475. //
  476. // MouseMotionListener
  477. //
  478. /**
  479. * If dragger is not null it is messaged with continueDrag.
  480. */
  481. public void mouseDragged(MouseEvent e) {
  482. if (dragger != null) {
  483. if (e.getSource() == splitPane) {
  484. dragger.continueDrag(e.getX(), e.getY());
  485. }
  486. else if (e.getSource() == BasicSplitPaneDivider.this) {
  487. Point ourLoc = getLocation();
  488. dragger.continueDrag(e.getX() + ourLoc.x,
  489. e.getY() + ourLoc.y);
  490. }
  491. else if (e.getSource() == hiddenDivider) {
  492. Point hDividerLoc = hiddenDivider.getLocation();
  493. int ourX = e.getX() + hDividerLoc.x;
  494. int ourY = e.getY() + hDividerLoc.y;
  495. dragger.continueDrag(ourX, ourY);
  496. }
  497. e.consume();
  498. }
  499. }
  500. /**
  501. * Resets the cursor based on the orientation.
  502. */
  503. public void mouseMoved(MouseEvent e) {
  504. }
  505. }
  506. /**
  507. * Handles the events during a dragging session for a
  508. * HORIZONTAL_SPLIT oriented split pane. This continually
  509. * messages <code>dragDividerTo</code> and then when done messages
  510. * <code>finishDraggingTo</code>. When an instance is created it should be
  511. * messaged with <code>isValid</code> to insure that dragging can happen
  512. * (dragging won't be allowed if the two views can not be resized).
  513. * <p>
  514. * <strong>Warning:</strong>
  515. * Serialized objects of this class will not be compatible with
  516. * future Swing releases. The current serialization support is
  517. * appropriate for short term storage or RMI between applications running
  518. * the same version of Swing. As of 1.4, support for long term storage
  519. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  520. * has been added to the <code>java.beans</code> package.
  521. * Please see {@link java.beans.XMLEncoder}.
  522. */
  523. protected class DragController
  524. {
  525. /**
  526. * Initial location of the divider.
  527. */
  528. int initialX;
  529. /**
  530. * Maximum and minimum positions to drag to.
  531. */
  532. int maxX, minX;
  533. /**
  534. * Initial location the mouse down happened at.
  535. */
  536. int offset;
  537. protected DragController(MouseEvent e) {
  538. JSplitPane splitPane = splitPaneUI.getSplitPane();
  539. Component leftC = splitPane.getLeftComponent();
  540. Component rightC = splitPane.getRightComponent();
  541. initialX = getLocation().x;
  542. if (e.getSource() == BasicSplitPaneDivider.this) {
  543. offset = e.getX();
  544. }
  545. else { // splitPane
  546. offset = e.getX() - initialX;
  547. }
  548. if (leftC == null || rightC == null || offset < -1 ||
  549. offset >= getSize().width) {
  550. // Don't allow dragging.
  551. maxX = -1;
  552. }
  553. else {
  554. Insets insets = splitPane.getInsets();
  555. if (leftC.isVisible()) {
  556. minX = leftC.getMinimumSize().width;
  557. if (insets != null) {
  558. minX += insets.left;
  559. }
  560. }
  561. else {
  562. minX = 0;
  563. }
  564. if (rightC.isVisible()) {
  565. int right = (insets != null) ? insets.right : 0;
  566. maxX = Math.max(0, splitPane.getSize().width -
  567. (getSize().width + right) -
  568. rightC.getMinimumSize().width);
  569. }
  570. else {
  571. int right = (insets != null) ? insets.right : 0;
  572. maxX = Math.max(0, splitPane.getSize().width -
  573. (getSize().width + right));
  574. }
  575. if (maxX < minX) minX = maxX = 0;
  576. }
  577. }
  578. /**
  579. * Returns true if the dragging session is valid.
  580. */
  581. protected boolean isValid() {
  582. return (maxX > 0);
  583. }
  584. /**
  585. * Returns the new position to put the divider at based on
  586. * the passed in MouseEvent.
  587. */
  588. protected int positionForMouseEvent(MouseEvent e) {
  589. int newX = (e.getSource() == BasicSplitPaneDivider.this) ?
  590. (e.getX() + getLocation().x) : e.getX();
  591. newX = Math.min(maxX, Math.max(minX, newX - offset));
  592. return newX;
  593. }
  594. /**
  595. * Returns the x argument, since this is used for horizontal
  596. * splits.
  597. */
  598. protected int getNeededLocation(int x, int y) {
  599. int newX;
  600. newX = Math.min(maxX, Math.max(minX, x - offset));
  601. return newX;
  602. }
  603. protected void continueDrag(int newX, int newY) {
  604. dragDividerTo(getNeededLocation(newX, newY));
  605. }
  606. /**
  607. * Messages dragDividerTo with the new location for the mouse
  608. * event.
  609. */
  610. protected void continueDrag(MouseEvent e) {
  611. dragDividerTo(positionForMouseEvent(e));
  612. }
  613. protected void completeDrag(int x, int y) {
  614. finishDraggingTo(getNeededLocation(x, y));
  615. }
  616. /**
  617. * Messages finishDraggingTo with the new location for the mouse
  618. * event.
  619. */
  620. protected void completeDrag(MouseEvent e) {
  621. finishDraggingTo(positionForMouseEvent(e));
  622. }
  623. } // End of BasicSplitPaneDivider.DragController
  624. /**
  625. * Handles the events during a dragging session for a
  626. * VERTICAL_SPLIT oriented split pane. This continually
  627. * messages <code>dragDividerTo</code> and then when done messages
  628. * <code>finishDraggingTo</code>. When an instance is created it should be
  629. * messaged with <code>isValid</code> to insure that dragging can happen
  630. * (dragging won't be allowed if the two views can not be resized).
  631. */
  632. protected class VerticalDragController extends DragController
  633. {
  634. /* DragControllers ivars are now in terms of y, not x. */
  635. protected VerticalDragController(MouseEvent e) {
  636. super(e);
  637. JSplitPane splitPane = splitPaneUI.getSplitPane();
  638. Component leftC = splitPane.getLeftComponent();
  639. Component rightC = splitPane.getRightComponent();
  640. initialX = getLocation().y;
  641. if (e.getSource() == BasicSplitPaneDivider.this) {
  642. offset = e.getY();
  643. }
  644. else {
  645. offset = e.getY() - initialX;
  646. }
  647. if (leftC == null || rightC == null || offset < -1 ||
  648. offset > getSize().height) {
  649. // Don't allow dragging.
  650. maxX = -1;
  651. }
  652. else {
  653. Insets insets = splitPane.getInsets();
  654. if (leftC.isVisible()) {
  655. minX = leftC.getMinimumSize().height;
  656. if (insets != null) {
  657. minX += insets.top;
  658. }
  659. }
  660. else {
  661. minX = 0;
  662. }
  663. if (rightC.isVisible()) {
  664. int bottom = (insets != null) ? insets.bottom : 0;
  665. maxX = Math.max(0, splitPane.getSize().height -
  666. (getSize().height + bottom) -
  667. rightC.getMinimumSize().height);
  668. }
  669. else {
  670. int bottom = (insets != null) ? insets.bottom : 0;
  671. maxX = Math.max(0, splitPane.getSize().height -
  672. (getSize().height + bottom));
  673. }
  674. if (maxX < minX) minX = maxX = 0;
  675. }
  676. }
  677. /**
  678. * Returns the y argument, since this is used for vertical
  679. * splits.
  680. */
  681. protected int getNeededLocation(int x, int y) {
  682. int newY;
  683. newY = Math.min(maxX, Math.max(minX, y - offset));
  684. return newY;
  685. }
  686. /**
  687. * Returns the new position to put the divider at based on
  688. * the passed in MouseEvent.
  689. */
  690. protected int positionForMouseEvent(MouseEvent e) {
  691. int newY = (e.getSource() == BasicSplitPaneDivider.this) ?
  692. (e.getY() + getLocation().y) : e.getY();
  693. newY = Math.min(maxX, Math.max(minX, newY - offset));
  694. return newY;
  695. }
  696. } // End of BasicSplitPaneDividier.VerticalDragController
  697. /**
  698. * Used to layout a <code>BasicSplitPaneDivider</code>.
  699. * Layout for the divider
  700. * involves appropriately moving the left/right buttons around.
  701. * <p>
  702. */
  703. protected class DividerLayout implements LayoutManager
  704. {
  705. public void layoutContainer(Container c) {
  706. if (leftButton != null && rightButton != null &&
  707. c == BasicSplitPaneDivider.this) {
  708. if (splitPane.isOneTouchExpandable()) {
  709. Insets insets = getInsets();
  710. if (orientation == JSplitPane.VERTICAL_SPLIT) {
  711. int extraX = (insets != null) ? insets.left : 0;
  712. int blockSize = getHeight();
  713. if (insets != null) {
  714. blockSize -= (insets.top + insets.bottom);
  715. blockSize = Math.max(blockSize, 0);
  716. }
  717. blockSize = Math.min(blockSize, ONE_TOUCH_SIZE);
  718. int y = (c.getSize().height - blockSize) / 2;
  719. leftButton.setBounds(extraX + ONE_TOUCH_OFFSET, y,
  720. blockSize * 2, blockSize);
  721. rightButton.setBounds(extraX + ONE_TOUCH_OFFSET +
  722. ONE_TOUCH_SIZE * 2, y,
  723. blockSize * 2, blockSize);
  724. }
  725. else {
  726. int extraY = (insets != null) ? insets.top : 0;
  727. int blockSize = getWidth();
  728. if (insets != null) {
  729. blockSize -= (insets.left + insets.right);
  730. blockSize = Math.max(blockSize, 0);
  731. }
  732. blockSize = Math.min(blockSize, ONE_TOUCH_SIZE);
  733. int x = (c.getSize().width - blockSize) / 2;
  734. leftButton.setBounds(x, extraY + ONE_TOUCH_OFFSET,
  735. blockSize, blockSize * 2);
  736. rightButton.setBounds(x, extraY + ONE_TOUCH_OFFSET +
  737. ONE_TOUCH_SIZE * 2, blockSize,
  738. blockSize * 2);
  739. }
  740. }
  741. else {
  742. leftButton.setBounds(-5, -5, 1, 1);
  743. rightButton.setBounds(-5, -5, 1, 1);
  744. }
  745. }
  746. }
  747. public Dimension minimumLayoutSize(Container c) {
  748. // NOTE: This isn't really used, refer to
  749. // BasicSplitPaneDivider.getPreferredSize for the reason.
  750. // I leave it in hopes of having this used at some point.
  751. if (c != BasicSplitPaneDivider.this || splitPane == null) {
  752. return new Dimension(0,0);
  753. }
  754. Dimension buttonMinSize = null;
  755. if (splitPane.isOneTouchExpandable() && leftButton != null) {
  756. buttonMinSize = leftButton.getMinimumSize();
  757. }
  758. Insets insets = getInsets();
  759. int width = getDividerSize();
  760. int height = width;
  761. if (orientation == JSplitPane.VERTICAL_SPLIT) {
  762. if (buttonMinSize != null) {
  763. int size = buttonMinSize.height;
  764. if (insets != null) {
  765. size += insets.top + insets.bottom;
  766. }
  767. height = Math.max(height, size);
  768. }
  769. width = 1;
  770. }
  771. else {
  772. if (buttonMinSize != null) {
  773. int size = buttonMinSize.width;
  774. if (insets != null) {
  775. size += insets.left + insets.right;
  776. }
  777. width = Math.max(width, size);
  778. }
  779. height = 1;
  780. }
  781. return new Dimension(width, height);
  782. }
  783. public Dimension preferredLayoutSize(Container c) {
  784. return minimumLayoutSize(c);
  785. }
  786. public void removeLayoutComponent(Component c) {}
  787. public void addLayoutComponent(String string, Component c) {}
  788. } // End of class BasicSplitPaneDivider.DividerLayout
  789. /**
  790. * Listeners installed on the one touch expandable buttons.
  791. */
  792. private class OneTouchActionHandler implements ActionListener {
  793. /** True indicates the resize should go the minimum (top or left)
  794. * vs false which indicates the resize should go to the maximum.
  795. */
  796. private boolean toMinimum;
  797. OneTouchActionHandler(boolean toMinimum) {
  798. this.toMinimum = toMinimum;
  799. }
  800. public void actionPerformed(ActionEvent e) {
  801. Insets insets = splitPane.getInsets();
  802. int lastLoc = splitPane.getLastDividerLocation();
  803. int currentLoc = splitPaneUI.getDividerLocation(splitPane);
  804. int newLoc;
  805. // We use the location from the UI directly, as the location the
  806. // JSplitPane itself maintains is not necessarly correct.
  807. if (toMinimum) {
  808. if (orientation == JSplitPane.VERTICAL_SPLIT) {
  809. if (currentLoc >= (splitPane.getHeight() -
  810. insets.bottom - getHeight()))
  811. newLoc = lastLoc;
  812. else
  813. newLoc = insets.top;
  814. }
  815. else {
  816. if (currentLoc >= (splitPane.getWidth() -
  817. insets.right - getWidth()))
  818. newLoc = lastLoc;
  819. else
  820. newLoc = insets.left;
  821. }
  822. }
  823. else {
  824. if (orientation == JSplitPane.VERTICAL_SPLIT) {
  825. if (currentLoc == insets.top)
  826. newLoc = lastLoc;
  827. else
  828. newLoc = splitPane.getHeight() - getHeight() -
  829. insets.top;
  830. }
  831. else {
  832. if (currentLoc == insets.left)
  833. newLoc = lastLoc;
  834. else
  835. newLoc = splitPane.getWidth() - getWidth() -
  836. insets.left;
  837. }
  838. }
  839. if (currentLoc != newLoc) {
  840. splitPane.setDividerLocation(newLoc);
  841. // We do this in case the dividers notion of the location
  842. // differs from the real location.
  843. splitPane.setLastDividerLocation(currentLoc);
  844. }
  845. }
  846. } // End of class BasicSplitPaneDivider.LeftActionListener
  847. }