001/* BasicSplitPaneDivider.java --
002   Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing.plaf.basic;
040
041import java.awt.Color;
042import java.awt.Component;
043import java.awt.Container;
044import java.awt.Dimension;
045import java.awt.Graphics;
046import java.awt.Insets;
047import java.awt.LayoutManager;
048import java.awt.event.ActionEvent;
049import java.awt.event.ActionListener;
050import java.awt.event.MouseAdapter;
051import java.awt.event.MouseEvent;
052import java.awt.event.MouseMotionListener;
053import java.beans.PropertyChangeEvent;
054import java.beans.PropertyChangeListener;
055
056import javax.swing.JButton;
057import javax.swing.JSplitPane;
058import javax.swing.UIManager;
059import javax.swing.border.Border;
060
061/**
062 * The divider that separates the two parts of a JSplitPane in the Basic look
063 * and feel.
064 *
065 * <p>
066 * Implementation status: We do not have a real implementation yet. Currently,
067 * it is mostly a stub to allow compiling other parts of the
068 * javax.swing.plaf.basic package, although some parts are already
069 * functional.
070 * </p>
071 *
072 * @author Sascha Brawer (brawer_AT_dandelis.ch)
073 */
074public class BasicSplitPaneDivider extends Container
075  implements PropertyChangeListener
076{
077  /**
078   * The buttons used as one touch buttons.
079   */
080  private class BasicOneTouchButton
081    extends JButton
082  {
083    /**
084     * Denotes a left button.
085     */
086    static final int LEFT = 0;
087
088    /**
089     * Denotes a right button.
090     */
091    static final int RIGHT = 1;
092
093    /**
094     * The x points for the arrow.
095     */
096    private int[] xpoints;
097
098    /**
099     * The y points for the arrow.
100     */
101    private int[] ypoints;
102
103    /**
104     * Either LEFT or RIGHT.
105     */
106    private int direction;
107
108    /**
109     * Creates a new instance.
110     *
111     * @param dir either LEFT or RIGHT
112     */
113    BasicOneTouchButton(int dir)
114    {
115      direction = dir;
116      xpoints = new int[3];
117      ypoints = new int[3];
118    }
119
120    /**
121     * Never allow borders.
122     */
123    public void setBorder(Border b)
124    {
125    }
126
127    /**
128     * Never allow focus traversal.
129     */
130    public boolean isFocusTraversable()
131    {
132      return false;
133    }
134
135    /**
136     * Paints the one touch button.
137     */
138    public void paint(Graphics g)
139    {
140      if (splitPane != null)
141        {
142          // Fill background.
143          g.setColor(splitPane.getBackground());
144          g.fillRect(0, 0, getWidth(), getHeight());
145
146          // Draw arrow.
147          int size;
148          if (direction == LEFT)
149            {
150              if (orientation == JSplitPane.VERTICAL_SPLIT)
151                {
152                  size = Math.min(getHeight(), ONE_TOUCH_SIZE);
153                  xpoints[0] = 0;
154                  xpoints[1] = size / 2;
155                  xpoints[2] = size;
156                  ypoints[0] = size;
157                  ypoints[1] = 0;
158                  ypoints[2] = size;
159                }
160              else
161                {
162                  size = Math.min(getWidth(), ONE_TOUCH_SIZE);
163                  xpoints[0] = size;
164                  xpoints[1] = 0;
165                  xpoints[2] = size;
166                  ypoints[0] = 0;
167                  ypoints[1] = size / 2;
168                  ypoints[2] = size;
169                }
170            }
171          else
172            {
173              if (orientation == JSplitPane.VERTICAL_SPLIT)
174                {
175                  size = Math.min(getHeight(), ONE_TOUCH_SIZE);
176                  xpoints[0] = 0;
177                  xpoints[1] = size / 2;
178                  xpoints[2] = size;
179                  ypoints[0] = 0;
180                  ypoints[1] = size;
181                  ypoints[2] = 0;
182                }
183              else
184                {
185                  size = Math.min(getWidth(), ONE_TOUCH_SIZE);
186                  xpoints[0] = 0;
187                  xpoints[1] = size;
188                  xpoints[2] = 0;
189                  ypoints[0] = 0;
190                  ypoints[1] = size / 2;
191                  ypoints[2] = size;
192                }
193            }
194          g.setColor(Color.BLACK);
195          g.fillPolygon(xpoints, ypoints, 3);
196        }
197    }
198  }
199
200  /**
201   * Listens for actions on the one touch buttons.
202   */
203  private class OneTouchAction
204    implements ActionListener
205  {
206
207    public void actionPerformed(ActionEvent ev)
208    {
209      Insets insets = splitPane.getInsets();
210      int lastLoc = splitPane.getLastDividerLocation();
211      int currentLoc = splitPaneUI.getDividerLocation(splitPane);
212      int newLoc;
213
214      if (ev.getSource() == leftButton)
215        {
216          if (orientation == JSplitPane.VERTICAL_SPLIT)
217            {
218              if (currentLoc
219                  >= splitPane.getHeight() - insets.bottom - getHeight())
220                {
221                  newLoc = Math.min(splitPane.getMaximumDividerLocation(),
222                                    lastLoc);
223                }
224              else
225                {
226                  newLoc = insets.top;
227                }
228            }
229          else
230            {
231              if (currentLoc
232                  >= splitPane.getWidth() - insets.right - getWidth())
233                {
234                  newLoc = Math.min(splitPane.getMaximumDividerLocation(),
235                                    lastLoc);
236                }
237              else
238                {
239                  newLoc = insets.left;
240                }
241            }
242        }
243      else
244        {
245          if (orientation == JSplitPane.VERTICAL_SPLIT)
246            {
247              if (currentLoc == insets.top)
248                {
249                  newLoc = Math.min(splitPane.getMaximumDividerLocation(),
250                                    lastLoc);
251                }
252              else
253                {
254                  newLoc = splitPane.getHeight() - insets.top - getHeight();
255                }
256            }
257          else
258            {
259              if (currentLoc == insets.left)
260                {
261                  newLoc = Math.min(splitPane.getMaximumDividerLocation(),
262                                    lastLoc);
263                }
264              else
265                {
266                  newLoc = splitPane.getWidth() - insets.left - getWidth();
267                }
268            }
269        }
270      if (currentLoc != newLoc)
271        {
272          splitPane.setDividerLocation(newLoc);
273          splitPane.setLastDividerLocation(currentLoc);
274        }
275    }
276  }
277
278  /**
279   * Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1
280   * on MacOS X 10.1.5.
281   */
282  static final long serialVersionUID = 1463404307042803342L;
283
284  /**
285   * The width and height of the little buttons for showing and hiding parts
286   * of a JSplitPane in a single mouse click.
287   */
288  protected static final int ONE_TOUCH_SIZE = 6;
289
290  /** The distance the one touch buttons will sit from the divider's edges. */
291  protected static final int ONE_TOUCH_OFFSET = 2;
292
293  /**
294   * An object that performs the tasks associated with an ongoing drag
295   * operation, or <code>null</code> if the user is currently not dragging
296   * the divider.
297   */
298  protected DragController dragger;
299
300  /**
301   * The delegate object that is responsible for the UI of the
302   * <code>JSplitPane</code> that contains this divider.
303   */
304  protected BasicSplitPaneUI splitPaneUI;
305
306  /** The thickness of the divider in pixels. */
307  protected int dividerSize;
308
309  /** A divider that is used for layout purposes. */
310  protected Component hiddenDivider;
311
312  /** The JSplitPane containing this divider. */
313  protected JSplitPane splitPane;
314
315  /**
316   * The listener for handling mouse events from both the divider and the
317   * containing <code>JSplitPane</code>.
318   *
319   * <p>
320   * The reason for also handling MouseEvents from the containing
321   * <code>JSplitPane</code> is that users should be able to start a drag
322   * gesture from inside the JSplitPane, but slightly outisde the divider.
323   * </p>
324   */
325  protected MouseHandler mouseHandler = new MouseHandler();
326
327  /**
328   * The current orientation of the containing <code>JSplitPane</code>, which
329   * is either {@link javax.swing.JSplitPane#HORIZONTAL_SPLIT} or {@link
330   * javax.swing.JSplitPane#VERTICAL_SPLIT}.
331   */
332  protected int orientation;
333
334  /**
335   * The button for showing and hiding the left (or top) component of the
336   * <code>JSplitPane</code>.
337   */
338  protected JButton leftButton;
339
340  /**
341   * The button for showing and hiding the right (or bottom) component of the
342   * <code>JSplitPane</code>.
343   */
344  protected JButton rightButton;
345
346  /**
347   * The border of this divider. Typically, this will be an instance of {@link
348   * javax.swing.plaf.basic.BasicBorders.SplitPaneDividerBorder}.
349   *
350   * @see #getBorder()
351   * @see #setBorder(javax.swing.border.Border)
352   */
353  private Border border;
354
355  // This is not a pixel count.
356  // This int should be able to take 3 values.
357  // left (top), middle, right(bottom)
358  //    0          1          2
359
360  /**
361   * Keeps track of where the divider should be placed when using one touch
362   * expand buttons.
363   * This is package-private to avoid an accessor method.
364   */
365  transient int currentDividerLocation = 1;
366
367  /**
368   * Indicates if the ont touch buttons are laid out centered or at the
369   * top/left.
370   *
371   * Package private to avoid accessor method.
372   */
373  boolean centerOneTouchButtons;
374
375  /**
376   * Constructs a new divider.
377   *
378   * @param ui the UI delegate of the enclosing <code>JSplitPane</code>.
379   */
380  public BasicSplitPaneDivider(BasicSplitPaneUI ui)
381  {
382    setLayout(new DividerLayout());
383    setBasicSplitPaneUI(ui);
384    setDividerSize(splitPane.getDividerSize());
385    centerOneTouchButtons =
386      UIManager.getBoolean("SplitPane.centerOneTouchButtons");
387  }
388
389  /**
390   * Sets the delegate object that is responsible for the UI of the {@link
391   * javax.swing.JSplitPane} containing this divider.
392   *
393   * @param newUI the UI delegate, or <code>null</code> to release the
394   *        connection to the current delegate.
395   */
396  public void setBasicSplitPaneUI(BasicSplitPaneUI newUI)
397  {
398    /* Remove the connection to the existing JSplitPane. */
399    if (splitPane != null)
400      {
401        splitPane.removePropertyChangeListener(this);
402        removeMouseListener(mouseHandler);
403        removeMouseMotionListener(mouseHandler);
404        splitPane = null;
405        hiddenDivider = null;
406      }
407
408    /* Establish the connection to the new JSplitPane. */
409    splitPaneUI = newUI;
410    if (splitPaneUI != null)
411      splitPane = newUI.getSplitPane();
412    if (splitPane != null)
413      {
414        splitPane.addPropertyChangeListener(this);
415        addMouseListener(mouseHandler);
416        addMouseMotionListener(mouseHandler);
417        hiddenDivider = splitPaneUI.getNonContinuousLayoutDivider();
418        orientation = splitPane.getOrientation();
419        if (splitPane.isOneTouchExpandable())
420          oneTouchExpandableChanged();
421      }
422  }
423
424  /**
425   * Returns the delegate object that is responsible for the UI of the {@link
426   * javax.swing.JSplitPane} containing this divider.
427   *
428   * @return The UI for the JSplitPane.
429   */
430  public BasicSplitPaneUI getBasicSplitPaneUI()
431  {
432    return splitPaneUI;
433  }
434
435  /**
436   * Sets the thickness of the divider.
437   *
438   * @param newSize the new width or height in pixels.
439   */
440  public void setDividerSize(int newSize)
441  {
442    this.dividerSize = newSize;
443  }
444
445  /**
446   * Retrieves the thickness of the divider.
447   *
448   * @return The thickness of the divider.
449   */
450  public int getDividerSize()
451  {
452    return dividerSize;
453  }
454
455  /**
456   * Sets the border of this divider.
457   *
458   * @param border the new border. Typically, this will be an instance of
459   *        {@link
460   *        javax.swing.plaf.basic.BasicBorders.SplitPaneBorder}.
461   *
462   * @since 1.3
463   */
464  public void setBorder(Border border)
465  {
466    if (border != this.border)
467      {
468        Border oldValue = this.border;
469        this.border = border;
470        firePropertyChange("border", oldValue, border);
471      }
472  }
473
474  /**
475   * Retrieves the border of this divider.
476   *
477   * @return the current border, or <code>null</code> if no border has been
478   *         set.
479   *
480   * @since 1.3
481   */
482  public Border getBorder()
483  {
484    return border;
485  }
486
487  /**
488   * Retrieves the insets of the divider. If a border has been installed on
489   * the divider, the result of calling its <code>getBorderInsets</code>
490   * method is returned. Otherwise, the inherited implementation will be
491   * invoked.
492   *
493   * @see javax.swing.border.Border#getBorderInsets(java.awt.Component)
494   */
495  public Insets getInsets()
496  {
497    if (border != null)
498      return border.getBorderInsets(this);
499    else
500      return super.getInsets();
501  }
502
503  /**
504   * Returns the preferred size of this divider, which is
505   * <code>dividerSize</code> by <code>dividerSize</code> pixels.
506   *
507   * @return The preferred size of the divider.
508   */
509  public Dimension getPreferredSize()
510  {
511    Dimension d;
512    if (orientation == JSplitPane.HORIZONTAL_SPLIT)
513      d = new Dimension(getDividerSize(), 1);
514    else
515      d = new Dimension(1, getDividerSize());
516    return d;
517  }
518
519  /**
520   * Returns the minimal size of this divider, which is
521   * <code>dividerSize</code> by <code>dividerSize</code> pixels.
522   *
523   * @return The minimal size of the divider.
524   */
525  public Dimension getMinimumSize()
526  {
527    return getPreferredSize();
528  }
529
530  /**
531   * Processes events from the <code>JSplitPane</code> that contains this
532   * divider.
533   *
534   * @param e The PropertyChangeEvent.
535   */
536  public void propertyChange(PropertyChangeEvent e)
537  {
538    if (e.getPropertyName().equals(JSplitPane.ONE_TOUCH_EXPANDABLE_PROPERTY))
539      oneTouchExpandableChanged();
540    else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY))
541      {
542        orientation = splitPane.getOrientation();
543        invalidate();
544        if (splitPane != null)
545          splitPane.revalidate();
546      }
547    else if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY))
548      dividerSize = splitPane.getDividerSize();
549  }
550
551  /**
552   * Paints the divider by painting its border.
553   *
554   * @param g The Graphics Object to paint with.
555   */
556  public void paint(Graphics g)
557  {
558    Dimension dividerSize;
559
560    super.paint(g);
561    if (border != null)
562      {
563        dividerSize = getSize();
564        border.paintBorder(this, g, 0, 0, dividerSize.width, dividerSize.height);
565      }
566  }
567
568  /**
569   * Reacts to changes of the <code>oneToughExpandable</code> property of the
570   * containing <code>JSplitPane</code>.
571   */
572  protected void oneTouchExpandableChanged()
573  {
574    if (splitPane.isOneTouchExpandable())
575      {
576        leftButton = createLeftOneTouchButton();
577        if (leftButton != null)
578          leftButton.addActionListener(new OneTouchAction());
579
580        rightButton = createRightOneTouchButton();
581        if (rightButton != null)
582          rightButton.addActionListener(new OneTouchAction());
583
584        // Only add them when both are non-null.
585        if (leftButton != null && rightButton != null)
586          {
587            add(leftButton);
588            add(rightButton);
589          }
590      }
591    invalidate();
592    if (splitPane != null)
593      splitPane.revalidate();
594  }
595
596  /**
597   * Creates a button for showing and hiding the left (or top) part of a
598   * <code>JSplitPane</code>.
599   *
600   * @return The left one touch button.
601   */
602  protected JButton createLeftOneTouchButton()
603  {
604    JButton button = new BasicOneTouchButton(BasicOneTouchButton.LEFT);
605    button.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
606    button.setRequestFocusEnabled(false);
607    return button;
608  }
609
610  /**
611   * Creates a button for showing and hiding the right (or bottom) part of a
612   * <code>JSplitPane</code>.
613   *
614   * @return The right one touch button.
615   */
616  protected JButton createRightOneTouchButton()
617  {
618    JButton button = new BasicOneTouchButton(BasicOneTouchButton.RIGHT);
619    button.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
620    button.setRequestFocusEnabled(false);
621    return button;
622  }
623
624  /**
625   * Prepares the divider for dragging by calling the
626   * <code>startDragging</code> method of the UI delegate of the enclosing
627   * <code>JSplitPane</code>.
628   *
629   * @see BasicSplitPaneUI#startDragging()
630   */
631  protected void prepareForDragging()
632  {
633    if (splitPaneUI != null)
634      splitPaneUI.startDragging();
635  }
636
637  /**
638   * Drags the divider to a given location by calling the
639   * <code>dragDividerTo</code> method of the UI delegate of the enclosing
640   * <code>JSplitPane</code>.
641   *
642   * @param location the new location of the divider.
643   *
644   * @see BasicSplitPaneUI#dragDividerTo(int location)
645   */
646  protected void dragDividerTo(int location)
647  {
648    if (splitPaneUI != null)
649      splitPaneUI.dragDividerTo(location);
650  }
651
652  /**
653   * Finishes a dragging gesture by calling the <code>finishDraggingTo</code>
654   * method of the UI delegate of the enclosing <code>JSplitPane</code>.
655   *
656   * @param location the new, final location of the divider.
657   *
658   * @see BasicSplitPaneUI#finishDraggingTo(int location)
659   */
660  protected void finishDraggingTo(int location)
661  {
662    if (splitPaneUI != null)
663      splitPaneUI.finishDraggingTo(location);
664  }
665
666  /**
667   * This helper method moves the divider to one of the  three locations when
668   * using one touch expand buttons. Location 0 is the left (or top) most
669   * location. Location 1 is the middle. Location 2 is the right (or bottom)
670   * most location.
671   * This is package-private to avoid an accessor method.
672   *
673   * @param locationIndex The location to move to.
674   */
675  void moveDividerTo(int locationIndex)
676  {
677    Insets insets = splitPane.getInsets();
678    switch (locationIndex)
679      {
680      case 1:
681        splitPane.setDividerLocation(splitPane.getLastDividerLocation());
682        break;
683      case 0:
684        int top = (orientation == JSplitPane.HORIZONTAL_SPLIT) ? insets.left
685                                                               : insets.top;
686        splitPane.setDividerLocation(top);
687        break;
688      case 2:
689        int bottom;
690        if (orientation == JSplitPane.HORIZONTAL_SPLIT)
691          bottom = splitPane.getBounds().width - insets.right - dividerSize;
692        else
693          bottom = splitPane.getBounds().height - insets.bottom - dividerSize;
694        splitPane.setDividerLocation(bottom);
695        break;
696      }
697  }
698
699  /**
700   * The listener for handling mouse events from both the divider and the
701   * containing <code>JSplitPane</code>.
702   *
703   * <p>
704   * The reason for also handling MouseEvents from the containing
705   * <code>JSplitPane</code> is that users should be able to start a drag
706   * gesture from inside the JSplitPane, but slightly outisde the divider.
707   * </p>
708   *
709   * @author Sascha Brawer (brawer_AT_dandelis.ch)
710   */
711  protected class MouseHandler extends MouseAdapter
712    implements MouseMotionListener
713  {
714    /** Keeps track of whether a drag is occurring. */
715    private transient boolean isDragging;
716
717    /**
718     * This method is called when the mouse is pressed.
719     *
720     * @param e The MouseEvent.
721     */
722    public void mousePressed(MouseEvent e)
723    {
724      isDragging = true;
725      currentDividerLocation = 1;
726      if (orientation == JSplitPane.HORIZONTAL_SPLIT)
727        dragger = new DragController(e);
728      else
729        dragger = new VerticalDragController(e);
730      prepareForDragging();
731    }
732
733    /**
734     * This method is called when the mouse is released.
735     *
736     * @param e The MouseEvent.
737     */
738    public void mouseReleased(MouseEvent e)
739    {
740      if (isDragging)
741        dragger.completeDrag(e);
742      isDragging = false;
743    }
744
745    /**
746     * Repeatedly invoked when the user is dragging the mouse cursor while
747     * having pressed a mouse button.
748     *
749     * @param e The MouseEvent.
750     */
751    public void mouseDragged(MouseEvent e)
752    {
753      if (dragger != null)
754        dragger.continueDrag(e);
755    }
756
757    /**
758     * Repeatedly invoked when the user is dragging the mouse cursor without
759     * having pressed a mouse button.
760     *
761     * @param e The MouseEvent.
762     */
763    public void mouseMoved(MouseEvent e)
764    {
765      // Do nothing.
766    }
767  }
768
769  /**
770   * Performs the tasks associated with an ongoing drag operation.
771   *
772   * @author Sascha Brawer (brawer_AT_dandelis.ch)
773   */
774  protected class DragController
775  {
776    /**
777     * The difference between where the mouse is clicked and the  initial
778     * divider location.
779     */
780    transient int offset;
781
782    /**
783     * Creates a new DragController object.
784     *
785     * @param e The MouseEvent to initialize with.
786     */
787    protected DragController(MouseEvent e)
788    {
789      offset = e.getX();
790    }
791
792    /**
793     * This method returns true if the divider can move.
794     *
795     * @return True if dragging is allowed.
796     */
797    protected boolean isValid()
798    {
799      // Views can always be resized?
800      return true;
801    }
802
803    /**
804     * Returns a position for the divider given the MouseEvent.
805     *
806     * @param e MouseEvent.
807     *
808     * @return The position for the divider to move to.
809     */
810    protected int positionForMouseEvent(MouseEvent e)
811    {
812      return e.getX() + getX() - offset;
813    }
814
815    /**
816     * This method returns one of the two paramters for the orientation. In
817     * this case, it returns x.
818     *
819     * @param x The x coordinate.
820     * @param y The y coordinate.
821     *
822     * @return The x coordinate.
823     */
824    protected int getNeededLocation(int x, int y)
825    {
826      return x;
827    }
828
829    /**
830     * This method is called to pass on the drag information to the UI through
831     * dragDividerTo.
832     *
833     * @param newX The x coordinate of the MouseEvent.
834     * @param newY The y coordinate of the MouseEvent.
835     */
836    protected void continueDrag(int newX, int newY)
837    {
838      if (isValid())
839        dragDividerTo(adjust(newX, newY));
840    }
841
842    /**
843     * This method is called to pass on the drag information  to the UI
844     * through dragDividerTo.
845     *
846     * @param e The MouseEvent.
847     */
848    protected void continueDrag(MouseEvent e)
849    {
850      if (isValid())
851        dragDividerTo(positionForMouseEvent(e));
852    }
853
854    /**
855     * This method is called to finish the drag session  by calling
856     * finishDraggingTo.
857     *
858     * @param x The x coordinate of the MouseEvent.
859     * @param y The y coordinate of the MouseEvent.
860     */
861    protected void completeDrag(int x, int y)
862    {
863      finishDraggingTo(adjust(x, y));
864    }
865
866    /**
867     * This method is called to finish the drag session  by calling
868     * finishDraggingTo.
869     *
870     * @param e The MouseEvent.
871     */
872    protected void completeDrag(MouseEvent e)
873    {
874      finishDraggingTo(positionForMouseEvent(e));
875    }
876
877    /**
878     * This is a helper method that includes the offset in the needed
879     * location.
880     *
881     * @param x The x coordinate of the MouseEvent.
882     * @param y The y coordinate of the MouseEvent.
883     *
884     * @return The needed location adjusted by the offsets.
885     */
886    int adjust(int x, int y)
887    {
888      return getNeededLocation(x, y) + getX() - offset;
889    }
890  }
891
892  /**
893   * This is a helper class that controls dragging when  the orientation is
894   * VERTICAL_SPLIT.
895   */
896  protected class VerticalDragController extends DragController
897  {
898    /**
899     * Creates a new VerticalDragController object.
900     *
901     * @param e The MouseEvent to initialize with.
902     */
903    protected VerticalDragController(MouseEvent e)
904    {
905      super(e);
906      offset = e.getY();
907    }
908
909    /**
910     * This method returns one of the two parameters given the orientation. In
911     * this case, it returns y.
912     *
913     * @param x The x coordinate of the MouseEvent.
914     * @param y The y coordinate of the MouseEvent.
915     *
916     * @return The y coordinate.
917     */
918    protected int getNeededLocation(int x, int y)
919    {
920      return y;
921    }
922
923    /**
924     * This method returns the new location of the divider given a MouseEvent.
925     *
926     * @param e The MouseEvent.
927     *
928     * @return The new location of the divider.
929     */
930    protected int positionForMouseEvent(MouseEvent e)
931    {
932      return e.getY() + getY() - offset;
933    }
934
935    /**
936     * This is a helper method that includes the offset in the needed
937     * location.
938     *
939     * @param x The x coordinate of the MouseEvent.
940     * @param y The y coordinate of the MouseEvent.
941     *
942     * @return The needed location adjusted by the offsets.
943     */
944    int adjust(int x, int y)
945    {
946      return getNeededLocation(x, y) + getY() - offset;
947    }
948  }
949
950  /**
951   * This helper class acts as the Layout Manager for the divider.
952   */
953  protected class DividerLayout implements LayoutManager
954  {
955    /**
956     * Creates a new DividerLayout object.
957     */
958    protected DividerLayout()
959    {
960      // Nothing to do here.
961    }
962
963    /**
964     * This method is called when a Component is added.
965     *
966     * @param string The constraints string.
967     * @param c The Component to add.
968     */
969    public void addLayoutComponent(String string, Component c)
970    {
971      // Do nothing.
972    }
973
974    /**
975     * This method is called to lay out the container.
976     *
977     * @param c The container to lay out.
978     */
979    public void layoutContainer(Container c)
980    {
981      if (leftButton != null && rightButton != null
982          && c == BasicSplitPaneDivider.this)
983        {
984          if (splitPane.isOneTouchExpandable())
985            {
986              Insets insets = getInsets();
987              if (orientation == JSplitPane.HORIZONTAL_SPLIT)
988                {
989                  int size = getWidth() - insets.left - insets.right;
990                  size = Math.max(size, 0);
991                  size = Math.min(size, ONE_TOUCH_SIZE);
992                  int x, y;
993                  if (centerOneTouchButtons)
994                    {
995                      y = insets.top;
996                      x = (getWidth() - size) / 2;
997                    }
998                  else
999                    {
1000                      x = insets.left;
1001                      y = 0;
1002                    }
1003
1004                  leftButton.setBounds(x, y + ONE_TOUCH_OFFSET, size,
1005                                       size * 2);
1006                  rightButton.setBounds(x, y + ONE_TOUCH_OFFSET
1007                                        + ONE_TOUCH_SIZE * 2, size, size * 2);
1008                }
1009              else
1010                {
1011                  int size = getHeight() - insets.top - insets.bottom;
1012                  size = Math.max(size, 0);
1013                  size = Math.min(size, ONE_TOUCH_SIZE);
1014                  int x, y;
1015                  if (centerOneTouchButtons)
1016                    {
1017                      x = insets.left;
1018                      y = (getHeight() - size) / 2;
1019                    }
1020                  else
1021                    {
1022                      x = 0;
1023                      y = insets.top;
1024                    }
1025                  leftButton.setBounds(x + ONE_TOUCH_OFFSET, y, size * 2,
1026                                       size);
1027                  rightButton.setBounds(x + ONE_TOUCH_OFFSET
1028                                        + ONE_TOUCH_SIZE * 2, y, size * 2,
1029                                        size);
1030                }
1031            }
1032          else
1033            {
1034              // The JDK sets this bounds for disabled one touch buttons, so
1035              // do we.
1036              leftButton.setBounds(-5, -5, 1, 1);
1037              rightButton.setBounds(-5, -5, 1, 1);
1038            }
1039        }
1040    }
1041
1042    /**
1043     * This method returns the minimum layout size.
1044     *
1045     * @param c The container to calculate for.
1046     *
1047     * @return The minimum layout size.
1048     */
1049    public Dimension minimumLayoutSize(Container c)
1050    {
1051      return preferredLayoutSize(c);
1052    }
1053
1054    /**
1055     * This method returns the preferred layout size.
1056     *
1057     * @param c The container to calculate for.
1058     *
1059     * @return The preferred layout size.
1060     */
1061    public Dimension preferredLayoutSize(Container c)
1062    {
1063      return new Dimension(dividerSize, dividerSize);
1064    }
1065
1066    /**
1067     * This method is called when a component is removed.
1068     *
1069     * @param c The component to remove.
1070     */
1071    public void removeLayoutComponent(Component c)
1072    {
1073      // Do nothing.
1074    }
1075
1076  }
1077}