Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / libjava / javax / swing / plaf / basic / BasicSplitPaneDivider.java
blob987f86bdb1be1b49a3053ae10c9cd5fde9a3dadc
1 /* BasicSplitPaneDivider.java --
2 Copyright (C) 2003, 2004 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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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.Color;
42 import java.awt.Component;
43 import java.awt.Container;
44 import java.awt.Dimension;
45 import java.awt.Graphics;
46 import java.awt.Insets;
47 import java.awt.LayoutManager;
48 import java.awt.event.MouseAdapter;
49 import java.awt.event.MouseEvent;
50 import java.awt.event.MouseMotionListener;
51 import java.beans.PropertyChangeEvent;
52 import java.beans.PropertyChangeListener;
54 import javax.swing.JButton;
55 import javax.swing.JSplitPane;
56 import javax.swing.SwingConstants;
57 import javax.swing.border.Border;
59 /**
60 * The divider that separates the two parts of a JSplitPane in the Basic look
61 * and feel.
63 * <p>
64 * Implementation status: We do not have a real implementation yet. Currently,
65 * it is mostly a stub to allow compiling other parts of the
66 * javax.swing.plaf.basic package, although some parts are already
67 * functional.
68 * </p>
70 * @author Sascha Brawer (brawer_AT_dandelis.ch)
72 public class BasicSplitPaneDivider extends Container
73 implements PropertyChangeListener
75 /**
76 * Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1
77 * on MacOS X 10.1.5.
79 static final long serialVersionUID = 1463404307042803342L;
81 /**
82 * The width and height of the little buttons for showing and hiding parts
83 * of a JSplitPane in a single mouse click.
85 protected static final int ONE_TOUCH_SIZE = 6;
87 /** The distance the one touch buttons will sit from the divider's edges. */
88 protected static final int ONE_TOUCH_OFFSET = 2;
90 /**
91 * An object that performs the tasks associated with an ongoing drag
92 * operation, or <code>null</code> if the user is currently not dragging
93 * the divider.
95 protected DragController dragger;
97 /**
98 * The delegate object that is responsible for the UI of the
99 * <code>JSplitPane</code> that contains this divider.
101 protected BasicSplitPaneUI splitPaneUI;
103 /** The thickness of the divider in pixels. */
104 protected int dividerSize;
106 /** A divider that is used for layout purposes. */
107 protected Component hiddenDivider;
109 /** The JSplitPane containing this divider. */
110 protected JSplitPane splitPane;
113 * The listener for handling mouse events from both the divider and the
114 * containing <code>JSplitPane</code>.
116 * <p>
117 * The reason for also handling MouseEvents from the containing
118 * <code>JSplitPane</code> is that users should be able to start a drag
119 * gesture from inside the JSplitPane, but slightly outisde the divider.
120 * </p>
122 protected MouseHandler mouseHandler = new MouseHandler();
125 * The current orientation of the containing <code>JSplitPane</code>, which
126 * is either {@link javax.swing.JSplitPane#HORIZONTAL_SPLIT} or {@link
127 * javax.swing.JSplitPane#VERTICAL_SPLIT}.
129 protected int orientation;
132 * The button for showing and hiding the left (or top) component of the
133 * <code>JSplitPane</code>.
135 protected JButton leftButton;
138 * The button for showing and hiding the right (or bottom) component of the
139 * <code>JSplitPane</code>.
141 protected JButton rightButton;
144 * The border of this divider. Typically, this will be an instance of {@link
145 * javax.swing.plaf.basic.BasicBorders.SplitPaneDividerBorder}.
147 * @see #getBorder()
148 * @see #setBorder(javax.swing.border.Border)
150 private Border border;
152 // This is not a pixel count.
153 // This int should be able to take 3 values.
154 // left (top), middle, right(bottom)
155 // 0 1 2
158 * Keeps track of where the divider should be placed when using one touch
159 * expand buttons.
161 private transient int currentDividerLocation = 1;
163 /** DOCUMENT ME! */
164 private transient Border tmpBorder = new Border()
166 public Insets getBorderInsets(Component c)
168 return new Insets(2, 2, 2, 2);
171 public boolean isBorderOpaque()
173 return false;
176 public void paintBorder(Component c, Graphics g, int x, int y,
177 int width, int height)
179 Color saved = g.getColor();
180 g.setColor(Color.BLACK);
182 g.drawRect(x + 2, y + 2, width - 4, height - 4);
184 g.setColor(saved);
189 * Constructs a new divider.
191 * @param ui the UI delegate of the enclosing <code>JSplitPane</code>.
193 public BasicSplitPaneDivider(BasicSplitPaneUI ui)
195 setLayout(new DividerLayout());
196 setBasicSplitPaneUI(ui);
197 setDividerSize(splitPane.getDividerSize());
198 setBorder(tmpBorder);
202 * Sets the delegate object that is responsible for the UI of the {@link
203 * javax.swing.JSplitPane} containing this divider.
205 * @param newUI the UI delegate, or <code>null</code> to release the
206 * connection to the current delegate.
208 public void setBasicSplitPaneUI(BasicSplitPaneUI newUI)
210 /* Remove the connection to the existing JSplitPane. */
211 if (splitPane != null)
213 splitPane.removePropertyChangeListener(this);
214 splitPane.removeMouseListener(mouseHandler);
215 splitPane.removeMouseMotionListener(mouseHandler);
216 removeMouseListener(mouseHandler);
217 removeMouseMotionListener(mouseHandler);
218 splitPane = null;
219 hiddenDivider = null;
222 /* Establish the connection to the new JSplitPane. */
223 splitPaneUI = newUI;
224 if (splitPaneUI != null)
225 splitPane = newUI.getSplitPane();
226 if (splitPane != null)
228 splitPane.addPropertyChangeListener(this);
229 splitPane.addMouseListener(mouseHandler);
230 splitPane.addMouseMotionListener(mouseHandler);
231 addMouseListener(mouseHandler);
232 addMouseMotionListener(mouseHandler);
233 hiddenDivider = splitPaneUI.getNonContinuousLayoutDivider();
234 orientation = splitPane.getOrientation();
235 oneTouchExpandableChanged();
240 * Returns the delegate object that is responsible for the UI of the {@link
241 * javax.swing.JSplitPane} containing this divider.
243 * @return The UI for the JSplitPane.
245 public BasicSplitPaneUI getBasicSplitPaneUI()
247 return splitPaneUI;
251 * Sets the thickness of the divider.
253 * @param newSize the new width or height in pixels.
255 public void setDividerSize(int newSize)
257 this.dividerSize = newSize;
261 * Retrieves the thickness of the divider.
263 * @return The thickness of the divider.
265 public int getDividerSize()
267 return dividerSize;
271 * Sets the border of this divider.
273 * @param border the new border. Typically, this will be an instance of
274 * {@link
275 * javax.swing.plaf.basic.BasicBorders.SplitPaneDividerBorder}.
277 * @since 1.3
279 public void setBorder(Border border)
281 if (border != this.border)
283 Border oldValue = this.border;
284 this.border = border;
285 firePropertyChange("border", oldValue, border);
290 * Retrieves the border of this divider.
292 * @return the current border, or <code>null</code> if no border has been
293 * set.
295 * @since 1.3
297 public Border getBorder()
299 return border;
303 * Retrieves the insets of the divider. If a border has been installed on
304 * the divider, the result of calling its <code>getBorderInsets</code>
305 * method is returned. Otherwise, the inherited implementation will be
306 * invoked.
308 * @see javax.swing.border.Border#getBorderInsets(java.awt.Component)
310 public Insets getInsets()
312 if (border != null)
313 return border.getBorderInsets(this);
314 else
315 return super.getInsets();
319 * Returns the preferred size of this divider, which is
320 * <code>dividerSize</code> by <code>dividerSize</code> pixels.
322 * @return The preferred size of the divider.
324 public Dimension getPreferredSize()
326 return getLayout().preferredLayoutSize(this);
330 * Returns the minimal size of this divider, which is
331 * <code>dividerSize</code> by <code>dividerSize</code> pixels.
333 * @return The minimal size of the divider.
335 public Dimension getMinimumSize()
337 return getPreferredSize();
341 * Processes events from the <code>JSplitPane</code> that contains this
342 * divider.
344 * @param e The PropertyChangeEvent.
346 public void propertyChange(PropertyChangeEvent e)
348 if (e.getPropertyName().equals(JSplitPane.ONE_TOUCH_EXPANDABLE_PROPERTY))
349 oneTouchExpandableChanged();
350 else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY))
352 orientation = splitPane.getOrientation();
353 if (splitPane.isOneTouchExpandable())
355 layout();
356 repaint();
359 else if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY))
360 dividerSize = splitPane.getDividerSize();
364 * Paints the divider by painting its border.
366 * @param g The Graphics Object to paint with.
368 public void paint(Graphics g)
370 Dimension dividerSize;
372 super.paint(g);
373 if (border != null)
375 dividerSize = getSize();
376 border.paintBorder(this, g, 0, 0, dividerSize.width, dividerSize.height);
381 * Reacts to changes of the <code>oneToughExpandable</code> property of the
382 * containing <code>JSplitPane</code>.
384 protected void oneTouchExpandableChanged()
386 if (splitPane.isOneTouchExpandable())
388 leftButton = createLeftOneTouchButton();
389 rightButton = createRightOneTouchButton();
390 add(leftButton);
391 add(rightButton);
393 leftButton.addMouseListener(mouseHandler);
394 rightButton.addMouseListener(mouseHandler);
396 // Set it to 1.
397 currentDividerLocation = 1;
399 else
401 if (leftButton != null && rightButton != null)
403 leftButton.removeMouseListener(mouseHandler);
404 rightButton.removeMouseListener(mouseHandler);
406 remove(leftButton);
407 remove(rightButton);
408 leftButton = null;
409 rightButton = null;
412 layout();
413 repaint();
417 * Creates a button for showing and hiding the left (or top) part of a
418 * <code>JSplitPane</code>.
420 * @return The left one touch button.
422 protected JButton createLeftOneTouchButton()
424 int dir = SwingConstants.WEST;
425 if (orientation == JSplitPane.VERTICAL_SPLIT)
426 dir = SwingConstants.NORTH;
427 JButton button = new BasicArrowButton(dir);
428 button.setBorder(null);
430 return button;
434 * Creates a button for showing and hiding the right (or bottom) part of a
435 * <code>JSplitPane</code>.
437 * @return The right one touch button.
439 protected JButton createRightOneTouchButton()
441 int dir = SwingConstants.EAST;
442 if (orientation == JSplitPane.VERTICAL_SPLIT)
443 dir = SwingConstants.SOUTH;
444 JButton button = new BasicArrowButton(dir);
445 button.setBorder(null);
446 return button;
450 * Prepares the divider for dragging by calling the
451 * <code>startDragging</code> method of the UI delegate of the enclosing
452 * <code>JSplitPane</code>.
454 * @see BasicSplitPaneUI#startDragging()
456 protected void prepareForDragging()
458 if (splitPaneUI != null)
459 splitPaneUI.startDragging();
463 * Drags the divider to a given location by calling the
464 * <code>dragDividerTo</code> method of the UI delegate of the enclosing
465 * <code>JSplitPane</code>.
467 * @param location the new location of the divider.
469 * @see BasicSplitPaneUI#dragDividerTo(int location)
471 protected void dragDividerTo(int location)
473 if (splitPaneUI != null)
474 splitPaneUI.dragDividerTo(location);
478 * Finishes a dragging gesture by calling the <code>finishDraggingTo</code>
479 * method of the UI delegate of the enclosing <code>JSplitPane</code>.
481 * @param location the new, final location of the divider.
483 * @see BasicSplitPaneUI#finishDraggingTo(int location)
485 protected void finishDraggingTo(int location)
487 if (splitPaneUI != null)
488 splitPaneUI.finishDraggingTo(location);
492 * This helper method moves the divider to one of the three locations when
493 * using one touch expand buttons. Location 0 is the left (or top) most
494 * location. Location 1 is the middle. Location 2 is the right (or bottom)
495 * most location.
497 * @param locationIndex The location to move to.
499 private void moveDividerTo(int locationIndex)
501 Insets insets = splitPane.getInsets();
502 switch (locationIndex)
504 case 1:
505 splitPane.setDividerLocation(splitPane.getLastDividerLocation());
506 break;
507 case 0:
508 int top = (orientation == JSplitPane.HORIZONTAL_SPLIT) ? insets.left
509 : insets.top;
510 splitPane.setDividerLocation(top);
511 break;
512 case 2:
513 int bottom;
514 if (orientation == JSplitPane.HORIZONTAL_SPLIT)
515 bottom = splitPane.getBounds().width - insets.right - dividerSize;
516 else
517 bottom = splitPane.getBounds().height - insets.bottom - dividerSize;
518 splitPane.setDividerLocation(bottom);
519 break;
524 * The listener for handling mouse events from both the divider and the
525 * containing <code>JSplitPane</code>.
527 * <p>
528 * The reason for also handling MouseEvents from the containing
529 * <code>JSplitPane</code> is that users should be able to start a drag
530 * gesture from inside the JSplitPane, but slightly outisde the divider.
531 * </p>
533 * @author Sascha Brawer (brawer_AT_dandelis.ch)
535 protected class MouseHandler extends MouseAdapter
536 implements MouseMotionListener
538 /** Keeps track of whether a drag is occurring. */
539 private transient boolean isDragging;
542 * This method is called when the mouse is pressed.
544 * @param e The MouseEvent.
546 public void mousePressed(MouseEvent e)
548 if (splitPane.isOneTouchExpandable())
550 if (e.getSource() == leftButton)
552 currentDividerLocation--;
553 if (currentDividerLocation < 0)
554 currentDividerLocation = 0;
555 moveDividerTo(currentDividerLocation);
556 return;
558 else if (e.getSource() == rightButton)
560 currentDividerLocation++;
561 if (currentDividerLocation > 2)
562 currentDividerLocation = 2;
563 moveDividerTo(currentDividerLocation);
564 return;
567 isDragging = true;
568 currentDividerLocation = 1;
569 if (orientation == JSplitPane.HORIZONTAL_SPLIT)
570 dragger = new DragController(e);
571 else
572 dragger = new VerticalDragController(e);
573 prepareForDragging();
577 * This method is called when the mouse is released.
579 * @param e The MouseEvent.
581 public void mouseReleased(MouseEvent e)
583 if (isDragging)
584 dragger.completeDrag(e);
585 isDragging = false;
589 * Repeatedly invoked when the user is dragging the mouse cursor while
590 * having pressed a mouse button.
592 * @param e The MouseEvent.
594 public void mouseDragged(MouseEvent e)
596 if (dragger != null)
597 dragger.continueDrag(e);
601 * Repeatedly invoked when the user is dragging the mouse cursor without
602 * having pressed a mouse button.
604 * @param e The MouseEvent.
606 public void mouseMoved(MouseEvent e)
608 // Do nothing.
613 * Performs the tasks associated with an ongoing drag operation.
615 * @author Sascha Brawer (brawer_AT_dandelis.ch)
617 protected class DragController
620 * The difference between where the mouse is clicked and the initial
621 * divider location.
623 transient int offset;
626 * Creates a new DragController object.
628 * @param e The MouseEvent to initialize with.
630 protected DragController(MouseEvent e)
632 offset = e.getX();
636 * This method returns true if the divider can move.
638 * @return True if dragging is allowed.
640 protected boolean isValid()
642 // Views can always be resized?
643 return true;
647 * Returns a position for the divider given the MouseEvent.
649 * @param e MouseEvent.
651 * @return The position for the divider to move to.
653 protected int positionForMouseEvent(MouseEvent e)
655 return e.getX() + getX() - offset;
659 * This method returns one of the two paramters for the orientation. In
660 * this case, it returns x.
662 * @param x The x coordinate.
663 * @param y The y coordinate.
665 * @return The x coordinate.
667 protected int getNeededLocation(int x, int y)
669 return x;
673 * This method is called to pass on the drag information to the UI through
674 * dragDividerTo.
676 * @param newX The x coordinate of the MouseEvent.
677 * @param newY The y coordinate of the MouseEvent.
679 protected void continueDrag(int newX, int newY)
681 if (isValid())
682 dragDividerTo(adjust(newX, newY));
686 * This method is called to pass on the drag information to the UI
687 * through dragDividerTo.
689 * @param e The MouseEvent.
691 protected void continueDrag(MouseEvent e)
693 if (isValid())
694 dragDividerTo(positionForMouseEvent(e));
698 * This method is called to finish the drag session by calling
699 * finishDraggingTo.
701 * @param x The x coordinate of the MouseEvent.
702 * @param y The y coordinate of the MouseEvent.
704 protected void completeDrag(int x, int y)
706 finishDraggingTo(adjust(x, y));
710 * This method is called to finish the drag session by calling
711 * finishDraggingTo.
713 * @param e The MouseEvent.
715 protected void completeDrag(MouseEvent e)
717 finishDraggingTo(positionForMouseEvent(e));
721 * This is a helper method that includes the offset in the needed
722 * location.
724 * @param x The x coordinate of the MouseEvent.
725 * @param y The y coordinate of the MouseEvent.
727 * @return The needed location adjusted by the offsets.
729 int adjust(int x, int y)
731 return getNeededLocation(x, y) + getX() - offset;
736 * This is a helper class that controls dragging when the orientation is
737 * VERTICAL_SPLIT.
739 protected class VerticalDragController extends DragController
742 * Creates a new VerticalDragController object.
744 * @param e The MouseEvent to initialize with.
746 protected VerticalDragController(MouseEvent e)
748 super(e);
749 offset = e.getY();
753 * This method returns one of the two parameters given the orientation. In
754 * this case, it returns y.
756 * @param x The x coordinate of the MouseEvent.
757 * @param y The y coordinate of the MouseEvent.
759 * @return The y coordinate.
761 protected int getNeededLocation(int x, int y)
763 return y;
767 * This method returns the new location of the divider given a MouseEvent.
769 * @param e The MouseEvent.
771 * @return The new location of the divider.
773 protected int positionForMouseEvent(MouseEvent e)
775 return e.getY() + getY() - offset;
779 * This is a helper method that includes the offset in the needed
780 * location.
782 * @param x The x coordinate of the MouseEvent.
783 * @param y The y coordinate of the MouseEvent.
785 * @return The needed location adjusted by the offsets.
787 int adjust(int x, int y)
789 return getNeededLocation(x, y) + getY() - offset;
794 * This helper class acts as the Layout Manager for the divider.
796 protected class DividerLayout implements LayoutManager
799 * Creates a new DividerLayout object.
801 protected DividerLayout()
806 * This method is called when a Component is added.
808 * @param string The constraints string.
809 * @param c The Component to add.
811 public void addLayoutComponent(String string, Component c)
813 // Do nothing.
817 * This method is called to lay out the container.
819 * @param c The container to lay out.
821 public void layoutContainer(Container c)
823 if (splitPane.isOneTouchExpandable())
825 changeButtonOrientation();
826 positionButtons();
831 * This method returns the minimum layout size.
833 * @param c The container to calculate for.
835 * @return The minimum layout size.
837 public Dimension minimumLayoutSize(Container c)
839 return preferredLayoutSize(c);
843 * This method returns the preferred layout size.
845 * @param c The container to calculate for.
847 * @return The preferred layout size.
849 public Dimension preferredLayoutSize(Container c)
851 return new Dimension(dividerSize, dividerSize);
855 * This method is called when a component is removed.
857 * @param c The component to remove.
859 public void removeLayoutComponent(Component c)
861 // Do nothing.
865 * This method changes the button orientation when the orientation of the
866 * SplitPane changes.
868 private void changeButtonOrientation()
870 if (orientation == JSplitPane.HORIZONTAL_SPLIT)
872 ((BasicArrowButton) rightButton).setDirection(SwingConstants.EAST);
873 ((BasicArrowButton) leftButton).setDirection(SwingConstants.WEST);
875 else
877 ((BasicArrowButton) rightButton).setDirection(SwingConstants.SOUTH);
878 ((BasicArrowButton) leftButton).setDirection(SwingConstants.NORTH);
883 * This method sizes and positions the buttons.
885 private void positionButtons()
887 int w = 0;
888 int h = 0;
889 if (orientation == JSplitPane.HORIZONTAL_SPLIT)
891 rightButton.setLocation(ONE_TOUCH_OFFSET, ONE_TOUCH_OFFSET);
892 leftButton.setLocation(ONE_TOUCH_OFFSET,
893 ONE_TOUCH_OFFSET + 2 * ONE_TOUCH_SIZE);
894 w = dividerSize - 2 * ONE_TOUCH_OFFSET;
895 h = 2 * ONE_TOUCH_SIZE;
897 else
899 leftButton.setLocation(ONE_TOUCH_OFFSET, ONE_TOUCH_OFFSET);
900 rightButton.setLocation(ONE_TOUCH_OFFSET + 2 * ONE_TOUCH_SIZE,
901 ONE_TOUCH_OFFSET);
902 h = dividerSize - 2 * ONE_TOUCH_OFFSET;
903 w = 2 * ONE_TOUCH_SIZE;
905 Dimension dims = new Dimension(w, h);
906 leftButton.setSize(dims);
907 rightButton.setSize(dims);