001    /* BasicSplitPaneDivider.java --
002       Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.plaf.basic;
040    
041    import java.awt.Color;
042    import java.awt.Component;
043    import java.awt.Container;
044    import java.awt.Dimension;
045    import java.awt.Graphics;
046    import java.awt.Insets;
047    import java.awt.LayoutManager;
048    import java.awt.event.ActionEvent;
049    import java.awt.event.ActionListener;
050    import java.awt.event.MouseAdapter;
051    import java.awt.event.MouseEvent;
052    import java.awt.event.MouseMotionListener;
053    import java.beans.PropertyChangeEvent;
054    import java.beans.PropertyChangeListener;
055    
056    import javax.swing.JButton;
057    import javax.swing.JSplitPane;
058    import javax.swing.UIManager;
059    import 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     */
074    public 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    }