1. /*
  2. * @(#)SynthSplitPaneUI.java 1.12 03/09/04
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.java.swing.plaf.gtk;
  8. import javax.swing.*;
  9. import javax.swing.border.Border;
  10. import javax.swing.event.*;
  11. import java.awt.*;
  12. import java.awt.event.*;
  13. import java.awt.peer.ComponentPeer;
  14. import java.awt.peer.LightweightPeer;
  15. import java.beans.*;
  16. import java.util.*;
  17. import javax.swing.plaf.ActionMapUIResource;
  18. import javax.swing.plaf.SplitPaneUI;
  19. import javax.swing.plaf.ComponentUI;
  20. import javax.swing.plaf.UIResource;
  21. /**
  22. * A Basic L&F implementation of the SplitPaneUI.
  23. *
  24. * @version 1.12, 09/04/03 (based on BasicSplitPaneUI v 1.72)
  25. * @author Scott Violet
  26. * @author Steve Wilson
  27. * @author Ralph Kar
  28. */
  29. class SynthSplitPaneUI extends SplitPaneUI implements SynthUI {
  30. /**
  31. * The divider used for non-continuous layout is added to the split pane
  32. * with this object.
  33. */
  34. private static final String NON_CONTINUOUS_DIVIDER =
  35. "nonContinuousDivider";
  36. /**
  37. * How far (relativ) the divider does move when it is moved around by
  38. * the cursor keys on the keyboard.
  39. */
  40. protected static int KEYBOARD_DIVIDER_MOVE_OFFSET = 3;
  41. /**
  42. * JSplitPane instance this instance is providing
  43. * the look and feel for.
  44. */
  45. protected JSplitPane splitPane;
  46. /**
  47. * Instance of the divider for this JSplitPane.
  48. */
  49. protected Divider divider;
  50. /**
  51. * Instance of the PropertyChangeListener for this JSplitPane.
  52. */
  53. protected PropertyChangeListener propertyChangeListener;
  54. /**
  55. * Instance of the FocusListener for this JSplitPane.
  56. */
  57. protected FocusListener focusListener;
  58. /**
  59. * Keys to use for forward focus traversal when the JComponent is
  60. * managing focus.
  61. */
  62. private static Set managingFocusForwardTraversalKeys;
  63. /**
  64. * Keys to use for backward focus traversal when the JComponent is
  65. * managing focus.
  66. */
  67. private static Set managingFocusBackwardTraversalKeys;
  68. /**
  69. * The size of the divider while the dragging session is valid.
  70. */
  71. protected int dividerSize;
  72. /**
  73. * Instance for the shadow of the divider when non continuous layout
  74. * is being used.
  75. */
  76. protected Component nonContinuousLayoutDivider;
  77. /**
  78. * Set to true in startDragging if any of the children
  79. * (not including the nonContinuousLayoutDivider) are heavy weights.
  80. */
  81. protected boolean draggingHW;
  82. /**
  83. * Location of the divider when the dragging session began.
  84. */
  85. protected int beginDragDividerLocation;
  86. // Private data of the instance
  87. private int orientation;
  88. private int lastDragLocation;
  89. private boolean continuousLayout;
  90. private boolean dividerKeyboardResize;
  91. private boolean dividerLocationIsSet; // needed for tracking
  92. // the first occurrence of
  93. // setDividerLocation()
  94. private boolean rememberPaneSizes;
  95. /** Indicates that we have painted once. */
  96. // This is used by the LayoutManager to determine when it should use
  97. // the divider location provided by the JSplitPane. This is used as there
  98. // is no way to determine when the layout process has completed.
  99. boolean painted;
  100. /** If true, setDividerLocation does nothing. */
  101. boolean ignoreDividerLocationChange;
  102. /**
  103. * Style for the JSplitPane.
  104. */
  105. private SynthStyle style;
  106. /**
  107. * Style for the divider.
  108. */
  109. private SynthStyle dividerStyle;
  110. /**
  111. * Size of the one touch buttons.
  112. */
  113. private int oneTouchSize;
  114. /**
  115. * Amount to offset one touch buttons.
  116. */
  117. private int oneTouchOffset;
  118. /**
  119. * Creates a new SynthSplitPaneUI instance
  120. */
  121. public static ComponentUI createUI(JComponent x) {
  122. return new SynthSplitPaneUI();
  123. }
  124. public static void loadActionMap(ActionMap map) {
  125. // NOTE: this needs to remain static. If you have a need to
  126. // have Actions that reference the UI in the ActionMap,
  127. // then you'll also need to change the registeration of the
  128. // ActionMap.
  129. map.put("negativeIncrement", new KeyboardUpLeftAction());
  130. map.put("positiveIncrement", new KeyboardDownRightAction());
  131. map.put("selectMin", new KeyboardHomeAction());
  132. map.put("selectMax", new KeyboardEndAction());
  133. map.put("startResize", new KeyboardResizeToggleAction());
  134. map.put("toggleFocus", new ToggleSideFocusAction());
  135. map.put("focusOutForward", new MoveFocusOutAction(1));
  136. map.put("focusOutBackward", new MoveFocusOutAction(-1));
  137. }
  138. /**
  139. * Installs the UI.
  140. */
  141. public void installUI(JComponent c) {
  142. splitPane = (JSplitPane) c;
  143. dividerLocationIsSet = false;
  144. dividerKeyboardResize = false;
  145. installComponents();
  146. installDefaults();
  147. installListeners();
  148. installKeyboardActions();
  149. setLastDragLocation(-1);
  150. }
  151. protected void installComponents() {
  152. divider = new Divider(splitPane);
  153. splitPane.add(divider, JSplitPane.DIVIDER);
  154. }
  155. /**
  156. * Installs the UI defaults.
  157. */
  158. protected void installDefaults() {
  159. setOrientation(splitPane.getOrientation());
  160. setContinuousLayout(splitPane.isContinuousLayout());
  161. fetchStyle(splitPane);
  162. resetLayoutManager();
  163. /* Install the nonContinuousLayoutDivider here to avoid having to
  164. add/remove everything later. */
  165. if(nonContinuousLayoutDivider == null) {
  166. setNonContinuousLayoutDivider(
  167. createDefaultNonContinuousLayoutDivider(),
  168. true);
  169. } else {
  170. setNonContinuousLayoutDivider(nonContinuousLayoutDivider, true);
  171. }
  172. // focus forward traversal key
  173. if (managingFocusForwardTraversalKeys==null) {
  174. managingFocusForwardTraversalKeys = new TreeSet();
  175. managingFocusForwardTraversalKeys.add(
  176. KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
  177. }
  178. splitPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
  179. managingFocusForwardTraversalKeys);
  180. // focus backward traversal key
  181. if (managingFocusBackwardTraversalKeys==null) {
  182. managingFocusBackwardTraversalKeys = new TreeSet();
  183. managingFocusBackwardTraversalKeys.add(
  184. KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK));
  185. }
  186. splitPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
  187. managingFocusBackwardTraversalKeys);
  188. }
  189. private void fetchStyle(JSplitPane splitPane) {
  190. SynthContext context = getContext(splitPane, ENABLED);
  191. SynthStyle oldStyle = style;
  192. style = SynthLookAndFeel.updateStyle(context, this);
  193. if (style != oldStyle) {
  194. splitPane.setDividerSize(((Number)style.get(
  195. context,"SplitPaneDivider.size")).intValue());
  196. divider.setDividerSize(splitPane.getDividerSize());
  197. }
  198. context.dispose();
  199. context = getContext(splitPane, Region.SPLIT_PANE_DIVIDER, ENABLED);
  200. dividerStyle = SynthLookAndFeel.updateStyle(context, this);
  201. divider.setOpaque(dividerStyle.isOpaque(context));
  202. oneTouchSize = dividerStyle.getInt(context,
  203. "SplitPaneDivider.oneTouchButtonSize", 6);
  204. oneTouchOffset = dividerStyle.getInt(context,
  205. "SplitPaneDivider.oneTouchOffset", 6);
  206. context.dispose();
  207. }
  208. /**
  209. * Installs the event listeners for the UI.
  210. */
  211. protected void installListeners() {
  212. if ((propertyChangeListener = createPropertyChangeListener()) !=
  213. null) {
  214. splitPane.addPropertyChangeListener(propertyChangeListener);
  215. }
  216. if ((focusListener = createFocusListener()) != null) {
  217. splitPane.addFocusListener(focusListener);
  218. }
  219. }
  220. /**
  221. * Installs the keyboard actions for the UI.
  222. */
  223. protected void installKeyboardActions() {
  224. InputMap km = getInputMap(JComponent.
  225. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  226. SwingUtilities.replaceUIInputMap(splitPane, JComponent.
  227. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  228. km);
  229. LazyActionMap.installLazyActionMap(splitPane, SynthSplitPaneUI.class,
  230. "SplitPane.actionMap");
  231. }
  232. InputMap getInputMap(int condition) {
  233. if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
  234. SynthContext context = getContext(splitPane, ENABLED);
  235. InputMap map = (InputMap)context.getStyle().get(context,
  236. "SplitPane.ancestorInputMap");
  237. context.dispose();
  238. return map;
  239. }
  240. return null;
  241. }
  242. /**
  243. * Uninstalls the UI.
  244. */
  245. public void uninstallUI(JComponent c) {
  246. uninstallKeyboardActions();
  247. uninstallListeners();
  248. uninstallDefaults();
  249. uninstallComponents();
  250. dividerLocationIsSet = false;
  251. dividerKeyboardResize = false;
  252. splitPane = null;
  253. }
  254. private void uninstallComponents() {
  255. if(nonContinuousLayoutDivider != null) {
  256. splitPane.remove(nonContinuousLayoutDivider);
  257. }
  258. splitPane.remove(divider);
  259. divider = null;
  260. }
  261. /**
  262. * Uninstalls the UI defaults.
  263. */
  264. protected void uninstallDefaults() {
  265. if (splitPane.getLayout() instanceof UIResource) {
  266. splitPane.setLayout(null);
  267. }
  268. SynthContext context = getContext(splitPane, ENABLED);
  269. style.uninstallDefaults(context);
  270. context.dispose();
  271. style = null;
  272. context = getContext(splitPane, Region.SPLIT_PANE_DIVIDER, ENABLED);
  273. dividerStyle.uninstallDefaults(context);
  274. context.dispose();
  275. dividerStyle = null;
  276. nonContinuousLayoutDivider = null;
  277. setNonContinuousLayoutDivider(null);
  278. // sets the focus forward and backward traversal keys to null
  279. // to restore the defaults
  280. splitPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null);
  281. splitPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null);
  282. }
  283. /**
  284. * Uninstalls the event listeners for the UI.
  285. */
  286. protected void uninstallListeners() {
  287. if (propertyChangeListener != null) {
  288. splitPane.removePropertyChangeListener(propertyChangeListener);
  289. propertyChangeListener = null;
  290. }
  291. if (focusListener != null) {
  292. splitPane.removeFocusListener(focusListener);
  293. focusListener = null;
  294. }
  295. }
  296. /**
  297. * Uninstalls the keyboard actions for the UI.
  298. */
  299. protected void uninstallKeyboardActions() {
  300. SwingUtilities.replaceUIActionMap(splitPane, null);
  301. SwingUtilities.replaceUIInputMap(splitPane, JComponent.
  302. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  303. null);
  304. }
  305. public SynthContext getContext(JComponent c) {
  306. return getContext(c, getComponentState(c));
  307. }
  308. private SynthContext getContext(JComponent c, int state) {
  309. return SynthContext.getContext(SynthContext.class, c,
  310. SynthLookAndFeel.getRegion(c), style, state);
  311. }
  312. private Region getRegion(JComponent c) {
  313. return SynthLookAndFeel.getRegion(c);
  314. }
  315. private int getComponentState(JComponent c) {
  316. return SynthLookAndFeel.getComponentState(c);
  317. }
  318. private SynthContext getContext(JComponent c, Region region) {
  319. return getContext(c, region, getComponentState(c, region));
  320. }
  321. private SynthContext getContext(JComponent c, Region region, int state) {
  322. return SynthContext.getContext(SynthContext.class, c, region,
  323. style, state);
  324. }
  325. private int getComponentState(JComponent c, Region subregion) {
  326. int state = SynthLookAndFeel.getComponentState(c);
  327. if (divider.isMouseOver()) {
  328. state |= MOUSE_OVER;
  329. }
  330. return state;
  331. }
  332. /**
  333. * Creates a PropertyChangeListener for the JSplitPane UI.
  334. */
  335. protected PropertyChangeListener createPropertyChangeListener() {
  336. return new PropertyHandler();
  337. }
  338. /**
  339. * Creates a FocusListener for the JSplitPane UI.
  340. */
  341. protected FocusListener createFocusListener() {
  342. return new FocusHandler();
  343. }
  344. /**
  345. * Returns the orientation for the JSplitPane.
  346. */
  347. public int getOrientation() {
  348. return orientation;
  349. }
  350. /**
  351. * Set the orientation for the JSplitPane.
  352. */
  353. public void setOrientation(int orientation) {
  354. this.orientation = orientation;
  355. }
  356. /**
  357. * Determines wether the JSplitPane is set to use a continuous layout.
  358. */
  359. public boolean isContinuousLayout() {
  360. return continuousLayout;
  361. }
  362. /**
  363. * Turn continuous layout on/off.
  364. */
  365. public void setContinuousLayout(boolean b) {
  366. continuousLayout = b;
  367. }
  368. /**
  369. * Returns the last drag location of the JSplitPane.
  370. */
  371. public int getLastDragLocation() {
  372. return lastDragLocation;
  373. }
  374. /**
  375. * Set the last drag location of the JSplitPane.
  376. */
  377. public void setLastDragLocation(int l) {
  378. lastDragLocation = l;
  379. }
  380. /**
  381. * @return increment via keyboard methods.
  382. */
  383. int getKeyboardMoveIncrement() {
  384. return KEYBOARD_DIVIDER_MOVE_OFFSET;
  385. }
  386. /**
  387. * Implementation of the PropertyChangeListener
  388. * that the JSplitPane UI uses.
  389. */
  390. class PropertyHandler implements PropertyChangeListener
  391. {
  392. /**
  393. * Messaged from the <code>JSplitPane</code> the receiver is
  394. * contained in. May potentially reset the layout manager and cause a
  395. * <code>validate</code> to be sent.
  396. */
  397. public void propertyChange(PropertyChangeEvent e) {
  398. if(e.getSource() == splitPane) {
  399. String changeName = e.getPropertyName();
  400. if (SynthLookAndFeel.shouldUpdateStyle(e)) {
  401. fetchStyle((JSplitPane)e.getSource());
  402. }
  403. if(changeName.equals(JSplitPane.ORIENTATION_PROPERTY)) {
  404. orientation = splitPane.getOrientation();
  405. resetLayoutManager();
  406. } else if(changeName.equals(
  407. JSplitPane.CONTINUOUS_LAYOUT_PROPERTY)) {
  408. setContinuousLayout(splitPane.isContinuousLayout());
  409. if(!isContinuousLayout()) {
  410. if(nonContinuousLayoutDivider == null) {
  411. setNonContinuousLayoutDivider(
  412. createDefaultNonContinuousLayoutDivider(),
  413. true);
  414. } else if(nonContinuousLayoutDivider.getParent() ==
  415. null) {
  416. setNonContinuousLayoutDivider(
  417. nonContinuousLayoutDivider,
  418. true);
  419. }
  420. }
  421. } else if(changeName.equals(JSplitPane.DIVIDER_SIZE_PROPERTY)){
  422. divider.setDividerSize(splitPane.getDividerSize());
  423. dividerSize = divider.getDividerSize();
  424. splitPane.revalidate();
  425. splitPane.repaint();
  426. }
  427. }
  428. }
  429. }
  430. /**
  431. * Implementation of the FocusListener that the JSplitPane UI uses.
  432. */
  433. class FocusHandler extends FocusAdapter
  434. {
  435. public void focusGained(FocusEvent ev) {
  436. dividerKeyboardResize = true;
  437. splitPane.repaint();
  438. }
  439. public void focusLost(FocusEvent ev) {
  440. dividerKeyboardResize = false;
  441. splitPane.repaint();
  442. }
  443. }
  444. static class KeyboardUpLeftAction extends AbstractAction {
  445. public void actionPerformed(ActionEvent ev) {
  446. JSplitPane splitPane = (JSplitPane)ev.getSource();
  447. SynthSplitPaneUI ui = (SynthSplitPaneUI)splitPane.getUI();
  448. if (ui.dividerKeyboardResize) {
  449. splitPane.setDividerLocation(Math.max(0,ui.getDividerLocation
  450. (splitPane) - ui.getKeyboardMoveIncrement()));
  451. }
  452. }
  453. }
  454. static class KeyboardDownRightAction extends AbstractAction {
  455. public void actionPerformed(ActionEvent ev) {
  456. JSplitPane splitPane = (JSplitPane)ev.getSource();
  457. SynthSplitPaneUI ui = (SynthSplitPaneUI)splitPane.getUI();
  458. if (ui.dividerKeyboardResize) {
  459. splitPane.setDividerLocation(ui.getDividerLocation(splitPane) +
  460. ui.getKeyboardMoveIncrement());
  461. }
  462. }
  463. }
  464. static class KeyboardHomeAction extends AbstractAction {
  465. public void actionPerformed(ActionEvent ev) {
  466. JSplitPane splitPane = (JSplitPane)ev.getSource();
  467. SynthSplitPaneUI ui = (SynthSplitPaneUI)splitPane.getUI();
  468. if (ui.dividerKeyboardResize) {
  469. splitPane.setDividerLocation(0);
  470. }
  471. }
  472. }
  473. static class KeyboardEndAction extends AbstractAction {
  474. public void actionPerformed(ActionEvent ev) {
  475. JSplitPane splitPane = (JSplitPane)ev.getSource();
  476. SynthSplitPaneUI ui = (SynthSplitPaneUI)splitPane.getUI();
  477. if (ui.dividerKeyboardResize) {
  478. Insets insets = splitPane.getInsets();
  479. int bottomI = (insets != null) ? insets.bottom : 0;
  480. int rightI = (insets != null) ? insets.right : 0;
  481. if (ui.orientation == JSplitPane.VERTICAL_SPLIT) {
  482. splitPane.setDividerLocation(splitPane.getHeight() -
  483. bottomI);
  484. }
  485. else {
  486. splitPane.setDividerLocation(splitPane.getWidth() -
  487. rightI);
  488. }
  489. }
  490. }
  491. }
  492. static class KeyboardResizeToggleAction extends AbstractAction {
  493. public void actionPerformed(ActionEvent ev) {
  494. JSplitPane splitPane = (JSplitPane)ev.getSource();
  495. SynthSplitPaneUI ui = (SynthSplitPaneUI)splitPane.getUI();
  496. if (!ui.dividerKeyboardResize) {
  497. splitPane.requestFocus();
  498. } else {
  499. JSplitPane parentSplitPane =
  500. (JSplitPane)SwingUtilities.getAncestorOfClass(JSplitPane.class, splitPane);
  501. if (parentSplitPane!=null) {
  502. parentSplitPane.requestFocus();
  503. }
  504. }
  505. }
  506. }
  507. /**
  508. * ActionListener that will focus on the opposite component that
  509. * has focus. EG if the left side has focus, this will transfer focus
  510. * to the right component.
  511. */
  512. static class ToggleSideFocusAction extends AbstractAction {
  513. public void actionPerformed(ActionEvent ae) {
  514. JSplitPane splitPane = (JSplitPane)ae.getSource();
  515. Component left = splitPane.getLeftComponent();
  516. Component right = splitPane.getRightComponent();
  517. KeyboardFocusManager manager =
  518. KeyboardFocusManager.getCurrentKeyboardFocusManager();
  519. Component focus = manager.getFocusOwner();
  520. Component focusOn = getNextSide(splitPane, focus);
  521. if (focusOn != null) {
  522. // don't change the focus if the new focused component belongs
  523. // to the same splitpane and the same side
  524. if ( focus!=null &&
  525. ( (SwingUtilities.isDescendingFrom(focus, left) &&
  526. SwingUtilities.isDescendingFrom(focusOn, left)) ||
  527. (SwingUtilities.isDescendingFrom(focus, right) &&
  528. SwingUtilities.isDescendingFrom(focusOn, right)) ) ) {
  529. return;
  530. }
  531. SynthLookAndFeel.compositeRequestFocus(focusOn);
  532. }
  533. }
  534. private Component getNextSide(JSplitPane splitPane, Component focus) {
  535. Component left = splitPane.getLeftComponent();
  536. Component right = splitPane.getRightComponent();
  537. Component next = null;
  538. if (focus!=null && SwingUtilities.isDescendingFrom(focus, left) &&
  539. right!=null) {
  540. next = getFirstAvailableComponent(right);
  541. if (next != null) {
  542. return next;
  543. }
  544. }
  545. JSplitPane parentSplitPane = (JSplitPane)SwingUtilities.getAncestorOfClass(JSplitPane.class, splitPane);
  546. if (parentSplitPane!=null) {
  547. // focus next side of the parent split pane
  548. next = getNextSide(parentSplitPane, focus);
  549. } else {
  550. next = getFirstAvailableComponent(left);
  551. if (next == null) {
  552. next = getFirstAvailableComponent(right);
  553. }
  554. }
  555. return next;
  556. }
  557. private Component getFirstAvailableComponent(Component c) {
  558. if (c!=null && c instanceof JSplitPane) {
  559. JSplitPane sp = (JSplitPane)c;
  560. Component left = getFirstAvailableComponent(sp.getLeftComponent());
  561. if (left != null) {
  562. c = left;
  563. } else {
  564. c = getFirstAvailableComponent(sp.getRightComponent());
  565. }
  566. }
  567. return c;
  568. }
  569. }
  570. /**
  571. * Action that will move focus out of the splitpane to the next
  572. * component (if present) if direction > 0 or to the previous
  573. * component (if present) if direction < 0
  574. */
  575. static class MoveFocusOutAction extends AbstractAction {
  576. private int direction;
  577. public MoveFocusOutAction(int newDirection) {
  578. direction = newDirection;
  579. }
  580. public void actionPerformed(ActionEvent ae) {
  581. JSplitPane splitPane = (JSplitPane)ae.getSource();
  582. Container rootAncestor = splitPane.getFocusCycleRootAncestor();
  583. FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy();
  584. Component focusOn = (direction > 0) ?
  585. policy.getComponentAfter(rootAncestor, splitPane) :
  586. policy.getComponentBefore(rootAncestor, splitPane);
  587. HashSet focusFrom = new HashSet();
  588. if (splitPane.isAncestorOf(focusOn)) {
  589. do {
  590. focusFrom.add(focusOn);
  591. rootAncestor = focusOn.getFocusCycleRootAncestor();
  592. policy = rootAncestor.getFocusTraversalPolicy();
  593. focusOn = (direction > 0) ?
  594. policy.getComponentAfter(rootAncestor, focusOn) :
  595. policy.getComponentBefore(rootAncestor, focusOn);
  596. } while (splitPane.isAncestorOf(focusOn) &&
  597. !focusFrom.contains(focusOn));
  598. }
  599. if ( focusOn!=null && !splitPane.isAncestorOf(focusOn) ) {
  600. focusOn.requestFocus();
  601. }
  602. }
  603. }
  604. /**
  605. * Returns the default non continuous layout divider, which is an
  606. * instanceof Canvas that fills the background in dark gray.
  607. */
  608. protected Component createDefaultNonContinuousLayoutDivider() {
  609. // PENDING: make this lightweight.
  610. return new Canvas() {
  611. public void paint(Graphics g) {
  612. paintDragDivider(g, 0, 0, getWidth(), getHeight());
  613. /*
  614. if(!isContinuousLayout() && getLastDragLocation() != -1) {
  615. Dimension size = splitPane.getSize();
  616. g.setColor(Color.darkGray);
  617. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  618. g.fillRect(0, 0, dividerSize - 1, size.height - 1);
  619. } else {
  620. g.fillRect(0, 0, size.width - 1, dividerSize - 1);
  621. }
  622. }
  623. */
  624. }
  625. };
  626. }
  627. /**
  628. * Sets the divider to use when the splitPane is configured to
  629. * not continuously layout. This divider will only be used during a
  630. * dragging session. It is recommended that the passed in component
  631. * be a heavy weight.
  632. */
  633. protected void setNonContinuousLayoutDivider(Component newDivider) {
  634. setNonContinuousLayoutDivider(newDivider, true);
  635. }
  636. /**
  637. * Sets the divider to use.
  638. */
  639. protected void setNonContinuousLayoutDivider(Component newDivider,
  640. boolean rememberSizes) {
  641. rememberPaneSizes = rememberSizes;
  642. if(nonContinuousLayoutDivider != null && splitPane != null) {
  643. splitPane.remove(nonContinuousLayoutDivider);
  644. }
  645. nonContinuousLayoutDivider = newDivider;
  646. }
  647. private void addHeavyweightDivider() {
  648. // PENDING: nuke this.
  649. if(nonContinuousLayoutDivider != null && splitPane != null) {
  650. /* Needs to remove all the components and re-add them! YECK! */
  651. // This is all done so that the nonContinuousLayoutDivider will
  652. // be drawn on top of the other components, without this, one
  653. // of the heavyweights will draw over the divider!
  654. Component leftC = splitPane.getLeftComponent();
  655. Component rightC = splitPane.getRightComponent();
  656. int lastLocation = splitPane.
  657. getDividerLocation();
  658. if(leftC != null)
  659. splitPane.setLeftComponent(null);
  660. if(rightC != null)
  661. splitPane.setRightComponent(null);
  662. splitPane.remove(divider);
  663. splitPane.add(nonContinuousLayoutDivider, SynthSplitPaneUI.
  664. NON_CONTINUOUS_DIVIDER,
  665. splitPane.getComponentCount());
  666. splitPane.setLeftComponent(leftC);
  667. splitPane.setRightComponent(rightC);
  668. splitPane.add(divider, JSplitPane.DIVIDER);
  669. if(rememberPaneSizes) {
  670. splitPane.setDividerLocation(lastLocation);
  671. }
  672. }
  673. }
  674. /**
  675. * Returns the divider to use when the splitPane is configured to
  676. * not continuously layout. This divider will only be used during a
  677. * dragging session.
  678. */
  679. public Component getNonContinuousLayoutDivider() {
  680. return nonContinuousLayoutDivider;
  681. }
  682. /**
  683. * Returns the splitpane this instance is currently contained
  684. * in.
  685. */
  686. public JSplitPane getSplitPane() {
  687. return splitPane;
  688. }
  689. /**
  690. * Messaged to reset the preferred sizes.
  691. */
  692. public void resetToPreferredSizes(JSplitPane jc) {
  693. if(splitPane != null) {
  694. LayoutManager layout = jc.getLayout();
  695. if (layout instanceof SplitPaneLayoutManager) {
  696. ((SplitPaneLayoutManager)layout).resetToPreferredSizes();
  697. }
  698. splitPane.revalidate();
  699. splitPane.repaint();
  700. }
  701. }
  702. /**
  703. * Sets the location of the divider to location.
  704. */
  705. public void setDividerLocation(JSplitPane jc, int location) {
  706. if (!ignoreDividerLocationChange) {
  707. dividerLocationIsSet = true;
  708. splitPane.revalidate();
  709. splitPane.repaint();
  710. }
  711. else {
  712. ignoreDividerLocationChange = false;
  713. }
  714. }
  715. /**
  716. * Returns the location of the divider, which may differ from what
  717. * the splitpane thinks the location of the divider is.
  718. */
  719. public int getDividerLocation(JSplitPane jc) {
  720. if(orientation == JSplitPane.HORIZONTAL_SPLIT)
  721. return divider.getX();
  722. return divider.getY();
  723. }
  724. /**
  725. * Gets the minimum location of the divider.
  726. */
  727. public int getMinimumDividerLocation(JSplitPane jc) {
  728. int minLoc = 0;
  729. Component leftC = splitPane.getLeftComponent();
  730. if ((leftC != null) && (leftC.isVisible())) {
  731. Insets insets = splitPane.getInsets();
  732. Dimension minSize = leftC.getMinimumSize();
  733. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  734. minLoc = minSize.width;
  735. } else {
  736. minLoc = minSize.height;
  737. }
  738. if(insets != null) {
  739. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  740. minLoc += insets.left;
  741. } else {
  742. minLoc += insets.top;
  743. }
  744. }
  745. }
  746. return minLoc;
  747. }
  748. /**
  749. * Gets the maximum location of the divider.
  750. */
  751. public int getMaximumDividerLocation(JSplitPane jc) {
  752. Dimension splitPaneSize = splitPane.getSize();
  753. int maxLoc = 0;
  754. Component rightC = splitPane.getRightComponent();
  755. if (rightC != null) {
  756. Insets insets = splitPane.getInsets();
  757. Dimension minSize = new Dimension(0, 0);
  758. if (rightC.isVisible()) {
  759. minSize = rightC.getMinimumSize();
  760. }
  761. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  762. maxLoc = splitPaneSize.width - minSize.width;
  763. } else {
  764. maxLoc = splitPaneSize.height - minSize.height;
  765. }
  766. maxLoc -= dividerSize;
  767. if(insets != null) {
  768. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  769. maxLoc -= insets.right;
  770. } else {
  771. maxLoc -= insets.top;
  772. }
  773. }
  774. }
  775. return Math.max(getMinimumDividerLocation(splitPane), maxLoc);
  776. }
  777. public void update(Graphics g, JComponent c) {
  778. SynthContext context = getContext(c);
  779. SynthLookAndFeel.update(context, g);
  780. paint(context, g);
  781. context.dispose();
  782. }
  783. public void paint(Graphics g, JComponent c) {
  784. SynthContext context = getContext(c);
  785. paint(context, g);
  786. context.dispose();
  787. }
  788. protected void paint(SynthContext context, Graphics g) {
  789. if (!painted && splitPane.getDividerLocation()<0) {
  790. ignoreDividerLocationChange = true;
  791. splitPane.setDividerLocation(getDividerLocation(splitPane));
  792. }
  793. painted = true;
  794. }
  795. private void paintDragDivider(Graphics g, int x, int y, int w, int h) {
  796. SynthContext context = getContext(splitPane,Region.SPLIT_PANE_DIVIDER);
  797. context.setComponentState(((context.getComponentState() | MOUSE_OVER) ^
  798. MOUSE_OVER) | PRESSED);
  799. SynthPainter painter = (SynthPainter)context.getStyle().
  800. get(context, "SplitPane.dragPainter");
  801. if (painter != null) {
  802. painter.paint(context, "foreground", g, x, y, w, h);
  803. }
  804. context.dispose();
  805. }
  806. /**
  807. * Messaged after the JSplitPane the receiver is providing the look
  808. * and feel for paints its children.
  809. */
  810. public void finishedPaintingChildren(JSplitPane jc, Graphics g) {
  811. if(jc == splitPane && getLastDragLocation() != -1 &&
  812. !isContinuousLayout() && !draggingHW) {
  813. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  814. paintDragDivider(g, getLastDragLocation(), 0, dividerSize - 1,
  815. splitPane.getHeight() - 1);
  816. } else {
  817. paintDragDivider(g, 0, lastDragLocation,
  818. splitPane.getWidth() - 1, dividerSize - 1);
  819. }
  820. }
  821. }
  822. /**
  823. * Resets the layout manager based on orientation and messages it
  824. * with invalidateLayout to pull in appropriate Components.
  825. */
  826. protected void resetLayoutManager() {
  827. SplitPaneLayoutManager layoutManager;
  828. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  829. layoutManager = new SplitPaneLayoutManager(0);
  830. } else {
  831. layoutManager = new SplitPaneLayoutManager(1);
  832. }
  833. splitPane.setLayout(layoutManager);
  834. layoutManager.updateComponents();
  835. splitPane.revalidate();
  836. splitPane.repaint();
  837. }
  838. /**
  839. * Should be messaged before the dragging session starts, resets
  840. * lastDragLocation and dividerSize.
  841. */
  842. protected void startDragging() {
  843. Component leftC = splitPane.getLeftComponent();
  844. Component rightC = splitPane.getRightComponent();
  845. ComponentPeer cPeer;
  846. beginDragDividerLocation = getDividerLocation(splitPane);
  847. draggingHW = false;
  848. if(leftC != null && (cPeer = leftC.getPeer()) != null &&
  849. !(cPeer instanceof LightweightPeer)) {
  850. draggingHW = true;
  851. } else if(rightC != null && (cPeer = rightC.getPeer()) != null
  852. && !(cPeer instanceof LightweightPeer)) {
  853. draggingHW = true;
  854. }
  855. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  856. setLastDragLocation(divider.getBounds().x);
  857. dividerSize = divider.getSize().width;
  858. if(!isContinuousLayout() && draggingHW) {
  859. nonContinuousLayoutDivider.setBounds
  860. (getLastDragLocation(), 0, dividerSize,
  861. splitPane.getHeight());
  862. addHeavyweightDivider();
  863. }
  864. } else {
  865. setLastDragLocation(divider.getBounds().y);
  866. dividerSize = divider.getSize().height;
  867. if(!isContinuousLayout() && draggingHW) {
  868. nonContinuousLayoutDivider.setBounds
  869. (0, getLastDragLocation(), splitPane.getWidth(),
  870. dividerSize);
  871. addHeavyweightDivider();
  872. }
  873. }
  874. }
  875. /**
  876. * Messaged during a dragging session to move the divider to the
  877. * passed in location. If continuousLayout is true the location is
  878. * reset and the splitPane validated.
  879. */
  880. protected void dragDividerTo(int location) {
  881. if(getLastDragLocation() != location) {
  882. if(isContinuousLayout()) {
  883. splitPane.setDividerLocation(location);
  884. setLastDragLocation(location);
  885. } else {
  886. int lastLoc = getLastDragLocation();
  887. setLastDragLocation(location);
  888. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  889. if(draggingHW) {
  890. nonContinuousLayoutDivider.setLocation(
  891. getLastDragLocation(), 0);
  892. } else {
  893. int splitHeight = splitPane.getHeight();
  894. splitPane.repaint(lastLoc, 0, dividerSize,
  895. splitHeight);
  896. splitPane.repaint(location, 0, dividerSize,
  897. splitHeight);
  898. }
  899. } else {
  900. if(draggingHW) {
  901. nonContinuousLayoutDivider.setLocation(0,
  902. getLastDragLocation());
  903. } else {
  904. int splitWidth = splitPane.getWidth();
  905. splitPane.repaint(0, lastLoc, splitWidth,
  906. dividerSize);
  907. splitPane.repaint(0, location, splitWidth,
  908. dividerSize);
  909. }
  910. }
  911. }
  912. }
  913. }
  914. /**
  915. * Messaged to finish the dragging session. If not continuous display
  916. * the dividers location will be reset.
  917. */
  918. protected void finishDraggingTo(int location) {
  919. dragDividerTo(location);
  920. setLastDragLocation(-1);
  921. if(!isContinuousLayout()) {
  922. Component leftC = splitPane.getLeftComponent();
  923. Rectangle leftBounds = leftC.getBounds();
  924. if (draggingHW) {
  925. if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
  926. nonContinuousLayoutDivider.setLocation(-dividerSize, 0);
  927. }
  928. else {
  929. nonContinuousLayoutDivider.setLocation(0, -dividerSize);
  930. }
  931. splitPane.remove(nonContinuousLayoutDivider);
  932. }
  933. splitPane.setDividerLocation(location);
  934. }
  935. }
  936. /**
  937. * LayoutManager for JSplitPanes that have an orientation of
  938. * HORIZONTAL_SPLIT.
  939. */
  940. private class SplitPaneLayoutManager implements LayoutManager2,UIResource {
  941. /* left, right, divider. (in this exact order) */
  942. protected int[] sizes;
  943. protected Component[] components;
  944. /** Size of the splitpane the last time laid out. */
  945. private int lastSplitPaneSize;
  946. /** True if resetToPreferredSizes has been invoked. */
  947. private boolean doReset;
  948. /** Axis, 0 for horizontal, or 1 for veritcal. */
  949. private int axis;
  950. SplitPaneLayoutManager(int axis) {
  951. this.axis = axis;
  952. components = new Component[3];
  953. components[0] = components[1] = components[2] = null;
  954. sizes = new int[3];
  955. }
  956. //
  957. // LayoutManager
  958. //
  959. /**
  960. * Does the actual layout.
  961. */
  962. public void layoutContainer(Container container) {
  963. Dimension containerSize = container.getSize();
  964. // If the splitpane has a zero size then no op out of here.
  965. // If we execute this function now, we're going to cause ourselves
  966. // much grief.
  967. if (containerSize.height <= 0 || containerSize.width <= 0 ) {
  968. lastSplitPaneSize = 0;
  969. return;
  970. }
  971. int spDividerLocation = splitPane.getDividerLocation();
  972. Insets insets = splitPane.getInsets();
  973. int availableSize = getAvailableSize(containerSize,
  974. insets);
  975. int newSize = getSizeForPrimaryAxis(containerSize);
  976. int beginLocation = getDividerLocation(splitPane);
  977. int dOffset = getSizeForPrimaryAxis(insets, true);
  978. Dimension dSize = (components[2] == null) ? null :
  979. components[2].getPreferredSize();
  980. if ((doReset && !dividerLocationIsSet) || spDividerLocation < 0) {
  981. resetToPreferredSizes(availableSize);
  982. }
  983. else if (lastSplitPaneSize <= 0 ||
  984. availableSize == lastSplitPaneSize || !painted ||
  985. (dSize != null &&
  986. getSizeForPrimaryAxis(dSize) != sizes[2])) {
  987. if (dSize != null) {
  988. sizes[2] = getSizeForPrimaryAxis(dSize);
  989. }
  990. else {
  991. sizes[2] = 0;
  992. }
  993. setDividerLocation(spDividerLocation - dOffset, availableSize);
  994. dividerLocationIsSet = false;
  995. }
  996. else if (availableSize != lastSplitPaneSize) {
  997. distributeSpace(availableSize - lastSplitPaneSize, true);
  998. }
  999. doReset = false;
  1000. dividerLocationIsSet = false;
  1001. lastSplitPaneSize = availableSize;
  1002. // Reset the bounds of each component
  1003. int nextLocation = getInitialLocation(insets);
  1004. int counter = 0;
  1005. while (counter < 3) {
  1006. if (components[counter] != null &&
  1007. components[counter].isVisible()) {
  1008. setComponentToSize(components[counter], sizes[counter],
  1009. nextLocation, insets, containerSize);
  1010. nextLocation += sizes[counter];
  1011. }
  1012. switch (counter) {
  1013. case 0:
  1014. counter = 2;
  1015. break;
  1016. case 2:
  1017. counter = 1;
  1018. break;
  1019. case 1:
  1020. counter = 3;
  1021. break;
  1022. }
  1023. }
  1024. if (painted) {
  1025. // This is tricky, there is never a good time for us
  1026. // to push the value to the splitpane, painted appears to
  1027. // the best time to do it. What is really needed is
  1028. // notification that layout has completed.
  1029. int newLocation = getDividerLocation(splitPane);
  1030. if (newLocation != (spDividerLocation - dOffset)) {
  1031. int lastLocation = splitPane.getLastDividerLocation();
  1032. ignoreDividerLocationChange = true;
  1033. try {
  1034. splitPane.setDividerLocation(newLocation);
  1035. // This is not always needed, but is rather tricky
  1036. // to determine when... The case this is needed for
  1037. // is if the user sets the divider location to some
  1038. // bogus value, say 0, and the actual value is 1, the
  1039. // call to setDividerLocation(1) will preserve the
  1040. // old value of 0, when we really want the divider
  1041. // location value before the call. This is needed for
  1042. // the one touch buttons.
  1043. splitPane.setLastDividerLocation(lastLocation);
  1044. } finally {
  1045. ignoreDividerLocationChange = false;
  1046. }
  1047. }
  1048. }
  1049. }
  1050. /**
  1051. * Adds the component at place. Place must be one of
  1052. * JSplitPane.LEFT, RIGHT, TOP, BOTTOM, or null (for the
  1053. * divider).
  1054. */
  1055. public void addLayoutComponent(String place, Component component) {
  1056. boolean isValid = true;
  1057. if(place != null) {
  1058. if(place.equals(JSplitPane.DIVIDER)) {
  1059. /* Divider. */
  1060. components[2] = component;
  1061. sizes[2] = getSizeForPrimaryAxis(component.
  1062. getPreferredSize());
  1063. } else if(place.equals(JSplitPane.LEFT) ||
  1064. place.equals(JSplitPane.TOP)) {
  1065. components[0] = component;
  1066. sizes[0] = 0;
  1067. } else if(place.equals(JSplitPane.RIGHT) ||
  1068. place.equals(JSplitPane.BOTTOM)) {
  1069. components[1] = component;
  1070. sizes[1] = 0;
  1071. } else if(!place.equals(
  1072. SynthSplitPaneUI.NON_CONTINUOUS_DIVIDER))
  1073. isValid = false;
  1074. } else {
  1075. isValid = false;
  1076. }
  1077. if(!isValid)
  1078. throw new IllegalArgumentException("cannot add to layout: " +
  1079. "unknown constraint: " +
  1080. place);
  1081. doReset = true;
  1082. }
  1083. /**
  1084. * Returns the minimum size needed to contain the children.
  1085. * The width is the sum of all the childrens min widths and
  1086. * the height is the largest of the childrens minimum heights.
  1087. */
  1088. public Dimension minimumLayoutSize(Container container) {
  1089. int minPrimary = 0;
  1090. int minSecondary = 0;
  1091. Insets insets = splitPane.getInsets();
  1092. for (int counter=0; counter<3; counter++) {
  1093. if(components[counter] != null) {
  1094. Dimension minSize = components[counter].getMinimumSize();
  1095. int secSize = getSizeForSecondaryAxis(minSize);
  1096. minPrimary += getSizeForPrimaryAxis(minSize);
  1097. if(secSize > minSecondary)
  1098. minSecondary = secSize;
  1099. }
  1100. }
  1101. if(insets != null) {
  1102. minPrimary += getSizeForPrimaryAxis(insets, true) +
  1103. getSizeForPrimaryAxis(insets, false);
  1104. minSecondary += getSizeForSecondaryAxis(insets, true) +
  1105. getSizeForSecondaryAxis(insets, false);
  1106. }
  1107. if (axis == 0) {
  1108. return new Dimension(minPrimary, minSecondary);
  1109. }
  1110. return new Dimension(minSecondary, minPrimary);
  1111. }
  1112. /**
  1113. * Returns the preferred size needed to contain the children.
  1114. * The width is the sum of all the childrens preferred widths and
  1115. * the height is the largest of the childrens preferred heights.
  1116. */
  1117. public Dimension preferredLayoutSize(Container container) {
  1118. int prePrimary = 0;
  1119. int preSecondary = 0;
  1120. Insets insets = splitPane.getInsets();
  1121. for(int counter = 0; counter < 3; counter++) {
  1122. if(components[counter] != null) {
  1123. Dimension preSize = components[counter].
  1124. getPreferredSize();
  1125. int secSize = getSizeForSecondaryAxis(preSize);
  1126. prePrimary += getSizeForPrimaryAxis(preSize);
  1127. if(secSize > preSecondary)
  1128. preSecondary = secSize;
  1129. }
  1130. }
  1131. if(insets != null) {
  1132. prePrimary += getSizeForPrimaryAxis(insets, true) +
  1133. getSizeForPrimaryAxis(insets, false);
  1134. preSecondary += getSizeForSecondaryAxis(insets, true) +
  1135. getSizeForSecondaryAxis(insets, false);
  1136. }
  1137. if (axis == 0) {
  1138. return new Dimension(prePrimary, preSecondary);
  1139. }
  1140. return new Dimension(preSecondary, prePrimary);
  1141. }
  1142. /**
  1143. * Removes the specified component from our knowledge.
  1144. */
  1145. public void removeLayoutComponent(Component component) {
  1146. for(int counter = 0; counter < 3; counter++) {
  1147. if(components[counter] == component) {
  1148. components[counter] = null;
  1149. sizes[counter] = 0;
  1150. doReset = true;
  1151. }
  1152. }
  1153. }
  1154. //
  1155. // LayoutManager2
  1156. //
  1157. /**
  1158. * Adds the specified component to the layout, using the specified
  1159. * constraint object.
  1160. * @param comp the component to be added
  1161. * @param constraints where/how the component is added to the layout.
  1162. */
  1163. public void addLayoutComponent(Component comp, Object constraints) {
  1164. if ((constraints == null) || (constraints instanceof String)) {
  1165. addLayoutComponent((String)constraints, comp);
  1166. } else {
  1167. throw new IllegalArgumentException("cannot add to layout: " +
  1168. "constraint must be a " +
  1169. "string (or null)");
  1170. }
  1171. }
  1172. /**
  1173. * Returns the alignment along the x axis. This specifies how
  1174. * the component would like to be aligned relative to other
  1175. * components. The value should be a number between 0 and 1
  1176. * where 0 represents alignment along the origin, 1 is aligned
  1177. * the furthest away from the origin, 0.5 is centered, etc.
  1178. */
  1179. public float getLayoutAlignmentX(Container target) {
  1180. return 0.0f;
  1181. }
  1182. /**
  1183. * Returns the alignment along the y axis. This specifies how
  1184. * the component would like to be aligned relative to other
  1185. * components. The value should be a number between 0 and 1
  1186. * where 0 represents alignment along the origin, 1 is aligned
  1187. * the furthest away from the origin, 0.5 is centered, etc.
  1188. */
  1189. public float getLayoutAlignmentY(Container target) {
  1190. return 0.0f;
  1191. }
  1192. /**
  1193. * Does nothing. If the developer really wants to change the
  1194. * size of one of the views JSplitPane.resetToPreferredSizes should
  1195. * be messaged.
  1196. */
  1197. public void invalidateLayout(Container c) {
  1198. }
  1199. /**
  1200. * Returns the maximum layout size, which is Integer.MAX_VALUE
  1201. * in both directions.
  1202. */
  1203. public Dimension maximumLayoutSize(Container target) {
  1204. return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  1205. }
  1206. //
  1207. // New methods.
  1208. //
  1209. /**
  1210. * Marks the receiver so that the next time this instance is
  1211. * laid out it'll ask for the preferred sizes.
  1212. */
  1213. public void resetToPreferredSizes() {
  1214. doReset = true;
  1215. }
  1216. /**
  1217. * Resets the size of the Component at the passed in location.
  1218. */
  1219. protected void resetSizeAt(int index) {
  1220. sizes[index] = 0;
  1221. doReset = true;
  1222. }
  1223. /**
  1224. * Sets the sizes to <code>newSizes</code>.
  1225. */
  1226. protected void setSizes(int[] newSizes) {
  1227. System.arraycopy(newSizes, 0, sizes, 0, 3);
  1228. }
  1229. /**
  1230. * Returns the sizes of the components.
  1231. */
  1232. protected int[] getSizes() {
  1233. int[] retSizes = new int[3];
  1234. System.arraycopy(sizes, 0, retSizes, 0, 3);
  1235. return retSizes;
  1236. }
  1237. /**
  1238. * Returns the width of the passed in Components preferred size.
  1239. */
  1240. protected int getPreferredSizeOfComponent(Component c) {
  1241. return getSizeForPrimaryAxis(c.getPreferredSize());
  1242. }
  1243. /**
  1244. * Returns the width of the passed in Components minimum size.
  1245. */
  1246. int getMinimumSizeOfComponent(Component c) {
  1247. return getSizeForPrimaryAxis(c.getMinimumSize());
  1248. }
  1249. /**
  1250. * Returns the width of the passed in component.
  1251. */
  1252. protected int getSizeOfComponent(Component c) {
  1253. return getSizeForPrimaryAxis(c.getSize());
  1254. }
  1255. /**
  1256. * Returns the available width based on the container size and
  1257. * Insets.
  1258. */
  1259. protected int getAvailableSize(Dimension containerSize,
  1260. Insets insets) {
  1261. if(insets == null)
  1262. return getSizeForPrimaryAxis(containerSize);
  1263. return (getSizeForPrimaryAxis(containerSize) -
  1264. (getSizeForPrimaryAxis(insets, true) +
  1265. getSizeForPrimaryAxis(insets, false)));
  1266. }
  1267. /**
  1268. * Returns the left inset, unless the Insets are null in which case
  1269. * 0 is returned.
  1270. */
  1271. protected int getInitialLocation(Insets insets) {
  1272. if(insets != null)
  1273. return getSizeForPrimaryAxis(insets, true);
  1274. return 0;
  1275. }
  1276. /**
  1277. * Sets the width of the component c to be size, placing its
  1278. * x location at location, y to the insets.top and height
  1279. * to the containersize.height less the top and bottom insets.
  1280. */
  1281. protected void setComponentToSize(Component c, int size,
  1282. int location, Insets insets,
  1283. Dimension containerSize) {
  1284. if(insets != null) {
  1285. if (axis == 0) {
  1286. c.setBounds(location, insets.top, size,
  1287. containerSize.height -
  1288. (insets.top + insets.bottom));
  1289. }
  1290. else {
  1291. c.setBounds(insets.left, location, containerSize.width -
  1292. (insets.left + insets.right), size);
  1293. }
  1294. }
  1295. else {
  1296. if (axis == 0) {
  1297. c.setBounds(location, 0, size, containerSize.height);
  1298. }
  1299. else {
  1300. c.setBounds(0, location, containerSize.width, size);
  1301. }
  1302. }
  1303. }
  1304. /**
  1305. * If the axis == 0, the width is returned, otherwise the height.
  1306. */
  1307. int getSizeForPrimaryAxis(Dimension size) {
  1308. if (axis == 0) {
  1309. return size.width;
  1310. }
  1311. return size.height;
  1312. }
  1313. /**
  1314. * If the axis == 0, the width is returned, otherwise the height.
  1315. */
  1316. int getSizeForSecondaryAxis(Dimension size) {
  1317. if (axis == 0) {
  1318. return size.height;
  1319. }
  1320. return size.width;
  1321. }
  1322. /**
  1323. * Returns a particular value of the inset identified by the
  1324. * axis and <code>isTop</code><p>
  1325. * axis isTop
  1326. * 0 true - left
  1327. * 0 false - right
  1328. * 1 true - top
  1329. * 1 false - bottom
  1330. */
  1331. int getSizeForPrimaryAxis(Insets insets, boolean isTop) {
  1332. if (axis == 0) {
  1333. if (isTop) {
  1334. return insets.left;
  1335. }
  1336. return insets.right;
  1337. }
  1338. if (isTop) {
  1339. return insets.top;
  1340. }
  1341. return insets.bottom;
  1342. }
  1343. /**
  1344. * Returns a particular value of the inset identified by the
  1345. * axis and <code>isTop</code><p>
  1346. * axis isTop
  1347. * 0 true - left
  1348. * 0 false - right
  1349. * 1 true - top
  1350. * 1 false - bottom
  1351. */
  1352. int getSizeForSecondaryAxis(Insets insets, boolean isTop) {
  1353. if (axis == 0) {
  1354. if (isTop) {
  1355. return insets.top;
  1356. }
  1357. return insets.bottom;
  1358. }
  1359. if (isTop) {
  1360. return insets.left;
  1361. }
  1362. return insets.right;
  1363. }
  1364. /**
  1365. * Determines the components. This should be called whenever
  1366. * a new instance of this is installed into an existing
  1367. * SplitPane.
  1368. */
  1369. protected void updateComponents() {
  1370. Component comp;
  1371. comp = splitPane.getLeftComponent();
  1372. if(components[0] != comp) {
  1373. components[0] = comp;
  1374. if(comp == null) {
  1375. sizes[0] = 0;
  1376. } else {
  1377. sizes[0] = -1;
  1378. }
  1379. }
  1380. comp = splitPane.getRightComponent();
  1381. if(components[1] != comp) {
  1382. components[1] = comp;
  1383. if(comp == null) {
  1384. sizes[1] = 0;
  1385. } else {
  1386. sizes[1] = -1;
  1387. }
  1388. }
  1389. /* Find the divider. */
  1390. Component[] children = splitPane.getComponents();
  1391. Component oldDivider = components[2];
  1392. components[2] = null;
  1393. for(int counter = children.length - 1; counter >= 0; counter--) {
  1394. if(children[counter] != components[0] &&
  1395. children[counter] != components[1] &&
  1396. children[counter] != nonContinuousLayoutDivider) {
  1397. if(oldDivider != children[counter]) {
  1398. components[2] = children[counter];
  1399. } else {
  1400. components[2] = oldDivider;
  1401. }
  1402. break;
  1403. }
  1404. }
  1405. if(components[2] == null) {
  1406. sizes[2] = 0;
  1407. }
  1408. else {
  1409. sizes[2] = getSizeForPrimaryAxis(components[2].getPreferredSize());
  1410. }
  1411. }
  1412. /**
  1413. * Resets the size of the first component to <code>leftSize</code>,
  1414. * and the right component to the remainder of the space.
  1415. */
  1416. void setDividerLocation(int leftSize, int availableSize) {
  1417. boolean lValid = (components[0] != null &&
  1418. components[0].isVisible());
  1419. boolean rValid = (components[1] != null &&
  1420. components[1].isVisible());
  1421. boolean dValid = (components[2] != null &&
  1422. components[2].isVisible());
  1423. int max = availableSize;
  1424. if (dValid) {
  1425. max -= sizes[2];
  1426. }
  1427. leftSize = Math.max(0, Math.min(leftSize, max));
  1428. if (lValid) {
  1429. if (rValid) {
  1430. sizes[0] = leftSize;
  1431. sizes[1] = max - leftSize;
  1432. }
  1433. else {
  1434. sizes[0] = max;
  1435. sizes[1] = 0;
  1436. }
  1437. }
  1438. else if (rValid) {
  1439. sizes[1] = max;
  1440. sizes[0] = 0;
  1441. }
  1442. }
  1443. /**
  1444. * Returns an array of the minimum sizes of the components.
  1445. */
  1446. int[] getPreferredSizes() {
  1447. int[] retValue = new int[3];
  1448. for (int counter = 0; counter < 3; counter++) {
  1449. if (components[counter] != null &&
  1450. components[counter].isVisible()) {
  1451. retValue[counter] = getPreferredSizeOfComponent
  1452. (components[counter]);
  1453. }
  1454. else {
  1455. retValue[counter] = -1;
  1456. }
  1457. }
  1458. return retValue;
  1459. }
  1460. /**
  1461. * Returns an array of the minimum sizes of the components.
  1462. */
  1463. int[] getMinimumSizes() {
  1464. int[] retValue = new int[3];
  1465. for (int counter = 0; counter < 2; counter++) {
  1466. if (components[counter] != null &&
  1467. components[counter].isVisible()) {
  1468. retValue[counter] = getMinimumSizeOfComponent
  1469. (components[counter]);
  1470. }
  1471. else {
  1472. retValue[counter] = -1;
  1473. }
  1474. }
  1475. retValue[2] = (components[2] != null) ?
  1476. getMinimumSizeOfComponent(components[2]) : -1;
  1477. return retValue;
  1478. }
  1479. /**
  1480. * Resets the components to their preferred sizes.
  1481. */
  1482. void resetToPreferredSizes(int availableSize) {
  1483. // Set the sizes to the preferred sizes (if fits), otherwise
  1484. // set to min sizes and distribute any extra space.
  1485. int[] testSizes = getPreferredSizes();
  1486. int totalSize = 0;
  1487. for (int counter = 0; counter < 3; counter++) {
  1488. if (testSizes[counter] != -1) {
  1489. totalSize += testSizes[counter];
  1490. }
  1491. }
  1492. if (totalSize > availableSize) {
  1493. testSizes = getMinimumSizes();
  1494. totalSize = 0;
  1495. for (int counter = 0; counter < 3; counter++) {
  1496. if (testSizes[counter] != -1) {
  1497. totalSize += testSizes[counter];
  1498. }
  1499. }
  1500. }
  1501. setSizes(testSizes);
  1502. distributeSpace(availableSize - totalSize, false);
  1503. }
  1504. /**
  1505. * Distributes <code>space</code> between the two components
  1506. * (divider won't get any extra space) based on the weighting. This
  1507. * attempts to honor the min size of the components.
  1508. *
  1509. * @param keepHidden if true and one of the components is 0x0
  1510. * it gets none of the extra space
  1511. */
  1512. void distributeSpace(int space, boolean keepHidden) {
  1513. boolean lValid = (components[0] != null &&
  1514. components[0].isVisible());
  1515. boolean rValid = (components[1] != null &&
  1516. components[1].isVisible());
  1517. if (keepHidden) {
  1518. if (lValid && getSizeForPrimaryAxis(
  1519. components[0].getSize()) == 0) {
  1520. lValid = false;
  1521. if (rValid && getSizeForPrimaryAxis(
  1522. components[1].getSize()) == 0) {
  1523. // Both aren't valid, force them both to be valid
  1524. lValid = true;
  1525. }
  1526. }
  1527. else if (rValid && getSizeForPrimaryAxis(
  1528. components[1].getSize()) == 0) {
  1529. rValid = false;
  1530. }
  1531. }
  1532. if (lValid && rValid) {
  1533. double weight = splitPane.getResizeWeight();
  1534. int lExtra = (int)(weight * (double)space);
  1535. int rExtra = (space - lExtra);
  1536. sizes[0] += lExtra;
  1537. sizes[1] += rExtra;
  1538. int lMin = getMinimumSizeOfComponent(components[0]);
  1539. int rMin = getMinimumSizeOfComponent(components[1]);
  1540. boolean lMinValid = (sizes[0] >= lMin);
  1541. boolean rMinValid = (sizes[1] >= rMin);
  1542. if (!lMinValid && !rMinValid) {
  1543. if (sizes[0] < 0) {
  1544. sizes[1] += sizes[0];
  1545. sizes[0] = 0;
  1546. }
  1547. else if (sizes[1] < 0) {
  1548. sizes[0] += sizes[1];
  1549. sizes[1] = 0;
  1550. }
  1551. }
  1552. else if (!lMinValid) {
  1553. if (sizes[1] - (lMin - sizes[0]) < rMin) {
  1554. // both below min, just make sure > 0
  1555. if (sizes[0] < 0) {
  1556. sizes[1] += sizes[0];
  1557. sizes[0] = 0;
  1558. }
  1559. }
  1560. else {
  1561. sizes[1] -= (lMin - sizes[0]);
  1562. sizes[0] = lMin;
  1563. }
  1564. }
  1565. else if (!rMinValid) {
  1566. if (sizes[0] - (rMin - sizes[1]) < lMin) {
  1567. // both below min, just make sure > 0
  1568. if (sizes[1] < 0) {
  1569. sizes[0] += sizes[1];
  1570. sizes[1] = 0;
  1571. }
  1572. }
  1573. else {
  1574. sizes[0] -= (rMin - sizes[1]);
  1575. sizes[1] = rMin;
  1576. }
  1577. }
  1578. if (sizes[0] < 0) {
  1579. sizes[0] = 0;
  1580. }
  1581. if (sizes[1] < 0) {
  1582. sizes[1] = 0;
  1583. }
  1584. }
  1585. else if (lValid) {
  1586. sizes[0] = Math.max(0, sizes[0] + space);
  1587. }
  1588. else if (rValid) {
  1589. sizes[1] = Math.max(0, sizes[1] + space);
  1590. }
  1591. }
  1592. }
  1593. /**
  1594. * Cursor used for HORIZONTAL_SPLIT splitpanes.
  1595. */
  1596. private static final Cursor horizontalCursor =
  1597. Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
  1598. /**
  1599. * Cursor used for VERTICAL_SPLIT splitpanes.
  1600. */
  1601. private static final Cursor verticalCursor =
  1602. Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR);
  1603. /**
  1604. * Default cursor.
  1605. */
  1606. private static final Cursor defaultCursor =
  1607. Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
  1608. // * @(#)BasicSplitPaneDivider.java 1.45 01/12/03
  1609. private class Divider extends JComponent implements PropertyChangeListener{
  1610. /**
  1611. * Handles mouse dragging message to do the actual dragging.
  1612. */
  1613. protected DragController dragger;
  1614. /**
  1615. * Size of the divider.
  1616. */
  1617. protected int dividerSize = 0;
  1618. /**
  1619. * Divider that is used for noncontinuous layout mode.
  1620. */
  1621. protected Component hiddenDivider;
  1622. /**
  1623. * Handles mouse events from both this class, and the split pane.
  1624. * Mouse events are handled for the splitpane since you want to be able
  1625. * to drag when clicking on the border of the divider, which is not
  1626. * drawn by the divider.
  1627. */
  1628. protected MouseHandler mouseHandler;
  1629. /**
  1630. * True while the mouse is over the divider.
  1631. */
  1632. private boolean mouseOver;
  1633. /**
  1634. * Creates an instance of Divider. Registers this
  1635. * instance for mouse events and mouse dragged events.
  1636. */
  1637. public Divider(JSplitPane splitPane) {
  1638. setLayout(new DividerLayout());
  1639. int orientation = splitPane.getOrientation();
  1640. setCursor((orientation == JSplitPane.HORIZONTAL_SPLIT) ?
  1641. horizontalCursor : verticalCursor);
  1642. mouseHandler = new MouseHandler();
  1643. splitPane.addMouseListener(mouseHandler);
  1644. splitPane.addMouseMotionListener(mouseHandler);
  1645. addMouseListener(mouseHandler);
  1646. addMouseMotionListener(mouseHandler);
  1647. splitPane.addPropertyChangeListener(this);
  1648. }
  1649. public String getUIClassID() {
  1650. return "SplitPaneDividerUI";
  1651. }
  1652. /**
  1653. * Sets the size of the divider to <code>newSize</code>. That is
  1654. * the width if the splitpane is <code>HORIZONTAL_SPLIT</code>, or
  1655. * the height of <code>VERTICAL_SPLIT</code>.
  1656. */
  1657. public void setDividerSize(int newSize) {
  1658. dividerSize = newSize;
  1659. }
  1660. /**
  1661. * Returns the size of the divider, that is the width if the splitpane
  1662. * is HORIZONTAL_SPLIT, or the height of VERTICAL_SPLIT.
  1663. */
  1664. public int getDividerSize() {
  1665. return dividerSize;
  1666. }
  1667. /**
  1668. * Returns dividerSize x dividerSize
  1669. */
  1670. public Dimension getPreferredSize() {
  1671. // Ideally this would return the size from the layout manager,
  1672. // but that could result in the layed out size being different from
  1673. // the dividerSize, which may break developers as well as
  1674. // SynthSplitPaneUI.
  1675. if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
  1676. return new Dimension(getDividerSize(), 1);
  1677. }
  1678. return new Dimension(1, getDividerSize());
  1679. }
  1680. /**
  1681. * Returns dividerSize x dividerSize
  1682. */
  1683. public Dimension getMinimumSize() {
  1684. return getPreferredSize();
  1685. }
  1686. /**
  1687. * Returns true if the mouse is over the divider.
  1688. */
  1689. public boolean isMouseOver() {
  1690. return mouseOver;
  1691. }
  1692. /**
  1693. * Property change event, presumably from the JSplitPane, will message
  1694. * updateOrientation if necessary.
  1695. */
  1696. public void propertyChange(PropertyChangeEvent e) {
  1697. if (e.getSource() == splitPane) {
  1698. if (e.getPropertyName().equals(
  1699. JSplitPane.ORIENTATION_PROPERTY)) {
  1700. orientation = splitPane.getOrientation();
  1701. setCursor((orientation == JSplitPane.HORIZONTAL_SPLIT) ?
  1702. horizontalCursor : verticalCursor);
  1703. revalidate();
  1704. }
  1705. }
  1706. }
  1707. public void paintComponent(Graphics g) {
  1708. SynthContext context = getContext(splitPane,
  1709. Region.SPLIT_PANE_DIVIDER);
  1710. Rectangle bounds = getBounds();
  1711. bounds.x = bounds.y = 0;
  1712. SynthLookAndFeel.updateSubregion(context, g, bounds);
  1713. SynthPainter foreground = (SynthPainter)context.getStyle().
  1714. get(context, "foreground");
  1715. if (foreground != null) {
  1716. foreground.paint(context, "foreground", g, 0, 0, getWidth(),
  1717. getHeight());
  1718. }
  1719. context.dispose();
  1720. }
  1721. private int mapDirection(boolean isLeft) {
  1722. if (isLeft) {
  1723. if (splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT){
  1724. return SwingConstants.WEST;
  1725. }
  1726. return SwingConstants.NORTH;
  1727. }
  1728. if (splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT){
  1729. return SwingConstants.EAST;
  1730. }
  1731. return SwingConstants.SOUTH;
  1732. }
  1733. /**
  1734. * MouseHandler is responsible for converting mouse events
  1735. * (released, dragged...) into the appropriate DragController
  1736. * methods.
  1737. * <p>
  1738. */
  1739. protected class MouseHandler extends MouseAdapter
  1740. implements MouseMotionListener {
  1741. /**
  1742. * Starts the dragging session by creating the appropriate instance
  1743. * of DragController.
  1744. */
  1745. public void mousePressed(MouseEvent e) {
  1746. if ((e.getSource() == Divider.this ||
  1747. e.getSource() == splitPane) &&
  1748. dragger == null && splitPane.isEnabled()) {
  1749. Component newHiddenDivider =
  1750. getNonContinuousLayoutDivider();
  1751. if (hiddenDivider != newHiddenDivider) {
  1752. if (hiddenDivider != null) {
  1753. hiddenDivider.removeMouseListener(this);
  1754. hiddenDivider.removeMouseMotionListener(this);
  1755. }
  1756. hiddenDivider = newHiddenDivider;
  1757. if (hiddenDivider != null) {
  1758. hiddenDivider.addMouseMotionListener(this);
  1759. hiddenDivider.addMouseListener(this);
  1760. }
  1761. }
  1762. if (splitPane.getLeftComponent() != null &&
  1763. splitPane.getRightComponent() != null) {
  1764. if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
  1765. dragger = new DragController(e);
  1766. }
  1767. else {
  1768. dragger = new VerticalDragController(e);
  1769. }
  1770. if (!dragger.isValid()) {
  1771. dragger = null;
  1772. }
  1773. else {
  1774. startDragging();
  1775. dragger.continueDrag(e);
  1776. }
  1777. }
  1778. e.consume();
  1779. }
  1780. }
  1781. /**
  1782. * If dragger is not null it is messaged with completeDrag.
  1783. */
  1784. public void mouseReleased(MouseEvent e) {
  1785. if (dragger != null) {
  1786. if (e.getSource() == splitPane) {
  1787. dragger.completeDrag(e.getX(), e.getY());
  1788. }
  1789. else if (e.getSource() == Divider.this) {
  1790. Point ourLoc = getLocation();
  1791. dragger.completeDrag(e.getX() + ourLoc.x,
  1792. e.getY() + ourLoc.y);
  1793. }
  1794. else if (e.getSource() == hiddenDivider) {
  1795. Point hDividerLoc = hiddenDivider.getLocation();
  1796. int ourX = e.getX() + hDividerLoc.x;
  1797. int ourY = e.getY() + hDividerLoc.y;
  1798. dragger.completeDrag(ourX, ourY);
  1799. }
  1800. dragger = null;
  1801. e.consume();
  1802. }
  1803. }
  1804. public void mouseEntered(MouseEvent e) {
  1805. if (e.getSource() == Divider.this) {
  1806. mouseOver = true;
  1807. repaint();
  1808. }
  1809. }
  1810. public void mouseExited(MouseEvent e) {
  1811. if (e.getSource() == Divider.this) {
  1812. mouseOver = false;
  1813. repaint();
  1814. }
  1815. }
  1816. //
  1817. // MouseMotionListener
  1818. //
  1819. /**
  1820. * If dragger is not null it is messaged with continueDrag.
  1821. */
  1822. public void mouseDragged(MouseEvent e) {
  1823. if (dragger != null) {
  1824. if (e.getSource() == splitPane) {
  1825. dragger.continueDrag(e.getX(), e.getY());
  1826. }
  1827. else if (e.getSource() == Divider.this) {
  1828. Point ourLoc = getLocation();
  1829. dragger.continueDrag(e.getX() + ourLoc.x,
  1830. e.getY() + ourLoc.y);
  1831. }
  1832. else if (e.getSource() == hiddenDivider) {
  1833. Point hDividerLoc = hiddenDivider.getLocation();
  1834. int ourX = e.getX() + hDividerLoc.x;
  1835. int ourY = e.getY() + hDividerLoc.y;
  1836. dragger.continueDrag(ourX, ourY);
  1837. }
  1838. e.consume();
  1839. }
  1840. }
  1841. /**
  1842. * Resets the cursor based on the orientation.
  1843. */
  1844. public void mouseMoved(MouseEvent e) {
  1845. }
  1846. }
  1847. /**
  1848. * Handles the events during a dragging session for a
  1849. * HORIZONTAL_SPLIT oriented split pane. This continually
  1850. * messages <code>dragDividerTo</code> and then when done messages
  1851. * <code>finishDraggingTo</code>. When an instance is created it should be
  1852. * messaged with <code>isValid</code> to insure that dragging can happen
  1853. * (dragging won't be allowed if the two views can not be resized).
  1854. */
  1855. protected class DragController {
  1856. /**
  1857. * Initial location of the divider.
  1858. */
  1859. int initialX;
  1860. /**
  1861. * Maximum and minimum positions to drag to.
  1862. */
  1863. int maxX, minX;
  1864. /**
  1865. * Initial location the mouse down happened at.
  1866. */
  1867. int offset;
  1868. protected DragController(MouseEvent e) {
  1869. JSplitPane splitPane = getSplitPane();
  1870. Component leftC = splitPane.getLeftComponent();
  1871. Component rightC = splitPane.getRightComponent();
  1872. initialX = getLocation().x;
  1873. if (e.getSource() == Divider.this) {
  1874. offset = e.getX();
  1875. }
  1876. else { // splitPane
  1877. offset = e.getX() - initialX;
  1878. }
  1879. if (leftC == null || rightC == null || offset < -1 ||
  1880. offset >= getSize().width) {
  1881. // Don't allow dragging.
  1882. maxX = -1;
  1883. }
  1884. else {
  1885. Insets insets = splitPane.getInsets();
  1886. if (leftC.isVisible()) {
  1887. minX = leftC.getMinimumSize().width;
  1888. if (insets != null) {
  1889. minX += insets.left;
  1890. }
  1891. }
  1892. else {
  1893. minX = 0;
  1894. }
  1895. if (rightC.isVisible()) {
  1896. int right = (insets != null) ? insets.right : 0;
  1897. maxX = Math.max(0, splitPane.getSize().width -
  1898. (getSize().width + right) -
  1899. rightC.getMinimumSize().width);
  1900. }
  1901. else {
  1902. int right = (insets != null) ? insets.right : 0;
  1903. maxX = Math.max(0, splitPane.getSize().width -
  1904. (getSize().width + right));
  1905. }
  1906. if (maxX < minX) minX = maxX = 0;
  1907. }
  1908. }
  1909. /**
  1910. * Returns true if the dragging session is valid.
  1911. */
  1912. protected boolean isValid() {
  1913. return (maxX > 0);
  1914. }
  1915. /**
  1916. * Returns the new position to put the divider at based on
  1917. * the passed in MouseEvent.
  1918. */
  1919. protected int positionForMouseEvent(MouseEvent e) {
  1920. int newX = (e.getSource() == Divider.this) ?
  1921. (e.getX() + getLocation().x) : e.getX();
  1922. newX = Math.min(maxX, Math.max(minX, newX - offset));
  1923. return newX;
  1924. }
  1925. /**
  1926. * Returns the x argument, since this is used for horizontal
  1927. * splits.
  1928. */
  1929. protected int getNeededLocation(int x, int y) {
  1930. int newX;
  1931. newX = Math.min(maxX, Math.max(minX, x - offset));
  1932. return newX;
  1933. }
  1934. protected void continueDrag(int newX, int newY) {
  1935. dragDividerTo(getNeededLocation(newX, newY));
  1936. }
  1937. /**
  1938. * Messages dragDividerTo with the new location for the mouse
  1939. * event.
  1940. */
  1941. protected void continueDrag(MouseEvent e) {
  1942. dragDividerTo(positionForMouseEvent(e));
  1943. }
  1944. protected void completeDrag(int x, int y) {
  1945. finishDraggingTo(getNeededLocation(x, y));
  1946. }
  1947. /**
  1948. * Messages finishDraggingTo with the new location for the mouse
  1949. * event.
  1950. */
  1951. protected void completeDrag(MouseEvent e) {
  1952. finishDraggingTo(positionForMouseEvent(e));
  1953. }
  1954. } // End of Divider.DragController
  1955. /**
  1956. * Handles the events during a dragging session for a
  1957. * VERTICAL_SPLIT oriented split pane. This continually
  1958. * messages <code>dragDividerTo</code> and then when done messages
  1959. * <code>finishDraggingTo</code>. When an instance is created it should be
  1960. * messaged with <code>isValid</code> to insure that dragging can happen
  1961. * (dragging won't be allowed if the two views can not be resized).
  1962. */
  1963. protected class VerticalDragController extends DragController {
  1964. /* DragControllers ivars are now in terms of y, not x. */
  1965. protected VerticalDragController(MouseEvent e) {
  1966. super(e);
  1967. JSplitPane splitPane = getSplitPane();
  1968. Component leftC = splitPane.getLeftComponent();
  1969. Component rightC = splitPane.getRightComponent();
  1970. initialX = getLocation().y;
  1971. if (e.getSource() == Divider.this) {
  1972. offset = e.getY();
  1973. }
  1974. else {
  1975. offset = e.getY() - initialX;
  1976. }
  1977. if (leftC == null || rightC == null || offset < -1 ||
  1978. offset > getSize().height) {
  1979. // Don't allow dragging.
  1980. maxX = -1;
  1981. }
  1982. else {
  1983. Insets insets = splitPane.getInsets();
  1984. if (leftC.isVisible()) {
  1985. minX = leftC.getMinimumSize().height;
  1986. if (insets != null) {
  1987. minX += insets.top;
  1988. }
  1989. }
  1990. else {
  1991. minX = 0;
  1992. }
  1993. if (rightC.isVisible()) {
  1994. int bottom = (insets != null) ? insets.bottom : 0;
  1995. maxX = Math.max(0, splitPane.getSize().height -
  1996. (getSize().height + bottom) -
  1997. rightC.getMinimumSize().height);
  1998. }
  1999. else {
  2000. int bottom = (insets != null) ? insets.bottom : 0;
  2001. maxX = Math.max(0, splitPane.getSize().height -
  2002. (getSize().height + bottom));
  2003. }
  2004. if (maxX < minX) minX = maxX = 0;
  2005. }
  2006. }
  2007. /**
  2008. * Returns the y argument, since this is used for vertical
  2009. * splits.
  2010. */
  2011. protected int getNeededLocation(int x, int y) {
  2012. int newY;
  2013. newY = Math.min(maxX, Math.max(minX, y - offset));
  2014. return newY;
  2015. }
  2016. /**
  2017. * Returns the new position to put the divider at based on
  2018. * the passed in MouseEvent.
  2019. */
  2020. protected int positionForMouseEvent(MouseEvent e) {
  2021. int newY = (e.getSource() == Divider.this) ?
  2022. (e.getY() + getLocation().y) : e.getY();
  2023. newY = Math.min(maxX, Math.max(minX, newY - offset));
  2024. return newY;
  2025. }
  2026. } // End of Dividier.VerticalDragController
  2027. /**
  2028. * Used to layout a <code>Divider</code>.
  2029. * Layout for the divider
  2030. * involves appropriately moving the left/right buttons around.
  2031. * <p>
  2032. */
  2033. protected class DividerLayout implements LayoutManager {
  2034. public void layoutContainer(Container c) {
  2035. }
  2036. public Dimension minimumLayoutSize(Container c) {
  2037. // PENDING: resolve!
  2038. // NOTE: This isn't really used, refer to
  2039. // Divider.getPreferredSize for the reason.
  2040. // I leave it in hopes of having this used at some point.
  2041. if (c != Divider.this || splitPane == null) {
  2042. return new Dimension(0,0);
  2043. }
  2044. Insets insets = getInsets();
  2045. int width = getDividerSize();
  2046. int height = width;
  2047. if (orientation == JSplitPane.VERTICAL_SPLIT) {
  2048. if (insets != null) {
  2049. height += insets.top + insets.bottom;
  2050. }
  2051. width = 1;
  2052. }
  2053. else {
  2054. if (insets != null) {
  2055. width += insets.left + insets.right;
  2056. }
  2057. height = 1;
  2058. }
  2059. return new Dimension(width, height);
  2060. }
  2061. public Dimension preferredLayoutSize(Container c) {
  2062. return minimumLayoutSize(c);
  2063. }
  2064. public void removeLayoutComponent(Component c) {}
  2065. public void addLayoutComponent(String string, Component c) {}
  2066. } // End of class Divider.DividerLayout
  2067. }
  2068. }