Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / javax / swing / plaf / basic / BasicSplitPaneDivider.java
blob06d32984efb87e6637cac682006be96b47f5a5c2
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)
9 any later version.
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
19 02110-1301 USA.
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
24 combination.
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;
58 /**
59 * The divider that separates the two parts of a JSplitPane in the Basic look
60 * and feel.
62 * <p>
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
66 * functional.
67 * </p>
69 * @author Sascha Brawer (brawer_AT_dandelis.ch)
71 public class BasicSplitPaneDivider extends Container
72 implements PropertyChangeListener
74 /**
75 * Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1
76 * on MacOS X 10.1.5.
78 static final long serialVersionUID = 1463404307042803342L;
80 /**
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;
89 /**
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
92 * the divider.
94 protected DragController dragger;
96 /**
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>.
115 * <p>
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.
119 * </p>
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}.
146 * @see #getBorder()
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)
154 // 0 1 2
157 * Keeps track of where the divider should be placed when using one touch
158 * expand buttons.
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);
190 splitPane = null;
191 hiddenDivider = null;
194 /* Establish the connection to the new JSplitPane. */
195 splitPaneUI = newUI;
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()
217 return splitPaneUI;
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()
237 return dividerSize;
241 * Sets the border of this divider.
243 * @param border the new border. Typically, this will be an instance of
244 * {@link
245 * javax.swing.plaf.basic.BasicBorders.SplitPaneBorder}.
247 * @since 1.3
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
263 * set.
265 * @since 1.3
267 public Border getBorder()
269 return border;
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
276 * invoked.
278 * @see javax.swing.border.Border#getBorderInsets(java.awt.Component)
280 public Insets getInsets()
282 if (border != null)
283 return border.getBorderInsets(this);
284 else
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
312 * divider.
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())
325 layout();
326 repaint();
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;
342 super.paint(g);
343 if (border != null)
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();
365 add(leftButton);
366 add(rightButton);
368 leftButton.addMouseListener(mouseHandler);
369 rightButton.addMouseListener(mouseHandler);
371 // Set it to 1.
372 currentDividerLocation = 1;
374 else
376 if (leftButton != null && rightButton != null)
378 leftButton.removeMouseListener(mouseHandler);
379 rightButton.removeMouseListener(mouseHandler);
381 remove(leftButton);
382 remove(rightButton);
383 leftButton = null;
384 rightButton = null;
387 layout();
388 repaint();
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);
405 return button;
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);
421 return button;
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)
470 * most location.
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)
480 case 1:
481 splitPane.setDividerLocation(splitPane.getLastDividerLocation());
482 break;
483 case 0:
484 int top = (orientation == JSplitPane.HORIZONTAL_SPLIT) ? insets.left
485 : insets.top;
486 splitPane.setDividerLocation(top);
487 break;
488 case 2:
489 int bottom;
490 if (orientation == JSplitPane.HORIZONTAL_SPLIT)
491 bottom = splitPane.getBounds().width - insets.right - dividerSize;
492 else
493 bottom = splitPane.getBounds().height - insets.bottom - dividerSize;
494 splitPane.setDividerLocation(bottom);
495 break;
500 * The listener for handling mouse events from both the divider and the
501 * containing <code>JSplitPane</code>.
503 * <p>
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.
507 * </p>
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);
532 return;
534 else if (e.getSource() == rightButton)
536 currentDividerLocation++;
537 if (currentDividerLocation > 2)
538 currentDividerLocation = 2;
539 moveDividerTo(currentDividerLocation);
540 return;
543 isDragging = true;
544 currentDividerLocation = 1;
545 if (orientation == JSplitPane.HORIZONTAL_SPLIT)
546 dragger = new DragController(e);
547 else
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)
559 if (isDragging)
560 dragger.completeDrag(e);
561 isDragging = false;
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)
572 if (dragger != null)
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)
584 // Do nothing.
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
597 * divider location.
599 transient int offset;
602 * Creates a new DragController object.
604 * @param e The MouseEvent to initialize with.
606 protected DragController(MouseEvent e)
608 offset = e.getX();
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?
619 return true;
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)
645 return x;
649 * This method is called to pass on the drag information to the UI through
650 * dragDividerTo.
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)
657 if (isValid())
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)
669 if (isValid())
670 dragDividerTo(positionForMouseEvent(e));
674 * This method is called to finish the drag session by calling
675 * finishDraggingTo.
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
687 * finishDraggingTo.
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
698 * location.
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
713 * VERTICAL_SPLIT.
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)
724 super(e);
725 offset = e.getY();
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)
739 return 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
756 * location.
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)
790 // Do nothing.
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();
803 positionButtons();
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)
838 // Do nothing.
842 * This method changes the button orientation when the orientation of the
843 * SplitPane changes.
845 private void changeButtonOrientation()
847 if (orientation == JSplitPane.HORIZONTAL_SPLIT)
849 ((BasicArrowButton) rightButton).setDirection(SwingConstants.EAST);
850 ((BasicArrowButton) leftButton).setDirection(SwingConstants.WEST);
852 else
854 ((BasicArrowButton) rightButton).setDirection(SwingConstants.SOUTH);
855 ((BasicArrowButton) leftButton).setDirection(SwingConstants.NORTH);
860 * This method sizes and positions the buttons.
862 private void positionButtons()
864 int w = 0;
865 int h = 0;
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;
874 else
876 leftButton.setLocation(ONE_TOUCH_OFFSET, ONE_TOUCH_OFFSET);
877 rightButton.setLocation(ONE_TOUCH_OFFSET + 2 * ONE_TOUCH_SIZE,
878 ONE_TOUCH_OFFSET);
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);