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.ActionEvent;
  49: import java.awt.event.ActionListener;
  50: import java.awt.event.MouseAdapter;
  51: import java.awt.event.MouseEvent;
  52: import java.awt.event.MouseMotionListener;
  53: import java.beans.PropertyChangeEvent;
  54: import java.beans.PropertyChangeListener;
  55: 
  56: import javax.swing.JButton;
  57: import javax.swing.JSplitPane;
  58: import javax.swing.UIManager;
  59: import javax.swing.border.Border;
  60: 
  61: /**
  62:  * The divider that separates the two parts of a JSplitPane in the Basic look
  63:  * and feel.
  64:  * 
  65:  * <p>
  66:  * Implementation status: We do not have a real implementation yet. Currently,
  67:  * it is mostly a stub to allow compiling other parts of the
  68:  * javax.swing.plaf.basic package, although some parts are already
  69:  * functional.
  70:  * </p>
  71:  *
  72:  * @author Sascha Brawer (brawer_AT_dandelis.ch)
  73:  */
  74: public class BasicSplitPaneDivider extends Container
  75:   implements PropertyChangeListener
  76: {
  77:   /**
  78:    * The buttons used as one touch buttons.
  79:    */
  80:   private class BasicOneTouchButton
  81:     extends JButton
  82:   {
  83:     /**
  84:      * Denotes a left button.
  85:      */
  86:     static final int LEFT = 0;
  87: 
  88:     /**
  89:      * Denotes a right button.
  90:      */
  91:     static final int RIGHT = 1;
  92: 
  93:     /**
  94:      * The x points for the arrow.
  95:      */
  96:     private int[] xpoints;
  97: 
  98:     /**
  99:      * 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: }