1 /* BasicSplitPaneDivider.java --
2 Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax
.swing
.plaf
.basic
;
41 import java
.awt
.Component
;
42 import java
.awt
.Container
;
43 import java
.awt
.Dimension
;
44 import java
.awt
.Graphics
;
45 import java
.awt
.Insets
;
46 import java
.awt
.LayoutManager
;
47 import java
.awt
.event
.MouseAdapter
;
48 import java
.awt
.event
.MouseEvent
;
49 import java
.awt
.event
.MouseMotionListener
;
50 import java
.beans
.PropertyChangeEvent
;
51 import java
.beans
.PropertyChangeListener
;
53 import javax
.swing
.JButton
;
54 import javax
.swing
.JSplitPane
;
55 import javax
.swing
.SwingConstants
;
56 import javax
.swing
.border
.Border
;
59 * The divider that separates the two parts of a JSplitPane in the Basic look
63 * Implementation status: We do not have a real implementation yet. Currently,
64 * it is mostly a stub to allow compiling other parts of the
65 * javax.swing.plaf.basic package, although some parts are already
69 * @author Sascha Brawer (brawer_AT_dandelis.ch)
71 public class BasicSplitPaneDivider
extends Container
72 implements PropertyChangeListener
75 * Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1
78 static final long serialVersionUID
= 1463404307042803342L;
81 * The width and height of the little buttons for showing and hiding parts
82 * of a JSplitPane in a single mouse click.
84 protected static final int ONE_TOUCH_SIZE
= 6;
86 /** The distance the one touch buttons will sit from the divider's edges. */
87 protected static final int ONE_TOUCH_OFFSET
= 2;
90 * An object that performs the tasks associated with an ongoing drag
91 * operation, or <code>null</code> if the user is currently not dragging
94 protected DragController dragger
;
97 * The delegate object that is responsible for the UI of the
98 * <code>JSplitPane</code> that contains this divider.
100 protected BasicSplitPaneUI splitPaneUI
;
102 /** The thickness of the divider in pixels. */
103 protected int dividerSize
;
105 /** A divider that is used for layout purposes. */
106 protected Component hiddenDivider
;
108 /** The JSplitPane containing this divider. */
109 protected JSplitPane splitPane
;
112 * The listener for handling mouse events from both the divider and the
113 * containing <code>JSplitPane</code>.
116 * The reason for also handling MouseEvents from the containing
117 * <code>JSplitPane</code> is that users should be able to start a drag
118 * gesture from inside the JSplitPane, but slightly outisde the divider.
121 protected MouseHandler mouseHandler
= new MouseHandler();
124 * The current orientation of the containing <code>JSplitPane</code>, which
125 * is either {@link javax.swing.JSplitPane#HORIZONTAL_SPLIT} or {@link
126 * javax.swing.JSplitPane#VERTICAL_SPLIT}.
128 protected int orientation
;
131 * The button for showing and hiding the left (or top) component of the
132 * <code>JSplitPane</code>.
134 protected JButton leftButton
;
137 * The button for showing and hiding the right (or bottom) component of the
138 * <code>JSplitPane</code>.
140 protected JButton rightButton
;
143 * The border of this divider. Typically, this will be an instance of {@link
144 * javax.swing.plaf.basic.BasicBorders.SplitPaneDividerBorder}.
147 * @see #setBorder(javax.swing.border.Border)
149 private Border border
;
151 // This is not a pixel count.
152 // This int should be able to take 3 values.
153 // left (top), middle, right(bottom)
157 * Keeps track of where the divider should be placed when using one touch
159 * This is package-private to avoid an accessor method.
161 transient int currentDividerLocation
= 1;
164 * Constructs a new divider.
166 * @param ui the UI delegate of the enclosing <code>JSplitPane</code>.
168 public BasicSplitPaneDivider(BasicSplitPaneUI ui
)
170 setLayout(new DividerLayout());
171 setBasicSplitPaneUI(ui
);
172 setDividerSize(splitPane
.getDividerSize());
176 * Sets the delegate object that is responsible for the UI of the {@link
177 * javax.swing.JSplitPane} containing this divider.
179 * @param newUI the UI delegate, or <code>null</code> to release the
180 * connection to the current delegate.
182 public void setBasicSplitPaneUI(BasicSplitPaneUI newUI
)
184 /* Remove the connection to the existing JSplitPane. */
185 if (splitPane
!= null)
187 splitPane
.removePropertyChangeListener(this);
188 removeMouseListener(mouseHandler
);
189 removeMouseMotionListener(mouseHandler
);
191 hiddenDivider
= null;
194 /* Establish the connection to the new JSplitPane. */
196 if (splitPaneUI
!= null)
197 splitPane
= newUI
.getSplitPane();
198 if (splitPane
!= null)
200 splitPane
.addPropertyChangeListener(this);
201 addMouseListener(mouseHandler
);
202 addMouseMotionListener(mouseHandler
);
203 hiddenDivider
= splitPaneUI
.getNonContinuousLayoutDivider();
204 orientation
= splitPane
.getOrientation();
205 oneTouchExpandableChanged();
210 * Returns the delegate object that is responsible for the UI of the {@link
211 * javax.swing.JSplitPane} containing this divider.
213 * @return The UI for the JSplitPane.
215 public BasicSplitPaneUI
getBasicSplitPaneUI()
221 * Sets the thickness of the divider.
223 * @param newSize the new width or height in pixels.
225 public void setDividerSize(int newSize
)
227 this.dividerSize
= newSize
;
231 * Retrieves the thickness of the divider.
233 * @return The thickness of the divider.
235 public int getDividerSize()
241 * Sets the border of this divider.
243 * @param border the new border. Typically, this will be an instance of
245 * javax.swing.plaf.basic.BasicBorders.SplitPaneBorder}.
249 public void setBorder(Border border
)
251 if (border
!= this.border
)
253 Border oldValue
= this.border
;
254 this.border
= border
;
255 firePropertyChange("border", oldValue
, border
);
260 * Retrieves the border of this divider.
262 * @return the current border, or <code>null</code> if no border has been
267 public Border
getBorder()
273 * Retrieves the insets of the divider. If a border has been installed on
274 * the divider, the result of calling its <code>getBorderInsets</code>
275 * method is returned. Otherwise, the inherited implementation will be
278 * @see javax.swing.border.Border#getBorderInsets(java.awt.Component)
280 public Insets
getInsets()
283 return border
.getBorderInsets(this);
285 return super.getInsets();
289 * Returns the preferred size of this divider, which is
290 * <code>dividerSize</code> by <code>dividerSize</code> pixels.
292 * @return The preferred size of the divider.
294 public Dimension
getPreferredSize()
296 return getLayout().preferredLayoutSize(this);
300 * Returns the minimal size of this divider, which is
301 * <code>dividerSize</code> by <code>dividerSize</code> pixels.
303 * @return The minimal size of the divider.
305 public Dimension
getMinimumSize()
307 return getPreferredSize();
311 * Processes events from the <code>JSplitPane</code> that contains this
314 * @param e The PropertyChangeEvent.
316 public void propertyChange(PropertyChangeEvent e
)
318 if (e
.getPropertyName().equals(JSplitPane
.ONE_TOUCH_EXPANDABLE_PROPERTY
))
319 oneTouchExpandableChanged();
320 else if (e
.getPropertyName().equals(JSplitPane
.ORIENTATION_PROPERTY
))
322 orientation
= splitPane
.getOrientation();
323 if (splitPane
.isOneTouchExpandable())
329 else if (e
.getPropertyName().equals(JSplitPane
.DIVIDER_SIZE_PROPERTY
))
330 dividerSize
= splitPane
.getDividerSize();
334 * Paints the divider by painting its border.
336 * @param g The Graphics Object to paint with.
338 public void paint(Graphics g
)
340 Dimension dividerSize
;
345 dividerSize
= getSize();
346 border
.paintBorder(this, g
, 0, 0, dividerSize
.width
, dividerSize
.height
);
348 if (splitPane
.isOneTouchExpandable())
350 ((BasicArrowButton
) rightButton
).paint(g
);
351 ((BasicArrowButton
) leftButton
).paint(g
);
356 * Reacts to changes of the <code>oneToughExpandable</code> property of the
357 * containing <code>JSplitPane</code>.
359 protected void oneTouchExpandableChanged()
361 if (splitPane
.isOneTouchExpandable())
363 leftButton
= createLeftOneTouchButton();
364 rightButton
= createRightOneTouchButton();
368 leftButton
.addMouseListener(mouseHandler
);
369 rightButton
.addMouseListener(mouseHandler
);
372 currentDividerLocation
= 1;
376 if (leftButton
!= null && rightButton
!= null)
378 leftButton
.removeMouseListener(mouseHandler
);
379 rightButton
.removeMouseListener(mouseHandler
);
392 * Creates a button for showing and hiding the left (or top) part of a
393 * <code>JSplitPane</code>.
395 * @return The left one touch button.
397 protected JButton
createLeftOneTouchButton()
399 int dir
= SwingConstants
.WEST
;
400 if (orientation
== JSplitPane
.VERTICAL_SPLIT
)
401 dir
= SwingConstants
.NORTH
;
402 JButton button
= new BasicArrowButton(dir
);
403 button
.setBorder(null);
409 * Creates a button for showing and hiding the right (or bottom) part of a
410 * <code>JSplitPane</code>.
412 * @return The right one touch button.
414 protected JButton
createRightOneTouchButton()
416 int dir
= SwingConstants
.EAST
;
417 if (orientation
== JSplitPane
.VERTICAL_SPLIT
)
418 dir
= SwingConstants
.SOUTH
;
419 JButton button
= new BasicArrowButton(dir
);
420 button
.setBorder(null);
425 * Prepares the divider for dragging by calling the
426 * <code>startDragging</code> method of the UI delegate of the enclosing
427 * <code>JSplitPane</code>.
429 * @see BasicSplitPaneUI#startDragging()
431 protected void prepareForDragging()
433 if (splitPaneUI
!= null)
434 splitPaneUI
.startDragging();
438 * Drags the divider to a given location by calling the
439 * <code>dragDividerTo</code> method of the UI delegate of the enclosing
440 * <code>JSplitPane</code>.
442 * @param location the new location of the divider.
444 * @see BasicSplitPaneUI#dragDividerTo(int location)
446 protected void dragDividerTo(int location
)
448 if (splitPaneUI
!= null)
449 splitPaneUI
.dragDividerTo(location
);
453 * Finishes a dragging gesture by calling the <code>finishDraggingTo</code>
454 * method of the UI delegate of the enclosing <code>JSplitPane</code>.
456 * @param location the new, final location of the divider.
458 * @see BasicSplitPaneUI#finishDraggingTo(int location)
460 protected void finishDraggingTo(int location
)
462 if (splitPaneUI
!= null)
463 splitPaneUI
.finishDraggingTo(location
);
467 * This helper method moves the divider to one of the three locations when
468 * using one touch expand buttons. Location 0 is the left (or top) most
469 * location. Location 1 is the middle. Location 2 is the right (or bottom)
471 * This is package-private to avoid an accessor method.
473 * @param locationIndex The location to move to.
475 void moveDividerTo(int locationIndex
)
477 Insets insets
= splitPane
.getInsets();
478 switch (locationIndex
)
481 splitPane
.setDividerLocation(splitPane
.getLastDividerLocation());
484 int top
= (orientation
== JSplitPane
.HORIZONTAL_SPLIT
) ? insets
.left
486 splitPane
.setDividerLocation(top
);
490 if (orientation
== JSplitPane
.HORIZONTAL_SPLIT
)
491 bottom
= splitPane
.getBounds().width
- insets
.right
- dividerSize
;
493 bottom
= splitPane
.getBounds().height
- insets
.bottom
- dividerSize
;
494 splitPane
.setDividerLocation(bottom
);
500 * The listener for handling mouse events from both the divider and the
501 * containing <code>JSplitPane</code>.
504 * The reason for also handling MouseEvents from the containing
505 * <code>JSplitPane</code> is that users should be able to start a drag
506 * gesture from inside the JSplitPane, but slightly outisde the divider.
509 * @author Sascha Brawer (brawer_AT_dandelis.ch)
511 protected class MouseHandler
extends MouseAdapter
512 implements MouseMotionListener
514 /** Keeps track of whether a drag is occurring. */
515 private transient boolean isDragging
;
518 * This method is called when the mouse is pressed.
520 * @param e The MouseEvent.
522 public void mousePressed(MouseEvent e
)
524 if (splitPane
.isOneTouchExpandable())
526 if (e
.getSource() == leftButton
)
528 currentDividerLocation
--;
529 if (currentDividerLocation
< 0)
530 currentDividerLocation
= 0;
531 moveDividerTo(currentDividerLocation
);
534 else if (e
.getSource() == rightButton
)
536 currentDividerLocation
++;
537 if (currentDividerLocation
> 2)
538 currentDividerLocation
= 2;
539 moveDividerTo(currentDividerLocation
);
544 currentDividerLocation
= 1;
545 if (orientation
== JSplitPane
.HORIZONTAL_SPLIT
)
546 dragger
= new DragController(e
);
548 dragger
= new VerticalDragController(e
);
549 prepareForDragging();
553 * This method is called when the mouse is released.
555 * @param e The MouseEvent.
557 public void mouseReleased(MouseEvent e
)
560 dragger
.completeDrag(e
);
565 * Repeatedly invoked when the user is dragging the mouse cursor while
566 * having pressed a mouse button.
568 * @param e The MouseEvent.
570 public void mouseDragged(MouseEvent e
)
573 dragger
.continueDrag(e
);
577 * Repeatedly invoked when the user is dragging the mouse cursor without
578 * having pressed a mouse button.
580 * @param e The MouseEvent.
582 public void mouseMoved(MouseEvent e
)
589 * Performs the tasks associated with an ongoing drag operation.
591 * @author Sascha Brawer (brawer_AT_dandelis.ch)
593 protected class DragController
596 * The difference between where the mouse is clicked and the initial
599 transient int offset
;
602 * Creates a new DragController object.
604 * @param e The MouseEvent to initialize with.
606 protected DragController(MouseEvent e
)
612 * This method returns true if the divider can move.
614 * @return True if dragging is allowed.
616 protected boolean isValid()
618 // Views can always be resized?
623 * Returns a position for the divider given the MouseEvent.
625 * @param e MouseEvent.
627 * @return The position for the divider to move to.
629 protected int positionForMouseEvent(MouseEvent e
)
631 return e
.getX() + getX() - offset
;
635 * This method returns one of the two paramters for the orientation. In
636 * this case, it returns x.
638 * @param x The x coordinate.
639 * @param y The y coordinate.
641 * @return The x coordinate.
643 protected int getNeededLocation(int x
, int y
)
649 * This method is called to pass on the drag information to the UI through
652 * @param newX The x coordinate of the MouseEvent.
653 * @param newY The y coordinate of the MouseEvent.
655 protected void continueDrag(int newX
, int newY
)
658 dragDividerTo(adjust(newX
, newY
));
662 * This method is called to pass on the drag information to the UI
663 * through dragDividerTo.
665 * @param e The MouseEvent.
667 protected void continueDrag(MouseEvent e
)
670 dragDividerTo(positionForMouseEvent(e
));
674 * This method is called to finish the drag session by calling
677 * @param x The x coordinate of the MouseEvent.
678 * @param y The y coordinate of the MouseEvent.
680 protected void completeDrag(int x
, int y
)
682 finishDraggingTo(adjust(x
, y
));
686 * This method is called to finish the drag session by calling
689 * @param e The MouseEvent.
691 protected void completeDrag(MouseEvent e
)
693 finishDraggingTo(positionForMouseEvent(e
));
697 * This is a helper method that includes the offset in the needed
700 * @param x The x coordinate of the MouseEvent.
701 * @param y The y coordinate of the MouseEvent.
703 * @return The needed location adjusted by the offsets.
705 int adjust(int x
, int y
)
707 return getNeededLocation(x
, y
) + getX() - offset
;
712 * This is a helper class that controls dragging when the orientation is
715 protected class VerticalDragController
extends DragController
718 * Creates a new VerticalDragController object.
720 * @param e The MouseEvent to initialize with.
722 protected VerticalDragController(MouseEvent e
)
729 * This method returns one of the two parameters given the orientation. In
730 * this case, it returns y.
732 * @param x The x coordinate of the MouseEvent.
733 * @param y The y coordinate of the MouseEvent.
735 * @return The y coordinate.
737 protected int getNeededLocation(int x
, int y
)
743 * This method returns the new location of the divider given a MouseEvent.
745 * @param e The MouseEvent.
747 * @return The new location of the divider.
749 protected int positionForMouseEvent(MouseEvent e
)
751 return e
.getY() + getY() - offset
;
755 * This is a helper method that includes the offset in the needed
758 * @param x The x coordinate of the MouseEvent.
759 * @param y The y coordinate of the MouseEvent.
761 * @return The needed location adjusted by the offsets.
763 int adjust(int x
, int y
)
765 return getNeededLocation(x
, y
) + getY() - offset
;
770 * This helper class acts as the Layout Manager for the divider.
772 protected class DividerLayout
implements LayoutManager
775 * Creates a new DividerLayout object.
777 protected DividerLayout()
779 // Nothing to do here.
783 * This method is called when a Component is added.
785 * @param string The constraints string.
786 * @param c The Component to add.
788 public void addLayoutComponent(String string
, Component c
)
794 * This method is called to lay out the container.
796 * @param c The container to lay out.
798 public void layoutContainer(Container c
)
800 if (splitPane
.isOneTouchExpandable())
802 changeButtonOrientation();
808 * This method returns the minimum layout size.
810 * @param c The container to calculate for.
812 * @return The minimum layout size.
814 public Dimension
minimumLayoutSize(Container c
)
816 return preferredLayoutSize(c
);
820 * This method returns the preferred layout size.
822 * @param c The container to calculate for.
824 * @return The preferred layout size.
826 public Dimension
preferredLayoutSize(Container c
)
828 return new Dimension(dividerSize
, dividerSize
);
832 * This method is called when a component is removed.
834 * @param c The component to remove.
836 public void removeLayoutComponent(Component c
)
842 * This method changes the button orientation when the orientation of the
845 private void changeButtonOrientation()
847 if (orientation
== JSplitPane
.HORIZONTAL_SPLIT
)
849 ((BasicArrowButton
) rightButton
).setDirection(SwingConstants
.EAST
);
850 ((BasicArrowButton
) leftButton
).setDirection(SwingConstants
.WEST
);
854 ((BasicArrowButton
) rightButton
).setDirection(SwingConstants
.SOUTH
);
855 ((BasicArrowButton
) leftButton
).setDirection(SwingConstants
.NORTH
);
860 * This method sizes and positions the buttons.
862 private void positionButtons()
866 if (orientation
== JSplitPane
.HORIZONTAL_SPLIT
)
868 rightButton
.setLocation(ONE_TOUCH_OFFSET
, ONE_TOUCH_OFFSET
);
869 leftButton
.setLocation(ONE_TOUCH_OFFSET
,
870 ONE_TOUCH_OFFSET
+ 2 * ONE_TOUCH_SIZE
);
871 w
= dividerSize
- 2 * ONE_TOUCH_OFFSET
;
872 h
= 2 * ONE_TOUCH_SIZE
;
876 leftButton
.setLocation(ONE_TOUCH_OFFSET
, ONE_TOUCH_OFFSET
);
877 rightButton
.setLocation(ONE_TOUCH_OFFSET
+ 2 * ONE_TOUCH_SIZE
,
879 h
= dividerSize
- 2 * ONE_TOUCH_OFFSET
;
880 w
= 2 * ONE_TOUCH_SIZE
;
882 Dimension dims
= new Dimension(w
, h
);
883 leftButton
.setSize(dims
);
884 rightButton
.setSize(dims
);