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