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