Source for javax.swing.plaf.basic.BasicSplitPaneDivider

   1: /* BasicSplitPaneDivider.java --
   2:    Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   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.
  10: 
  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.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  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.
  25: 
  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. */
  37: 
  38: 
  39: package javax.swing.plaf.basic;
  40: 
  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;
  53: 
  54: import javax.swing.JButton;
  55: import javax.swing.JSplitPane;
  56: import javax.swing.SwingConstants;
  57: import javax.swing.border.Border;
  58: 
  59: /**
  60:  * The divider that separates the two parts of a JSplitPane in the Basic look
  61:  * and feel.
  62:  * 
  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>
  69:  *
  70:  * @author Sascha Brawer (brawer_AT_dandelis.ch)
  71:  */
  72: public class BasicSplitPaneDivider extends Container
  73:   implements PropertyChangeListener
  74: {
  75:   /**
  76:    * Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1
  77:    * on MacOS X 10.1.5.
  78:    */
  79:   static final long serialVersionUID = 1463404307042803342L;
  80: 
  81:   /**
  82:    * The width and height of the little buttons for showing and hiding parts
  83:    * of a JSplitPane in a single mouse click.
  84:    */
  85:   protected static final int ONE_TOUCH_SIZE = 6;
  86: 
  87:   /** The distance the one touch buttons will sit from the divider's edges. */
  88:   protected static final int ONE_TOUCH_OFFSET = 2;
  89: 
  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.
  94:    */
  95:   protected DragController dragger;
  96: 
  97:   /**
  98:    * The delegate object that is responsible for the UI of the
  99:    * <code>JSplitPane</code> that contains this divider.
 100:    */
 101:   protected BasicSplitPaneUI splitPaneUI;
 102: 
 103:   /** The thickness of the divider in pixels. */
 104:   protected int dividerSize;
 105: 
 106:   /** A divider that is used for layout purposes. */
 107:   protected Component hiddenDivider;
 108: 
 109:   /** The JSplitPane containing this divider. */
 110:   protected JSplitPane splitPane;
 111: 
 112:   /**
 113:    * The listener for handling mouse events from both the divider and the
 114:    * containing <code>JSplitPane</code>.
 115:    * 
 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>
 121:    */
 122:   protected MouseHandler mouseHandler = new MouseHandler();
 123: 
 124:   /**
 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}.
 128:    */
 129:   protected int orientation;
 130: 
 131:   /**
 132:    * The button for showing and hiding the left (or top) component of the
 133:    * <code>JSplitPane</code>.
 134:    */
 135:   protected JButton leftButton;
 136: 
 137:   /**
 138:    * The button for showing and hiding the right (or bottom) component of the
 139:    * <code>JSplitPane</code>.
 140:    */
 141:   protected JButton rightButton;
 142: 
 143:   /**
 144:    * The border of this divider. Typically, this will be an instance of {@link
 145:    * javax.swing.plaf.basic.BasicBorders.SplitPaneDividerBorder}.
 146:    *
 147:    * @see #getBorder()
 148:    * @see #setBorder(javax.swing.border.Border)
 149:    */
 150:   private Border border;
 151: 
 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
 156: 
 157:   /**
 158:    * Keeps track of where the divider should be placed when using one touch
 159:    * expand buttons.
 160:    * This is package-private to avoid an accessor method.
 161:    */
 162:   transient int currentDividerLocation = 1;
 163: 
 164:   /** DOCUMENT ME! */
 165:   private transient Border tmpBorder = new Border()
 166:     {
 167:       public Insets getBorderInsets(Component c)
 168:       {
 169:     return new Insets(2, 2, 2, 2);
 170:       }
 171: 
 172:       public boolean isBorderOpaque()
 173:       {
 174:     return false;
 175:       }
 176: 
 177:       public void paintBorder(Component c, Graphics g, int x, int y,
 178:                               int width, int height)
 179:       {
 180:     Color saved = g.getColor();
 181:     g.setColor(Color.BLACK);
 182: 
 183:     g.drawRect(x + 2, y + 2, width - 4, height - 4);
 184: 
 185:     g.setColor(saved);
 186:       }
 187:     };
 188: 
 189:   /**
 190:    * Constructs a new divider.
 191:    *
 192:    * @param ui the UI delegate of the enclosing <code>JSplitPane</code>.
 193:    */
 194:   public BasicSplitPaneDivider(BasicSplitPaneUI ui)
 195:   {
 196:     setLayout(new DividerLayout());
 197:     setBasicSplitPaneUI(ui);
 198:     setDividerSize(splitPane.getDividerSize());
 199:     setBorder(tmpBorder);
 200:   }
 201: 
 202:   /**
 203:    * Sets the delegate object that is responsible for the UI of the {@link
 204:    * javax.swing.JSplitPane} containing this divider.
 205:    *
 206:    * @param newUI the UI delegate, or <code>null</code> to release the
 207:    *        connection to the current delegate.
 208:    */
 209:   public void setBasicSplitPaneUI(BasicSplitPaneUI newUI)
 210:   {
 211:     /* Remove the connection to the existing JSplitPane. */
 212:     if (splitPane != null)
 213:       {
 214:     splitPane.removePropertyChangeListener(this);
 215:     splitPane.removeMouseListener(mouseHandler);
 216:     splitPane.removeMouseMotionListener(mouseHandler);
 217:     removeMouseListener(mouseHandler);
 218:     removeMouseMotionListener(mouseHandler);
 219:     splitPane = null;
 220:     hiddenDivider = null;
 221:       }
 222: 
 223:     /* Establish the connection to the new JSplitPane. */
 224:     splitPaneUI = newUI;
 225:     if (splitPaneUI != null)
 226:       splitPane = newUI.getSplitPane();
 227:     if (splitPane != null)
 228:       {
 229:     splitPane.addPropertyChangeListener(this);
 230:     splitPane.addMouseListener(mouseHandler);
 231:     splitPane.addMouseMotionListener(mouseHandler);
 232:     addMouseListener(mouseHandler);
 233:     addMouseMotionListener(mouseHandler);
 234:     hiddenDivider = splitPaneUI.getNonContinuousLayoutDivider();
 235:     orientation = splitPane.getOrientation();
 236:     oneTouchExpandableChanged();
 237:       }
 238:   }
 239: 
 240:   /**
 241:    * Returns the delegate object that is responsible for the UI of the {@link
 242:    * javax.swing.JSplitPane} containing this divider.
 243:    *
 244:    * @return The UI for the JSplitPane.
 245:    */
 246:   public BasicSplitPaneUI getBasicSplitPaneUI()
 247:   {
 248:     return splitPaneUI;
 249:   }
 250: 
 251:   /**
 252:    * Sets the thickness of the divider.
 253:    *
 254:    * @param newSize the new width or height in pixels.
 255:    */
 256:   public void setDividerSize(int newSize)
 257:   {
 258:     this.dividerSize = newSize;
 259:   }
 260: 
 261:   /**
 262:    * Retrieves the thickness of the divider.
 263:    *
 264:    * @return The thickness of the divider.
 265:    */
 266:   public int getDividerSize()
 267:   {
 268:     return dividerSize;
 269:   }
 270: 
 271:   /**
 272:    * Sets the border of this divider.
 273:    *
 274:    * @param border the new border. Typically, this will be an instance of
 275:    *        {@link
 276:    *        javax.swing.plaf.basic.BasicBorders.SplitPaneBorder}.
 277:    *
 278:    * @since 1.3
 279:    */
 280:   public void setBorder(Border border)
 281:   {
 282:     if (border != this.border)
 283:       {
 284:     Border oldValue = this.border;
 285:     this.border = border;
 286:     firePropertyChange("border", oldValue, border);
 287:       }
 288:   }
 289: 
 290:   /**
 291:    * Retrieves the border of this divider.
 292:    *
 293:    * @return the current border, or <code>null</code> if no border has been
 294:    *         set.
 295:    *
 296:    * @since 1.3
 297:    */
 298:   public Border getBorder()
 299:   {
 300:     return border;
 301:   }
 302: 
 303:   /**
 304:    * Retrieves the insets of the divider. If a border has been installed on
 305:    * the divider, the result of calling its <code>getBorderInsets</code>
 306:    * method is returned. Otherwise, the inherited implementation will be
 307:    * invoked.
 308:    *
 309:    * @see javax.swing.border.Border#getBorderInsets(java.awt.Component)
 310:    */
 311:   public Insets getInsets()
 312:   {
 313:     if (border != null)
 314:       return border.getBorderInsets(this);
 315:     else
 316:       return super.getInsets();
 317:   }
 318: 
 319:   /**
 320:    * Returns the preferred size of this divider, which is
 321:    * <code>dividerSize</code> by <code>dividerSize</code> pixels.
 322:    *
 323:    * @return The preferred size of the divider.
 324:    */
 325:   public Dimension getPreferredSize()
 326:   {
 327:     return getLayout().preferredLayoutSize(this);
 328:   }
 329: 
 330:   /**
 331:    * Returns the minimal size of this divider, which is
 332:    * <code>dividerSize</code> by <code>dividerSize</code> pixels.
 333:    *
 334:    * @return The minimal size of the divider.
 335:    */
 336:   public Dimension getMinimumSize()
 337:   {
 338:     return getPreferredSize();
 339:   }
 340: 
 341:   /**
 342:    * Processes events from the <code>JSplitPane</code> that contains this
 343:    * divider.
 344:    *
 345:    * @param e The PropertyChangeEvent.
 346:    */
 347:   public void propertyChange(PropertyChangeEvent e)
 348:   {
 349:     if (e.getPropertyName().equals(JSplitPane.ONE_TOUCH_EXPANDABLE_PROPERTY))
 350:       oneTouchExpandableChanged();
 351:     else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY))
 352:       {
 353:     orientation = splitPane.getOrientation();
 354:     if (splitPane.isOneTouchExpandable())
 355:       {
 356:         layout();
 357:         repaint();
 358:       }
 359:       }
 360:     else if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY))
 361:       dividerSize = splitPane.getDividerSize();
 362:   }
 363: 
 364:   /**
 365:    * Paints the divider by painting its border.
 366:    *
 367:    * @param g The Graphics Object to paint with.
 368:    */
 369:   public void paint(Graphics g)
 370:   {
 371:     Dimension dividerSize;
 372: 
 373:     super.paint(g);
 374:     if (border != null)
 375:       {
 376:     dividerSize = getSize();
 377:     border.paintBorder(this, g, 0, 0, dividerSize.width, dividerSize.height);
 378:       }
 379:     if (splitPane.isOneTouchExpandable())
 380:       {
 381:         ((BasicArrowButton) rightButton).paint(g);
 382:         ((BasicArrowButton) leftButton).paint(g);
 383:       }
 384:   }
 385: 
 386:   /**
 387:    * Reacts to changes of the <code>oneToughExpandable</code> property of the
 388:    * containing <code>JSplitPane</code>.
 389:    */
 390:   protected void oneTouchExpandableChanged()
 391:   {
 392:     if (splitPane.isOneTouchExpandable())
 393:       {
 394:     leftButton = createLeftOneTouchButton();
 395:     rightButton = createRightOneTouchButton();
 396:     add(leftButton);
 397:     add(rightButton);
 398: 
 399:     leftButton.addMouseListener(mouseHandler);
 400:     rightButton.addMouseListener(mouseHandler);
 401: 
 402:     // Set it to 1.
 403:     currentDividerLocation = 1;
 404:       }
 405:     else
 406:       {
 407:     if (leftButton != null && rightButton != null)
 408:       {
 409:         leftButton.removeMouseListener(mouseHandler);
 410:         rightButton.removeMouseListener(mouseHandler);
 411: 
 412:         remove(leftButton);
 413:         remove(rightButton);
 414:         leftButton = null;
 415:         rightButton = null;
 416:       }
 417:       }
 418:     layout();
 419:     repaint();
 420:   }
 421: 
 422:   /**
 423:    * Creates a button for showing and hiding the left (or top) part of a
 424:    * <code>JSplitPane</code>.
 425:    *
 426:    * @return The left one touch button.
 427:    */
 428:   protected JButton createLeftOneTouchButton()
 429:   {
 430:     int dir = SwingConstants.WEST;
 431:     if (orientation == JSplitPane.VERTICAL_SPLIT)
 432:       dir = SwingConstants.NORTH;
 433:     JButton button = new BasicArrowButton(dir);
 434:     button.setBorder(null);
 435: 
 436:     return button;
 437:   }
 438: 
 439:   /**
 440:    * Creates a button for showing and hiding the right (or bottom) part of a
 441:    * <code>JSplitPane</code>.
 442:    *
 443:    * @return The right one touch button.
 444:    */
 445:   protected JButton createRightOneTouchButton()
 446:   {
 447:     int dir = SwingConstants.EAST;
 448:     if (orientation == JSplitPane.VERTICAL_SPLIT)
 449:       dir = SwingConstants.SOUTH;
 450:     JButton button = new BasicArrowButton(dir);
 451:     button.setBorder(null);
 452:     return button;
 453:   }
 454: 
 455:   /**
 456:    * Prepares the divider for dragging by calling the
 457:    * <code>startDragging</code> method of the UI delegate of the enclosing
 458:    * <code>JSplitPane</code>.
 459:    *
 460:    * @see BasicSplitPaneUI#startDragging()
 461:    */
 462:   protected void prepareForDragging()
 463:   {
 464:     if (splitPaneUI != null)
 465:       splitPaneUI.startDragging();
 466:   }
 467: 
 468:   /**
 469:    * Drags the divider to a given location by calling the
 470:    * <code>dragDividerTo</code> method of the UI delegate of the enclosing
 471:    * <code>JSplitPane</code>.
 472:    *
 473:    * @param location the new location of the divider.
 474:    *
 475:    * @see BasicSplitPaneUI#dragDividerTo(int location)
 476:    */
 477:   protected void dragDividerTo(int location)
 478:   {
 479:     if (splitPaneUI != null)
 480:       splitPaneUI.dragDividerTo(location);
 481:   }
 482: 
 483:   /**
 484:    * Finishes a dragging gesture by calling the <code>finishDraggingTo</code>
 485:    * method of the UI delegate of the enclosing <code>JSplitPane</code>.
 486:    *
 487:    * @param location the new, final location of the divider.
 488:    *
 489:    * @see BasicSplitPaneUI#finishDraggingTo(int location)
 490:    */
 491:   protected void finishDraggingTo(int location)
 492:   {
 493:     if (splitPaneUI != null)
 494:       splitPaneUI.finishDraggingTo(location);
 495:   }
 496: 
 497:   /**
 498:    * This helper method moves the divider to one of the  three locations when
 499:    * using one touch expand buttons. Location 0 is the left (or top) most
 500:    * location. Location 1 is the middle. Location 2 is the right (or bottom)
 501:    * most location.
 502:    * This is package-private to avoid an accessor method.
 503:    *
 504:    * @param locationIndex The location to move to.
 505:    */
 506:   void moveDividerTo(int locationIndex)
 507:   {
 508:     Insets insets = splitPane.getInsets();
 509:     switch (locationIndex)
 510:       {
 511:       case 1:
 512:     splitPane.setDividerLocation(splitPane.getLastDividerLocation());
 513:     break;
 514:       case 0:
 515:     int top = (orientation == JSplitPane.HORIZONTAL_SPLIT) ? insets.left
 516:                                                            : insets.top;
 517:     splitPane.setDividerLocation(top);
 518:     break;
 519:       case 2:
 520:     int bottom;
 521:     if (orientation == JSplitPane.HORIZONTAL_SPLIT)
 522:       bottom = splitPane.getBounds().width - insets.right - dividerSize;
 523:     else
 524:       bottom = splitPane.getBounds().height - insets.bottom - dividerSize;
 525:     splitPane.setDividerLocation(bottom);
 526:     break;
 527:       }
 528:   }
 529: 
 530:   /**
 531:    * The listener for handling mouse events from both the divider and the
 532:    * containing <code>JSplitPane</code>.
 533:    * 
 534:    * <p>
 535:    * The reason for also handling MouseEvents from the containing
 536:    * <code>JSplitPane</code> is that users should be able to start a drag
 537:    * gesture from inside the JSplitPane, but slightly outisde the divider.
 538:    * </p>
 539:    *
 540:    * @author Sascha Brawer (brawer_AT_dandelis.ch)
 541:    */
 542:   protected class MouseHandler extends MouseAdapter
 543:     implements MouseMotionListener
 544:   {
 545:     /** Keeps track of whether a drag is occurring. */
 546:     private transient boolean isDragging;
 547: 
 548:     /**
 549:      * This method is called when the mouse is pressed.
 550:      *
 551:      * @param e The MouseEvent.
 552:      */
 553:     public void mousePressed(MouseEvent e)
 554:     {
 555:       if (splitPane.isOneTouchExpandable())
 556:         {
 557:       if (e.getSource() == leftButton)
 558:         {
 559:           currentDividerLocation--;
 560:           if (currentDividerLocation < 0)
 561:         currentDividerLocation = 0;
 562:           moveDividerTo(currentDividerLocation);
 563:           return;
 564:         }
 565:       else if (e.getSource() == rightButton)
 566:         {
 567:           currentDividerLocation++;
 568:           if (currentDividerLocation > 2)
 569:         currentDividerLocation = 2;
 570:           moveDividerTo(currentDividerLocation);
 571:           return;
 572:         }
 573:         }
 574:       isDragging = true;
 575:       currentDividerLocation = 1;
 576:       if (orientation == JSplitPane.HORIZONTAL_SPLIT)
 577:     dragger = new DragController(e);
 578:       else
 579:     dragger = new VerticalDragController(e);
 580:       prepareForDragging();
 581:     }
 582: 
 583:     /**
 584:      * This method is called when the mouse is released.
 585:      *
 586:      * @param e The MouseEvent.
 587:      */
 588:     public void mouseReleased(MouseEvent e)
 589:     {
 590:       if (isDragging)
 591:         dragger.completeDrag(e);
 592:       isDragging = false;
 593:     }
 594: 
 595:     /**
 596:      * Repeatedly invoked when the user is dragging the mouse cursor while
 597:      * having pressed a mouse button.
 598:      *
 599:      * @param e The MouseEvent.
 600:      */
 601:     public void mouseDragged(MouseEvent e)
 602:     {
 603:       if (dragger != null)
 604:         dragger.continueDrag(e);
 605:     }
 606: 
 607:     /**
 608:      * Repeatedly invoked when the user is dragging the mouse cursor without
 609:      * having pressed a mouse button.
 610:      *
 611:      * @param e The MouseEvent.
 612:      */
 613:     public void mouseMoved(MouseEvent e)
 614:     {
 615:       // Do nothing.
 616:     }
 617:   }
 618: 
 619:   /**
 620:    * Performs the tasks associated with an ongoing drag operation.
 621:    *
 622:    * @author Sascha Brawer (brawer_AT_dandelis.ch)
 623:    */
 624:   protected class DragController
 625:   {
 626:     /**
 627:      * The difference between where the mouse is clicked and the  initial
 628:      * divider location.
 629:      */
 630:     transient int offset;
 631: 
 632:     /**
 633:      * Creates a new DragController object.
 634:      *
 635:      * @param e The MouseEvent to initialize with.
 636:      */
 637:     protected DragController(MouseEvent e)
 638:     {
 639:       offset = e.getX();
 640:     }
 641: 
 642:     /**
 643:      * This method returns true if the divider can move.
 644:      *
 645:      * @return True if dragging is allowed.
 646:      */
 647:     protected boolean isValid()
 648:     {
 649:       // Views can always be resized?
 650:       return true;
 651:     }
 652: 
 653:     /**
 654:      * Returns a position for the divider given the MouseEvent.
 655:      *
 656:      * @param e MouseEvent.
 657:      *
 658:      * @return The position for the divider to move to.
 659:      */
 660:     protected int positionForMouseEvent(MouseEvent e)
 661:     {
 662:       return e.getX() + getX() - offset;
 663:     }
 664: 
 665:     /**
 666:      * This method returns one of the two paramters for the orientation. In
 667:      * this case, it returns x.
 668:      *
 669:      * @param x The x coordinate.
 670:      * @param y The y coordinate.
 671:      *
 672:      * @return The x coordinate.
 673:      */
 674:     protected int getNeededLocation(int x, int y)
 675:     {
 676:       return x;
 677:     }
 678: 
 679:     /**
 680:      * This method is called to pass on the drag information to the UI through
 681:      * dragDividerTo.
 682:      *
 683:      * @param newX The x coordinate of the MouseEvent.
 684:      * @param newY The y coordinate of the MouseEvent.
 685:      */
 686:     protected void continueDrag(int newX, int newY)
 687:     {
 688:       if (isValid())
 689:     dragDividerTo(adjust(newX, newY));
 690:     }
 691: 
 692:     /**
 693:      * This method is called to pass on the drag information  to the UI
 694:      * through dragDividerTo.
 695:      *
 696:      * @param e The MouseEvent.
 697:      */
 698:     protected void continueDrag(MouseEvent e)
 699:     {
 700:       if (isValid())
 701:     dragDividerTo(positionForMouseEvent(e));
 702:     }
 703: 
 704:     /**
 705:      * This method is called to finish the drag session  by calling
 706:      * finishDraggingTo.
 707:      *
 708:      * @param x The x coordinate of the MouseEvent.
 709:      * @param y The y coordinate of the MouseEvent.
 710:      */
 711:     protected void completeDrag(int x, int y)
 712:     {
 713:       finishDraggingTo(adjust(x, y));
 714:     }
 715: 
 716:     /**
 717:      * This method is called to finish the drag session  by calling
 718:      * finishDraggingTo.
 719:      *
 720:      * @param e The MouseEvent.
 721:      */
 722:     protected void completeDrag(MouseEvent e)
 723:     {
 724:       finishDraggingTo(positionForMouseEvent(e));
 725:     }
 726: 
 727:     /**
 728:      * This is a helper method that includes the offset in the needed
 729:      * location.
 730:      *
 731:      * @param x The x coordinate of the MouseEvent.
 732:      * @param y The y coordinate of the MouseEvent.
 733:      *
 734:      * @return The needed location adjusted by the offsets.
 735:      */
 736:     int adjust(int x, int y)
 737:     {
 738:       return getNeededLocation(x, y) + getX() - offset;
 739:     }
 740:   }
 741: 
 742:   /**
 743:    * This is a helper class that controls dragging when  the orientation is
 744:    * VERTICAL_SPLIT.
 745:    */
 746:   protected class VerticalDragController extends DragController
 747:   {
 748:     /**
 749:      * Creates a new VerticalDragController object.
 750:      *
 751:      * @param e The MouseEvent to initialize with.
 752:      */
 753:     protected VerticalDragController(MouseEvent e)
 754:     {
 755:       super(e);
 756:       offset = e.getY();
 757:     }
 758: 
 759:     /**
 760:      * This method returns one of the two parameters given the orientation. In
 761:      * this case, it returns y.
 762:      *
 763:      * @param x The x coordinate of the MouseEvent.
 764:      * @param y The y coordinate of the MouseEvent.
 765:      *
 766:      * @return The y coordinate.
 767:      */
 768:     protected int getNeededLocation(int x, int y)
 769:     {
 770:       return y;
 771:     }
 772: 
 773:     /**
 774:      * This method returns the new location of the divider given a MouseEvent.
 775:      *
 776:      * @param e The MouseEvent.
 777:      *
 778:      * @return The new location of the divider.
 779:      */
 780:     protected int positionForMouseEvent(MouseEvent e)
 781:     {
 782:       return e.getY() + getY() - offset;
 783:     }
 784: 
 785:     /**
 786:      * This is a helper method that includes the offset in the needed
 787:      * location.
 788:      *
 789:      * @param x The x coordinate of the MouseEvent.
 790:      * @param y The y coordinate of the MouseEvent.
 791:      *
 792:      * @return The needed location adjusted by the offsets.
 793:      */
 794:     int adjust(int x, int y)
 795:     {
 796:       return getNeededLocation(x, y) + getY() - offset;
 797:     }
 798:   }
 799: 
 800:   /**
 801:    * This helper class acts as the Layout Manager for the divider.
 802:    */
 803:   protected class DividerLayout implements LayoutManager
 804:   {
 805:     /**
 806:      * Creates a new DividerLayout object.
 807:      */
 808:     protected DividerLayout()
 809:     {
 810:       // Nothing to do here.
 811:     }
 812: 
 813:     /**
 814:      * This method is called when a Component is added.
 815:      *
 816:      * @param string The constraints string.
 817:      * @param c The Component to add.
 818:      */
 819:     public void addLayoutComponent(String string, Component c)
 820:     {
 821:       // Do nothing.
 822:     }
 823: 
 824:     /**
 825:      * This method is called to lay out the container.
 826:      *
 827:      * @param c The container to lay out.
 828:      */
 829:     public void layoutContainer(Container c)
 830:     {
 831:       if (splitPane.isOneTouchExpandable())
 832:         {
 833:       changeButtonOrientation();
 834:       positionButtons();
 835:         }
 836:     }
 837: 
 838:     /**
 839:      * This method returns the minimum layout size.
 840:      *
 841:      * @param c The container to calculate for.
 842:      *
 843:      * @return The minimum layout size.
 844:      */
 845:     public Dimension minimumLayoutSize(Container c)
 846:     {
 847:       return preferredLayoutSize(c);
 848:     }
 849: 
 850:     /**
 851:      * This method returns the preferred layout size.
 852:      *
 853:      * @param c The container to calculate for.
 854:      *
 855:      * @return The preferred layout size.
 856:      */
 857:     public Dimension preferredLayoutSize(Container c)
 858:     {
 859:       return new Dimension(dividerSize, dividerSize);
 860:     }
 861: 
 862:     /**
 863:      * This method is called when a component is removed.
 864:      *
 865:      * @param c The component to remove.
 866:      */
 867:     public void removeLayoutComponent(Component c)
 868:     {
 869:       // Do nothing.
 870:     }
 871: 
 872:     /**
 873:      * This method changes the button orientation when the orientation of the
 874:      * SplitPane changes.
 875:      */
 876:     private void changeButtonOrientation()
 877:     {
 878:       if (orientation == JSplitPane.HORIZONTAL_SPLIT)
 879:         {
 880:       ((BasicArrowButton) rightButton).setDirection(SwingConstants.EAST);
 881:       ((BasicArrowButton) leftButton).setDirection(SwingConstants.WEST);
 882:         }
 883:       else
 884:         {
 885:       ((BasicArrowButton) rightButton).setDirection(SwingConstants.SOUTH);
 886:       ((BasicArrowButton) leftButton).setDirection(SwingConstants.NORTH);
 887:         }
 888:     }
 889: 
 890:     /**
 891:      * This method sizes and positions the buttons.
 892:      */
 893:     private void positionButtons()
 894:     {
 895:       int w = 0;
 896:       int h = 0;
 897:       if (orientation == JSplitPane.HORIZONTAL_SPLIT)
 898:         {
 899:       rightButton.setLocation(ONE_TOUCH_OFFSET, ONE_TOUCH_OFFSET);
 900:       leftButton.setLocation(ONE_TOUCH_OFFSET,
 901:                              ONE_TOUCH_OFFSET + 2 * ONE_TOUCH_SIZE);
 902:       w = dividerSize - 2 * ONE_TOUCH_OFFSET;
 903:       h = 2 * ONE_TOUCH_SIZE;
 904:         }
 905:       else
 906:         {
 907:       leftButton.setLocation(ONE_TOUCH_OFFSET, ONE_TOUCH_OFFSET);
 908:       rightButton.setLocation(ONE_TOUCH_OFFSET + 2 * ONE_TOUCH_SIZE,
 909:                               ONE_TOUCH_OFFSET);
 910:       h = dividerSize - 2 * ONE_TOUCH_OFFSET;
 911:       w = 2 * ONE_TOUCH_SIZE;
 912:         }
 913:       Dimension dims = new Dimension(w, h);
 914:       leftButton.setSize(dims);
 915:       rightButton.setSize(dims);
 916:     }
 917:   }
 918: }