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