1 /* BasicSplitPaneUI.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.Canvas; 42 import java.awt.Color; 43 import java.awt.Component; 44 import java.awt.Container; 45 import java.awt.Dimension; 46 import java.awt.Graphics; 47 import java.awt.Insets; 48 import java.awt.LayoutManager2; 49 import java.awt.Point; 50 import java.awt.event.ActionEvent; 51 import java.awt.event.ActionListener; 52 import java.awt.event.FocusAdapter; 53 import java.awt.event.FocusEvent; 54 import java.awt.event.FocusListener; 55 import java.beans.PropertyChangeEvent; 56 import java.beans.PropertyChangeListener; 57 58 import javax.swing.JComponent; 59 import javax.swing.JSplitPane; 60 import javax.swing.KeyStroke; 61 import javax.swing.LookAndFeel; 62 import javax.swing.UIManager; 63 import javax.swing.plaf.ComponentUI; 64 import javax.swing.plaf.SplitPaneUI; 65 66 /** 67 * This is the Basic Look and Feel implementation of the SplitPaneUI class. 68 */ 69 public class BasicSplitPaneUI extends SplitPaneUI 70 { 71 /** 72 * This Layout Manager controls the position and size of the components when 73 * the JSplitPane's orientation is HORIZONTAL_SPLIT. 74 * 75 * @specnote Apparently this class was intended to be protected, 76 * but was made public by a compiler bug and is now 77 * public for compatibility. 78 */ 79 public class BasicHorizontalLayoutManager implements LayoutManager2 80 { 81 // 3 components at a time. 82 // LEFT/TOP = 0 83 // RIGHT/BOTTOM = 1 84 // DIVIDER = 2 85 86 /** 87 * This array contains the components in the JSplitPane. The left/top 88 * component is at index 0, the right/bottom is at 1, and the divider is 89 * at 2. 90 */ 91 protected Component[] components = new Component[3]; 92 93 // These are the _current_ widths of the associated component. 94 95 /** 96 * This array contains the current width (for HORIZONTAL_SPLIT) or height 97 * (for VERTICAL_SPLIT) of the components. The indices are the same as 98 * for components. 99 */ 100 protected int[] sizes = new int[3]; 101 102 /** 103 * This method adds the component given to the JSplitPane. The position of 104 * the component is given by the constraints object. 105 * 106 * @param comp The Component to add. 107 * @param constraints The constraints that bind the object. 108 */ addLayoutComponent(Component comp, Object constraints)109 public void addLayoutComponent(Component comp, Object constraints) 110 { 111 addLayoutComponent((String) constraints, comp); 112 } 113 114 /** 115 * This method is called to add a Component to the JSplitPane. The 116 * placement string determines where the Component will be placed. The 117 * string should be one of LEFT, RIGHT, TOP, BOTTOM or null (signals that 118 * the component is the divider). 119 * 120 * @param place The placement of the Component. 121 * @param component The Component to add. 122 * 123 * @throws IllegalArgumentException DOCUMENT ME! 124 */ addLayoutComponent(String place, Component component)125 public void addLayoutComponent(String place, Component component) 126 { 127 int i = 0; 128 if (place == null) 129 i = 2; 130 else if (place.equals(JSplitPane.TOP) || place.equals(JSplitPane.LEFT)) 131 i = 0; 132 else if (place.equals(JSplitPane.BOTTOM) 133 || place.equals(JSplitPane.RIGHT)) 134 i = 1; 135 else 136 throw new IllegalArgumentException("Illegal placement in JSplitPane"); 137 components[i] = component; 138 resetSizeAt(i); 139 splitPane.revalidate(); 140 splitPane.repaint(); 141 } 142 143 /** 144 * This method returns the width of the JSplitPane minus the insets. 145 * 146 * @param containerSize The Dimensions of the JSplitPane. 147 * @param insets The Insets of the JSplitPane. 148 * 149 * @return The width of the JSplitPane minus the insets. 150 */ getAvailableSize(Dimension containerSize, Insets insets)151 protected int getAvailableSize(Dimension containerSize, Insets insets) 152 { 153 return containerSize.width - insets.left - insets.right; 154 } 155 156 /** 157 * This method returns the given insets left value. If the given inset is 158 * null, then 0 is returned. 159 * 160 * @param insets The Insets to use with the JSplitPane. 161 * 162 * @return The inset's left value. 163 */ getInitialLocation(Insets insets)164 protected int getInitialLocation(Insets insets) 165 { 166 if (insets != null) 167 return insets.left; 168 return 0; 169 } 170 171 /** 172 * This specifies how a component is aligned with respect to other 173 * components in the x fdirection. 174 * 175 * @param target The container. 176 * 177 * @return The component's alignment. 178 */ getLayoutAlignmentX(Container target)179 public float getLayoutAlignmentX(Container target) 180 { 181 return target.getAlignmentX(); 182 } 183 184 /** 185 * This specifies how a component is aligned with respect to other 186 * components in the y direction. 187 * 188 * @param target The container. 189 * 190 * @return The component's alignment. 191 */ getLayoutAlignmentY(Container target)192 public float getLayoutAlignmentY(Container target) 193 { 194 return target.getAlignmentY(); 195 } 196 197 /** 198 * This method returns the preferred width of the component. 199 * 200 * @param c The component to measure. 201 * 202 * @return The preferred width of the component. 203 */ getPreferredSizeOfComponent(Component c)204 protected int getPreferredSizeOfComponent(Component c) 205 { 206 Dimension dims = c.getPreferredSize(); 207 if (dims != null) 208 return dims.width; 209 return 0; 210 } 211 212 /** 213 * This method returns the current width of the component. 214 * 215 * @param c The component to measure. 216 * 217 * @return The width of the component. 218 */ getSizeOfComponent(Component c)219 protected int getSizeOfComponent(Component c) 220 { 221 return c.getWidth(); 222 } 223 224 /** 225 * This method returns the sizes array. 226 * 227 * @return The sizes array. 228 */ getSizes()229 protected int[] getSizes() 230 { 231 return sizes; 232 } 233 234 /** 235 * This method invalidates the layout. It does nothing. 236 * 237 * @param c The container to invalidate. 238 */ invalidateLayout(Container c)239 public void invalidateLayout(Container c) 240 { 241 // DO NOTHING 242 } 243 244 /** 245 * This method lays out the components in the container. 246 * 247 * @param container The container to lay out. 248 */ layoutContainer(Container container)249 public void layoutContainer(Container container) 250 { 251 if (container instanceof JSplitPane) 252 { 253 JSplitPane split = (JSplitPane) container; 254 distributeExtraSpace(); 255 Insets insets = split.getInsets(); 256 int width = getInitialLocation(insets); 257 Dimension dims = split.getSize(); 258 for (int i = 0; i < components.length; i += 2) 259 { 260 if (components[i] == null) 261 continue; 262 setComponentToSize(components[i], sizes[i], width, insets, dims); 263 width += sizes[i]; 264 } 265 if (components[1] != null) 266 { 267 setComponentToSize(components[1], sizes[1], width, insets, dims); 268 width += sizes[1]; 269 } 270 } 271 } 272 273 /** 274 * This method returns the maximum size for the container given the 275 * components. It returns a new Dimension object that has width and 276 * height equal to Integer.MAX_VALUE. 277 * 278 * @param target The container to measure. 279 * 280 * @return The maximum size. 281 */ maximumLayoutSize(Container target)282 public Dimension maximumLayoutSize(Container target) 283 { 284 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 285 } 286 287 /** 288 * This method returns the container's minimum size. The minimum width is 289 * the sum of all the component's minimum widths. The minimum height is 290 * the maximum of all the components' minimum heights. 291 * 292 * @param target The container to measure. 293 * 294 * @return The minimum size. 295 */ minimumLayoutSize(Container target)296 public Dimension minimumLayoutSize(Container target) 297 { 298 if (target instanceof JSplitPane) 299 { 300 JSplitPane split = (JSplitPane) target; 301 Insets insets = target.getInsets(); 302 303 int height = 0; 304 int width = 0; 305 for (int i = 0; i < components.length; i++) 306 { 307 if (components[i] == null) 308 continue; 309 Dimension dims = components[i].getMinimumSize(); 310 if (dims != null) 311 { 312 width += dims.width; 313 height = Math.max(height, dims.height); 314 } 315 } 316 return new Dimension(width, height); 317 } 318 return null; 319 } 320 321 /** 322 * This method returns the container's preferred size. The preferred width 323 * is the sum of all the component's preferred widths. The preferred 324 * height is the maximum of all the components' preferred heights. 325 * 326 * @param target The container to measure. 327 * 328 * @return The preferred size. 329 */ preferredLayoutSize(Container target)330 public Dimension preferredLayoutSize(Container target) 331 { 332 if (target instanceof JSplitPane) 333 { 334 JSplitPane split = (JSplitPane) target; 335 Insets insets = target.getInsets(); 336 337 int height = 0; 338 int width = 0; 339 for (int i = 0; i < components.length; i++) 340 { 341 if (components[i] == null) 342 continue; 343 Dimension dims = components[i].getPreferredSize(); 344 if (dims != null) 345 { 346 width += dims.width; 347 if (! (components[i] instanceof BasicSplitPaneDivider)) 348 height = Math.max(height, dims.height); 349 } 350 } 351 return new Dimension(width, height); 352 } 353 return null; 354 } 355 356 /** 357 * This method removes the component from the layout. 358 * 359 * @param component The component to remove from the layout. 360 */ removeLayoutComponent(Component component)361 public void removeLayoutComponent(Component component) 362 { 363 for (int i = 0; i < components.length; i++) 364 { 365 if (component == components[i]) 366 { 367 components[i] = null; 368 sizes[i] = 0; 369 } 370 } 371 } 372 373 /** 374 * This method resets the size of Component to the preferred size. 375 * 376 * @param index The index of the component to reset. 377 */ resetSizeAt(int index)378 protected void resetSizeAt(int index) 379 { 380 if (components[index] != null) 381 sizes[index] = getPreferredSizeOfComponent(components[index]); 382 } 383 384 /** 385 * This method resets the sizes of all the components. 386 */ resetToPreferredSizes()387 public void resetToPreferredSizes() 388 { 389 for (int i = 0; i < components.length; i++) 390 resetSizeAt(i); 391 } 392 393 /** 394 * This methods sets the bounds of the given component. The width is the 395 * size. The height is the container size minus the top and bottom 396 * inset. The x coordinate is the location given. The y coordinate is 397 * the top inset. 398 * 399 * @param c The component to set. 400 * @param size The width of the component. 401 * @param location The x coordinate. 402 * @param insets The insets to use. 403 * @param containerSize The height of the container. 404 */ setComponentToSize(Component c, int size, int location, Insets insets, Dimension containerSize)405 protected void setComponentToSize(Component c, int size, int location, 406 Insets insets, Dimension containerSize) 407 { 408 int w = size; 409 int h = containerSize.height - insets.top - insets.bottom; 410 int x = location; 411 int y = insets.top; 412 c.setBounds(x, y, w, h); 413 } 414 415 /** 416 * This method stores the given int array as the new sizes array. 417 * 418 * @param newSizes The array to use as sizes. 419 */ setSizes(int[] newSizes)420 protected void setSizes(int[] newSizes) 421 { 422 sizes = newSizes; 423 } 424 425 /** 426 * This method determines the size of each component. It should be called 427 * when a new Layout Manager is created for an existing JSplitPane. 428 */ updateComponents()429 protected void updateComponents() 430 { 431 Component left = splitPane.getLeftComponent(); 432 Component right = splitPane.getRightComponent(); 433 434 if (left != null) 435 { 436 components[0] = left; 437 resetSizeAt(0); 438 } 439 if (right != null) 440 { 441 components[1] = right; 442 resetSizeAt(1); 443 } 444 components[2] = divider; 445 resetSizeAt(2); 446 } 447 448 /** 449 * This method resizes the left and right components to fit inside the 450 * JSplitPane when there is extra space. 451 */ distributeExtraSpace()452 void distributeExtraSpace() 453 { 454 int availSize = getAvailableSize(splitPane.getSize(), 455 splitPane.getInsets()); 456 int[] newSizes = new int[3]; 457 double weight = splitPane.getResizeWeight(); 458 459 int oldLen = sizes[0] + sizes[1]; 460 461 // dividers don't change size. 462 availSize -= sizes[2] + oldLen; 463 464 int rightAlloc = (int) (availSize * (1 - weight)); 465 int leftAlloc = availSize - rightAlloc; 466 467 sizes[0] += leftAlloc; 468 sizes[1] += rightAlloc; 469 } 470 471 /** 472 * This method returns the minimum width of the component at the given 473 * index. 474 * 475 * @param index The index to check. 476 * 477 * @return The minimum width. 478 */ minimumSizeOfComponent(int index)479 int minimumSizeOfComponent(int index) 480 { 481 Dimension dims = components[index].getMinimumSize(); 482 if (dims != null) 483 return dims.width; 484 else 485 return 0; 486 } 487 } //end BasicHorizontalLayoutManager 488 489 /** 490 * This class is the Layout Manager for the JSplitPane when the orientation 491 * is VERTICAL_SPLIT. 492 * 493 * @specnote Apparently this class was intended to be protected, 494 * but was made public by a compiler bug and is now 495 * public for compatibility. 496 */ 497 public class BasicVerticalLayoutManager 498 extends BasicHorizontalLayoutManager 499 { 500 /** 501 * This method returns the height of the container minus the top and 502 * bottom inset. 503 * 504 * @param containerSize The size of the container. 505 * @param insets The insets of the container. 506 * 507 * @return The height minus top and bottom inset. 508 */ getAvailableSize(Dimension containerSize, Insets insets)509 protected int getAvailableSize(Dimension containerSize, Insets insets) 510 { 511 return containerSize.height - insets.top - insets.bottom; 512 } 513 514 /** 515 * This method returns the top inset. 516 * 517 * @param insets The Insets to use. 518 * 519 * @return The top inset. 520 */ getInitialLocation(Insets insets)521 protected int getInitialLocation(Insets insets) 522 { 523 return insets.top; 524 } 525 526 /** 527 * This method returns the preferred height of the component. 528 * 529 * @param c The component to measure. 530 * 531 * @return The preferred height of the component. 532 */ getPreferredSizeOfComponent(Component c)533 protected int getPreferredSizeOfComponent(Component c) 534 { 535 Dimension dims = c.getPreferredSize(); 536 if (dims != null) 537 return dims.height; 538 return 0; 539 } 540 541 /** 542 * This method returns the current height of the component. 543 * 544 * @param c The component to measure. 545 * 546 * @return The current height of the component. 547 */ getSizeOfComponent(Component c)548 protected int getSizeOfComponent(Component c) 549 { 550 return c.getHeight(); 551 } 552 553 /** 554 * This method returns the minimum layout size. The minimum height is the 555 * sum of all the components' minimum heights. The minimum width is the 556 * maximum of all the components' minimum widths. 557 * 558 * @param container The container to measure. 559 * 560 * @return The minimum size. 561 */ minimumLayoutSize(Container container)562 public Dimension minimumLayoutSize(Container container) 563 { 564 if (container instanceof JSplitPane) 565 { 566 JSplitPane split = (JSplitPane) container; 567 Insets insets = container.getInsets(); 568 569 int height = 0; 570 int width = 0; 571 for (int i = 0; i < components.length; i++) 572 { 573 if (components[i] == null) 574 continue; 575 Dimension dims = components[i].getMinimumSize(); 576 if (dims != null) 577 { 578 height += dims.height; 579 width = Math.max(width, dims.width); 580 } 581 } 582 return new Dimension(width, height); 583 } 584 return null; 585 } 586 587 /** 588 * This method returns the preferred layout size. The preferred height is 589 * the sum of all the components' preferred heights. The preferred width 590 * is the maximum of all the components' preferred widths. 591 * 592 * @param container The container to measure. 593 * 594 * @return The preferred size. 595 */ preferredLayoutSize(Container container)596 public Dimension preferredLayoutSize(Container container) 597 { 598 if (container instanceof JSplitPane) 599 { 600 JSplitPane split = (JSplitPane) container; 601 Insets insets = container.getInsets(); 602 603 int height = 0; 604 int width = 0; 605 for (int i = 0; i < components.length; i++) 606 { 607 if (components[i] == null) 608 continue; 609 Dimension dims = components[i].getPreferredSize(); 610 if (dims != null) 611 { 612 height += dims.height; 613 width = Math.max(width, dims.width); 614 } 615 } 616 return new Dimension(width, height); 617 } 618 return null; 619 } 620 621 /** 622 * This method sets the bounds of the given component. The y coordinate is 623 * the location given. The x coordinate is the left inset. The height is 624 * the size given. The width is the container size minus the left and 625 * right inset. 626 * 627 * @param c The component to set bounds for. 628 * @param size The height. 629 * @param location The y coordinate. 630 * @param insets The insets to use. 631 * @param containerSize The container's size. 632 */ setComponentToSize(Component c, int size, int location, Insets insets, Dimension containerSize)633 protected void setComponentToSize(Component c, int size, int location, 634 Insets insets, Dimension containerSize) 635 { 636 int y = location; 637 int x = insets.left; 638 int h = size; 639 int w = containerSize.width - insets.left - insets.right; 640 c.setBounds(x, y, w, h); 641 } 642 643 /** 644 * This method returns the minimum height of the component at the given 645 * index. 646 * 647 * @param index The index of the component to check. 648 * 649 * @return The minimum height of the given component. 650 */ minimumSizeOfComponent(int index)651 int minimumSizeOfComponent(int index) 652 { 653 Dimension dims = components[index].getMinimumSize(); 654 if (dims != null) 655 return dims.height; 656 else 657 return 0; 658 } 659 } 660 661 /** 662 * This class handles FocusEvents from the JComponent. 663 * 664 * @specnote Apparently this class was intended to be protected, 665 * but was made public by a compiler bug and is now 666 * public for compatibility. 667 */ 668 public class FocusHandler extends FocusAdapter 669 { 670 /** 671 * This method is called when the JSplitPane gains focus. 672 * 673 * @param ev The FocusEvent. 674 */ focusGained(FocusEvent ev)675 public void focusGained(FocusEvent ev) 676 { 677 // FIXME: implement. 678 } 679 680 /** 681 * This method is called when the JSplitPane loses focus. 682 * 683 * @param ev The FocusEvent. 684 */ focusLost(FocusEvent ev)685 public void focusLost(FocusEvent ev) 686 { 687 // FIXME: implement. 688 } 689 } 690 691 /** 692 * This is a deprecated class. It is supposed to be used for handling down 693 * and right key presses. 694 * 695 * @specnote Apparently this class was intended to be protected, 696 * but was made public by a compiler bug and is now 697 * public for compatibility. 698 */ 699 public class KeyboardDownRightHandler implements ActionListener 700 { 701 /** 702 * This method is called when the down or right keys are pressed. 703 * 704 * @param ev The ActionEvent 705 */ actionPerformed(ActionEvent ev)706 public void actionPerformed(ActionEvent ev) 707 { 708 // FIXME: implement. 709 } 710 } 711 712 /** 713 * This is a deprecated class. It is supposed to be used for handling end 714 * key presses. 715 * 716 * @specnote Apparently this class was intended to be protected, 717 * but was made public by a compiler bug and is now 718 * public for compatibility. 719 */ 720 public class KeyboardEndHandler implements ActionListener 721 { 722 /** 723 * This method is called when the end key is pressed. 724 * 725 * @param ev The ActionEvent. 726 */ actionPerformed(ActionEvent ev)727 public void actionPerformed(ActionEvent ev) 728 { 729 // FIXME: implement. 730 } 731 } 732 733 /** 734 * This is a deprecated class. It is supposed to be used for handling home 735 * key presses. 736 * 737 * @specnote Apparently this class was intended to be protected, 738 * but was made public by a compiler bug and is now 739 * public for compatibility. 740 */ 741 public class KeyboardHomeHandler implements ActionListener 742 { 743 /** 744 * This method is called when the home key is pressed. 745 * 746 * @param ev The ActionEvent. 747 */ actionPerformed(ActionEvent ev)748 public void actionPerformed(ActionEvent ev) 749 { 750 // FIXME: implement. 751 } 752 } 753 754 /** 755 * This is a deprecated class. It is supposed to be used for handling resize 756 * toggles. 757 * 758 * @specnote Apparently this class was intended to be protected, 759 * but was made public by a compiler bug and is now 760 * public for compatibility. 761 */ 762 public class KeyboardResizeToggleHandler implements ActionListener 763 { 764 /** 765 * This method is called when a resize is toggled. 766 * 767 * @param ev The ActionEvent. 768 */ actionPerformed(ActionEvent ev)769 public void actionPerformed(ActionEvent ev) 770 { 771 // FIXME: implement. 772 } 773 } 774 775 /** 776 * This is a deprecated class. It is supposed to be used for handler up and 777 * left key presses. 778 * 779 * @specnote Apparently this class was intended to be protected, 780 * but was made public by a compiler bug and is now 781 * public for compatibility. 782 */ 783 public class KeyboardUpLeftHandler implements ActionListener 784 { 785 /** 786 * This method is called when the left or up keys are pressed. 787 * 788 * @param ev The ActionEvent. 789 */ actionPerformed(ActionEvent ev)790 public void actionPerformed(ActionEvent ev) 791 { 792 // FIXME: implement. 793 } 794 } 795 796 /** 797 * This helper class handles PropertyChangeEvents from the JSplitPane. When 798 * a property changes, this will update the UI accordingly. 799 * 800 * @specnote Apparently this class was intended to be protected, 801 * but was made public by a compiler bug and is now 802 * public for compatibility. 803 */ 804 public class PropertyHandler implements PropertyChangeListener 805 { 806 /** 807 * This method is called whenever one of the JSplitPane's properties 808 * change. 809 * 810 * @param e DOCUMENT ME! 811 */ propertyChange(PropertyChangeEvent e)812 public void propertyChange(PropertyChangeEvent e) 813 { 814 if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY)) 815 { 816 int newSize = splitPane.getDividerSize(); 817 int[] tmpSizes = layoutManager.getSizes(); 818 dividerSize = tmpSizes[2]; 819 int newSpace = newSize - tmpSizes[2]; 820 tmpSizes[2] = newSize; 821 822 tmpSizes[0] += newSpace / 2; 823 tmpSizes[1] += newSpace / 2; 824 825 layoutManager.setSizes(tmpSizes); 826 } 827 else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY)) 828 { 829 int max = layoutManager.getAvailableSize(splitPane.getSize(), 830 splitPane.getInsets()); 831 int dividerLoc = getDividerLocation(splitPane); 832 double prop = ((double) dividerLoc) / max; 833 834 resetLayoutManager(); 835 if (prop <= 1 && prop >= 0) 836 splitPane.setDividerLocation(prop); 837 } 838 layoutManager.layoutContainer(splitPane); 839 splitPane.repaint(); 840 // Don't have to deal with continuous_layout - only 841 // necessary in dragging modes (and it's checked 842 // every time you drag there) 843 // Don't have to deal with resize_weight (as there 844 // will be no extra space associated with this 845 // event - the changes to the weighting will 846 // be taken into account the next time the 847 // sizes change.) 848 // Don't have to deal with divider_location 849 // The method in JSplitPane calls our setDividerLocation 850 // so we'll know about those anyway. 851 // Don't have to deal with last_divider_location 852 // Although I'm not sure why, it doesn't seem to 853 // have any effect on Sun's JSplitPane. 854 // one_touch_expandable changes are dealt with 855 // by our divider. 856 } 857 } 858 859 /** The location of the divider when dragging began. */ 860 protected int beginDragDividerLocation; 861 862 /** The size of the divider while dragging. */ 863 protected int dividerSize; 864 865 /** The location where the last drag location ended. */ 866 transient int lastDragLocation = -1; 867 868 /** The distance the divider is moved when moved by keyboard actions. */ 869 // Sun defines this as 3 870 protected static int KEYBOARD_DIVIDER_MOVE_OFFSET = 3; 871 872 /** The divider that divides this JSplitPane. */ 873 protected BasicSplitPaneDivider divider; 874 875 /** The listener that listens for PropertyChangeEvents from the JSplitPane. */ 876 protected PropertyChangeListener propertyChangeListener; 877 878 /** The JSplitPane's focus handler. */ 879 protected FocusListener focusListener; 880 881 /** @deprecated The handler for down and right key presses. */ 882 protected ActionListener keyboardDownRightListener; 883 884 /** @deprecated The handler for end key presses. */ 885 protected ActionListener keyboardEndListener; 886 887 /** @deprecated The handler for home key presses. */ 888 protected ActionListener keyboardHomeListener; 889 890 /** @deprecated The handler for toggling resizes. */ 891 protected ActionListener keyboardResizeToggleListener; 892 893 /** @deprecated The handler for up and left key presses. */ 894 protected ActionListener keyboardUpLeftListener; 895 896 /** The JSplitPane's current layout manager. */ 897 protected BasicHorizontalLayoutManager layoutManager; 898 899 /** @deprecated The divider resize toggle key. */ 900 protected KeyStroke dividerResizeToggleKey; 901 902 /** @deprecated The down key. */ 903 protected KeyStroke downKey; 904 905 /** @deprecated The end key. */ 906 protected KeyStroke endKey; 907 908 /** @deprecated The home key. */ 909 protected KeyStroke homeKey; 910 911 /** @deprecated The left key. */ 912 protected KeyStroke leftKey; 913 914 /** @deprecated The right key. */ 915 protected KeyStroke rightKey; 916 917 /** @deprecated The up key. */ 918 protected KeyStroke upKey; 919 920 /** Set to true when dragging heavy weight components. */ 921 protected boolean draggingHW; 922 923 /** 924 * The constraints object used when adding the non-continuous divider to the 925 * JSplitPane. 926 */ 927 protected static final String NON_CONTINUOUS_DIVIDER 928 = "nonContinuousDivider"; 929 930 /** The dark divider used when dragging in non-continuous layout mode. */ 931 protected Component nonContinuousLayoutDivider; 932 933 /** The JSplitPane that this UI draws. */ 934 protected JSplitPane splitPane; 935 936 /** 937 * Creates a new BasicSplitPaneUI object. 938 */ BasicSplitPaneUI()939 public BasicSplitPaneUI() 940 { 941 // Nothing to do here. 942 } 943 944 /** 945 * This method creates a new BasicSplitPaneUI for the given JComponent. 946 * 947 * @param x The JComponent to create a UI for. 948 * 949 * @return A new BasicSplitPaneUI. 950 */ createUI(JComponent x)951 public static ComponentUI createUI(JComponent x) 952 { 953 return new BasicSplitPaneUI(); 954 } 955 956 /** 957 * This method installs the BasicSplitPaneUI for the given JComponent. 958 * 959 * @param c The JComponent to install the UI for. 960 */ installUI(JComponent c)961 public void installUI(JComponent c) 962 { 963 if (c instanceof JSplitPane) 964 { 965 splitPane = (JSplitPane) c; 966 installDefaults(); 967 installListeners(); 968 installKeyboardActions(); 969 } 970 } 971 972 /** 973 * This method uninstalls the BasicSplitPaneUI for the given JComponent. 974 * 975 * @param c The JComponent to uninstall the UI for. 976 */ uninstallUI(JComponent c)977 public void uninstallUI(JComponent c) 978 { 979 uninstallKeyboardActions(); 980 uninstallListeners(); 981 uninstallDefaults(); 982 983 splitPane = null; 984 } 985 986 /** 987 * This method installs the defaults given by the Look and Feel. 988 */ installDefaults()989 protected void installDefaults() 990 { 991 LookAndFeel.installColors(splitPane, "SplitPane.background", 992 "SplitPane.foreground"); 993 LookAndFeel.installBorder(splitPane, "SplitPane.border"); 994 divider = createDefaultDivider(); 995 resetLayoutManager(); 996 nonContinuousLayoutDivider = createDefaultNonContinuousLayoutDivider(); 997 splitPane.add(divider, JSplitPane.DIVIDER); 998 999 // There is no need to add the nonContinuousLayoutDivider 1000 splitPane.setDividerSize(UIManager.getInt("SplitPane.dividerSize")); 1001 splitPane.setOpaque(true); 1002 } 1003 1004 /** 1005 * This method uninstalls the defaults and nulls any objects created during 1006 * install. 1007 */ uninstallDefaults()1008 protected void uninstallDefaults() 1009 { 1010 layoutManager = null; 1011 splitPane.remove(divider); 1012 divider = null; 1013 nonContinuousLayoutDivider = null; 1014 1015 splitPane.setBackground(null); 1016 splitPane.setBorder(null); 1017 } 1018 1019 /** 1020 * This method installs the listeners needed for this UI to function. 1021 */ installListeners()1022 protected void installListeners() 1023 { 1024 propertyChangeListener = createPropertyChangeListener(); 1025 focusListener = createFocusListener(); 1026 1027 splitPane.addPropertyChangeListener(propertyChangeListener); 1028 splitPane.addFocusListener(focusListener); 1029 } 1030 1031 /** 1032 * This method uninstalls all listeners registered for the UI. 1033 */ uninstallListeners()1034 protected void uninstallListeners() 1035 { 1036 splitPane.removePropertyChangeListener(propertyChangeListener); 1037 splitPane.removeFocusListener(focusListener); 1038 1039 focusListener = null; 1040 propertyChangeListener = null; 1041 } 1042 1043 /** 1044 * This method installs the keyboard actions for the JSplitPane. 1045 */ installKeyboardActions()1046 protected void installKeyboardActions() 1047 { 1048 // FIXME: implement. 1049 } 1050 1051 /** 1052 * This method reverses the work done in installKeyboardActions. 1053 */ uninstallKeyboardActions()1054 protected void uninstallKeyboardActions() 1055 { 1056 // FIXME: implement. 1057 } 1058 1059 /** 1060 * This method creates a new PropertyChangeListener. 1061 * 1062 * @return A new PropertyChangeListener. 1063 */ createPropertyChangeListener()1064 protected PropertyChangeListener createPropertyChangeListener() 1065 { 1066 return new PropertyHandler(); 1067 } 1068 1069 /** 1070 * This method creates a new FocusListener. 1071 * 1072 * @return A new FocusListener. 1073 */ createFocusListener()1074 protected FocusListener createFocusListener() 1075 { 1076 return new FocusHandler(); 1077 } 1078 1079 /** 1080 * This method creates a new ActionListener for up and left key presses. 1081 * 1082 * @return A new ActionListener for up and left keys. 1083 * 1084 * @deprecated 1.3 1085 */ createKeyboardUpLeftListener()1086 protected ActionListener createKeyboardUpLeftListener() 1087 { 1088 return new KeyboardUpLeftHandler(); 1089 } 1090 1091 /** 1092 * This method creates a new ActionListener for down and right key presses. 1093 * 1094 * @return A new ActionListener for down and right keys. 1095 * 1096 * @deprecated 1.3 1097 */ createKeyboardDownRightListener()1098 protected ActionListener createKeyboardDownRightListener() 1099 { 1100 return new KeyboardDownRightHandler(); 1101 } 1102 1103 /** 1104 * This method creates a new ActionListener for home key presses. 1105 * 1106 * @return A new ActionListener for home keys. 1107 * 1108 * @deprecated 1109 */ createKeyboardHomeListener()1110 protected ActionListener createKeyboardHomeListener() 1111 { 1112 return new KeyboardHomeHandler(); 1113 } 1114 1115 /** 1116 * This method creates a new ActionListener for end key presses.i 1117 * 1118 * @return A new ActionListener for end keys. 1119 * 1120 * @deprecated 1.3 1121 */ createKeyboardEndListener()1122 protected ActionListener createKeyboardEndListener() 1123 { 1124 return new KeyboardEndHandler(); 1125 } 1126 1127 /** 1128 * This method creates a new ActionListener for resize toggle key events. 1129 * 1130 * @return A new ActionListener for resize toggle keys. 1131 * 1132 * @deprecated 1.3 1133 */ createKeyboardResizeToggleListener()1134 protected ActionListener createKeyboardResizeToggleListener() 1135 { 1136 return new KeyboardResizeToggleHandler(); 1137 } 1138 1139 /** 1140 * This method returns the orientation of the JSplitPane. 1141 * 1142 * @return The orientation of the JSplitPane. 1143 */ getOrientation()1144 public int getOrientation() 1145 { 1146 return splitPane.getOrientation(); 1147 } 1148 1149 /** 1150 * This method sets the orientation of the JSplitPane. 1151 * 1152 * @param orientation The new orientation of the JSplitPane. 1153 */ setOrientation(int orientation)1154 public void setOrientation(int orientation) 1155 { 1156 splitPane.setOrientation(orientation); 1157 } 1158 1159 /** 1160 * This method returns true if the JSplitPane is using continuous layout. 1161 * 1162 * @return True if the JSplitPane is using continuous layout. 1163 */ isContinuousLayout()1164 public boolean isContinuousLayout() 1165 { 1166 return splitPane.isContinuousLayout(); 1167 } 1168 1169 /** 1170 * This method sets the continuous layout property of the JSplitPane. 1171 * 1172 * @param b True if the JsplitPane is to use continuous layout. 1173 */ setContinuousLayout(boolean b)1174 public void setContinuousLayout(boolean b) 1175 { 1176 splitPane.setContinuousLayout(b); 1177 } 1178 1179 /** 1180 * This method returns the last location the divider was dragged to. 1181 * 1182 * @return The last location the divider was dragged to. 1183 */ getLastDragLocation()1184 public int getLastDragLocation() 1185 { 1186 return lastDragLocation; 1187 } 1188 1189 /** 1190 * This method sets the last location the divider was dragged to. 1191 * 1192 * @param l The last location the divider was dragged to. 1193 */ setLastDragLocation(int l)1194 public void setLastDragLocation(int l) 1195 { 1196 lastDragLocation = l; 1197 } 1198 1199 /** 1200 * This method returns the BasicSplitPaneDivider that divides this 1201 * JSplitPane. 1202 * 1203 * @return The divider for the JSplitPane. 1204 */ getDivider()1205 public BasicSplitPaneDivider getDivider() 1206 { 1207 return divider; 1208 } 1209 1210 /** 1211 * This method creates a nonContinuousLayoutDivider for use with the 1212 * JSplitPane in nonContinousLayout mode. The default divider is a gray 1213 * Canvas. 1214 * 1215 * @return The default nonContinousLayoutDivider. 1216 */ createDefaultNonContinuousLayoutDivider()1217 protected Component createDefaultNonContinuousLayoutDivider() 1218 { 1219 if (nonContinuousLayoutDivider == null) 1220 { 1221 nonContinuousLayoutDivider = new Canvas(); 1222 nonContinuousLayoutDivider.setBackground(Color.DARK_GRAY); 1223 } 1224 return nonContinuousLayoutDivider; 1225 } 1226 1227 /** 1228 * This method sets the component to use as the nonContinuousLayoutDivider. 1229 * 1230 * @param newDivider The component to use as the nonContinuousLayoutDivider. 1231 */ setNonContinuousLayoutDivider(Component newDivider)1232 protected void setNonContinuousLayoutDivider(Component newDivider) 1233 { 1234 setNonContinuousLayoutDivider(newDivider, true); 1235 } 1236 1237 /** 1238 * This method sets the component to use as the nonContinuousLayoutDivider. 1239 * 1240 * @param newDivider The component to use as the nonContinuousLayoutDivider. 1241 * @param rememberSizes FIXME: document. 1242 */ setNonContinuousLayoutDivider(Component newDivider, boolean rememberSizes)1243 protected void setNonContinuousLayoutDivider(Component newDivider, 1244 boolean rememberSizes) 1245 { 1246 // FIXME: use rememberSizes for something 1247 nonContinuousLayoutDivider = newDivider; 1248 } 1249 1250 /** 1251 * This method returns the nonContinuousLayoutDivider. 1252 * 1253 * @return The nonContinuousLayoutDivider. 1254 */ getNonContinuousLayoutDivider()1255 public Component getNonContinuousLayoutDivider() 1256 { 1257 return nonContinuousLayoutDivider; 1258 } 1259 1260 /** 1261 * This method returns the JSplitPane that this BasicSplitPaneUI draws. 1262 * 1263 * @return The JSplitPane. 1264 */ getSplitPane()1265 public JSplitPane getSplitPane() 1266 { 1267 return splitPane; 1268 } 1269 1270 /** 1271 * This method creates the divider used normally with the JSplitPane. 1272 * 1273 * @return The default divider. 1274 */ createDefaultDivider()1275 public BasicSplitPaneDivider createDefaultDivider() 1276 { 1277 if (divider == null) 1278 divider = new BasicSplitPaneDivider(this); 1279 return divider; 1280 } 1281 1282 /** 1283 * This method is called when JSplitPane's resetToPreferredSizes is called. 1284 * It resets the sizes of all components in the JSplitPane. 1285 * 1286 * @param jc The JSplitPane to reset. 1287 */ resetToPreferredSizes(JSplitPane jc)1288 public void resetToPreferredSizes(JSplitPane jc) 1289 { 1290 layoutManager.resetToPreferredSizes(); 1291 } 1292 1293 /** 1294 * This method sets the location of the divider. 1295 * 1296 * @param jc The JSplitPane to set the divider location in. 1297 * @param location The new location of the divider. 1298 */ setDividerLocation(JSplitPane jc, int location)1299 public void setDividerLocation(JSplitPane jc, int location) 1300 { 1301 location = validLocation(location); 1302 Container p = jc.getParent(); 1303 Dimension rightPrefSize = jc.getRightComponent().getPreferredSize(); 1304 Dimension size = jc.getSize(); 1305 // check if the size has been set for the splitpane 1306 if (size.width == 0 && size.height == 0) 1307 size = jc.getPreferredSize(); 1308 1309 if (getOrientation() == 0 && location > size.height) 1310 { 1311 location = size.height; 1312 while (p != null) 1313 { 1314 p.setSize(p.getWidth(), p.getHeight() + rightPrefSize.height); 1315 p = p.getParent(); 1316 } 1317 } 1318 else if (location > size.width) 1319 { 1320 location = size.width; 1321 while (p != null) 1322 { 1323 p.setSize(p.getWidth() + rightPrefSize.width, p.getHeight()); 1324 p = p.getParent(); 1325 } 1326 } 1327 1328 setLastDragLocation(getDividerLocation(splitPane)); 1329 splitPane.setLastDividerLocation(getDividerLocation(splitPane)); 1330 int[] tmpSizes = layoutManager.getSizes(); 1331 tmpSizes[0] = location 1332 - layoutManager.getInitialLocation(splitPane.getInsets()); 1333 tmpSizes[1] = layoutManager.getAvailableSize(splitPane.getSize(), 1334 splitPane.getInsets()) 1335 - tmpSizes[0]; 1336 layoutManager.setSizes(tmpSizes); 1337 splitPane.revalidate(); 1338 splitPane.repaint(); 1339 } 1340 1341 /** 1342 * This method returns the location of the divider. 1343 * 1344 * @param jc The JSplitPane to retrieve the location for. 1345 * 1346 * @return The location of the divider. 1347 */ getDividerLocation(JSplitPane jc)1348 public int getDividerLocation(JSplitPane jc) 1349 { 1350 return layoutManager.sizes[0] 1351 + layoutManager.getInitialLocation(splitPane.getInsets()); 1352 } 1353 1354 /** 1355 * This method returns the smallest value possible for the location of the 1356 * divider. 1357 * 1358 * @param jc The JSplitPane. 1359 * 1360 * @return The minimum divider location. 1361 */ getMinimumDividerLocation(JSplitPane jc)1362 public int getMinimumDividerLocation(JSplitPane jc) 1363 { 1364 int value = layoutManager.getInitialLocation(jc.getInsets()) 1365 - layoutManager.getAvailableSize(jc.getSize(), jc.getInsets()) 1366 + splitPane.getDividerSize(); 1367 if (layoutManager.components[1] != null) 1368 value += layoutManager.minimumSizeOfComponent(1); 1369 return value; 1370 } 1371 1372 /** 1373 * This method returns the largest value possible for the location of the 1374 * divider. 1375 * 1376 * @param jc The JSplitPane. 1377 * 1378 * @return The maximum divider location. 1379 */ getMaximumDividerLocation(JSplitPane jc)1380 public int getMaximumDividerLocation(JSplitPane jc) 1381 { 1382 int value = layoutManager.getInitialLocation(jc.getInsets()) 1383 + layoutManager.getAvailableSize(jc.getSize(), jc.getInsets()) 1384 - splitPane.getDividerSize(); 1385 if (layoutManager.components[1] != null) 1386 value -= layoutManager.minimumSizeOfComponent(1); 1387 return value; 1388 } 1389 1390 /** 1391 * This method is called after the children of the JSplitPane are painted. 1392 * 1393 * @param jc The JSplitPane. 1394 * @param g The Graphics object to paint with. 1395 */ finishedPaintingChildren(JSplitPane jc, Graphics g)1396 public void finishedPaintingChildren(JSplitPane jc, Graphics g) 1397 { 1398 if (! splitPane.isContinuousLayout() && nonContinuousLayoutDivider != null 1399 && nonContinuousLayoutDivider.isVisible()) 1400 javax.swing.SwingUtilities.paintComponent(g, nonContinuousLayoutDivider, 1401 null, 1402 nonContinuousLayoutDivider 1403 .getBounds()); 1404 } 1405 1406 /** 1407 * This method is called to paint the JSplitPane. 1408 * 1409 * @param g The Graphics object to paint with. 1410 * @param jc The JSplitPane to paint. 1411 */ paint(Graphics g, JComponent jc)1412 public void paint(Graphics g, JComponent jc) 1413 { 1414 // TODO: What should be done here? 1415 } 1416 1417 /** 1418 * This method returns the preferred size of the JSplitPane. 1419 * 1420 * @param jc The JSplitPane. 1421 * 1422 * @return The preferred size of the JSplitPane. 1423 */ getPreferredSize(JComponent jc)1424 public Dimension getPreferredSize(JComponent jc) 1425 { 1426 return layoutManager.preferredLayoutSize((Container) jc); 1427 } 1428 1429 /** 1430 * This method returns the minimum size of the JSplitPane. 1431 * 1432 * @param jc The JSplitPane. 1433 * 1434 * @return The minimum size of the JSplitPane. 1435 */ getMinimumSize(JComponent jc)1436 public Dimension getMinimumSize(JComponent jc) 1437 { 1438 return layoutManager.minimumLayoutSize((Container) jc); 1439 } 1440 1441 /** 1442 * This method returns the maximum size of the JSplitPane. 1443 * 1444 * @param jc The JSplitPane. 1445 * 1446 * @return The maximum size of the JSplitPane. 1447 */ getMaximumSize(JComponent jc)1448 public Dimension getMaximumSize(JComponent jc) 1449 { 1450 return layoutManager.maximumLayoutSize((Container) jc); 1451 } 1452 1453 /** 1454 * This method returns the border insets of the current border. 1455 * 1456 * @param jc The JSplitPane. 1457 * 1458 * @return The current border insets. 1459 */ getInsets(JComponent jc)1460 public Insets getInsets(JComponent jc) 1461 { 1462 return splitPane.getBorder().getBorderInsets(splitPane); 1463 } 1464 1465 /** 1466 * This method resets the current layout manager. The type of layout manager 1467 * is dependent on the current orientation. 1468 */ resetLayoutManager()1469 protected void resetLayoutManager() 1470 { 1471 if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT) 1472 layoutManager = new BasicHorizontalLayoutManager(); 1473 else 1474 layoutManager = new BasicVerticalLayoutManager(); 1475 getSplitPane().setLayout(layoutManager); 1476 layoutManager.updateComponents(); 1477 1478 // invalidating by itself does not invalidate the layout. 1479 getSplitPane().revalidate(); 1480 } 1481 1482 /** 1483 * This method is called when dragging starts. It resets lastDragLocation 1484 * and dividerSize. 1485 */ startDragging()1486 protected void startDragging() 1487 { 1488 dividerSize = divider.getDividerSize(); 1489 setLastDragLocation(-1); 1490 1491 if (! splitPane.getLeftComponent().isLightweight() 1492 || ! splitPane.getRightComponent().isLightweight()) 1493 draggingHW = true; 1494 1495 if (splitPane.isContinuousLayout()) 1496 nonContinuousLayoutDivider.setVisible(false); 1497 else 1498 { 1499 nonContinuousLayoutDivider.setVisible(true); 1500 nonContinuousLayoutDivider.setBounds(divider.getBounds()); 1501 } 1502 splitPane.revalidate(); 1503 splitPane.repaint(); 1504 } 1505 1506 /** 1507 * This method is called whenever the divider is dragged. If the JSplitPane 1508 * is in continuousLayout mode, the divider needs to be moved and the 1509 * JSplitPane needs to be laid out. 1510 * 1511 * @param location The new location of the divider. 1512 */ dragDividerTo(int location)1513 protected void dragDividerTo(int location) 1514 { 1515 location = validLocation(location); 1516 if (beginDragDividerLocation == -1) 1517 beginDragDividerLocation = location; 1518 1519 if (splitPane.isContinuousLayout()) 1520 splitPane.setDividerLocation(location); 1521 else 1522 { 1523 Point p = nonContinuousLayoutDivider.getLocation(); 1524 if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT) 1525 p.x = location; 1526 else 1527 p.y = location; 1528 nonContinuousLayoutDivider.setLocation(p); 1529 } 1530 setLastDragLocation(location); 1531 splitPane.repaint(); 1532 } 1533 1534 /** 1535 * This method is called when the dragging is finished. 1536 * 1537 * @param location The location where the drag finished. 1538 */ finishDraggingTo(int location)1539 protected void finishDraggingTo(int location) 1540 { 1541 if (nonContinuousLayoutDivider != null) 1542 nonContinuousLayoutDivider.setVisible(false); 1543 draggingHW = false; 1544 location = validLocation(location); 1545 dragDividerTo(location); 1546 splitPane.setDividerLocation(location); 1547 splitPane.setLastDividerLocation(beginDragDividerLocation); 1548 beginDragDividerLocation = -1; 1549 splitPane.repaint(); 1550 } 1551 1552 /** 1553 * This method returns the width of one of the sides of the divider's border. 1554 * 1555 * @return The width of one side of the divider's border. 1556 * 1557 * @deprecated 1.3 1558 */ getDividerBorderSize()1559 protected int getDividerBorderSize() 1560 { 1561 if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT) 1562 return divider.getBorder().getBorderInsets(divider).left; 1563 else 1564 return divider.getBorder().getBorderInsets(divider).top; 1565 } 1566 1567 /** 1568 * This is a helper method that returns a valid location for the divider 1569 * when dragging. 1570 * 1571 * @param location The location to check. 1572 * 1573 * @return A valid location. 1574 */ validLocation(int location)1575 private int validLocation(int location) 1576 { 1577 int min = getMinimumDividerLocation(splitPane); 1578 int max = getMaximumDividerLocation(splitPane); 1579 if (min > 0 && location < min) 1580 return min; 1581 if (max > 0 && location > max) 1582 return max; 1583 return location; 1584 } 1585 } 1586