29 import java.awt.event.*; 30 import java.beans.Transient; 31 import java.util.*; 32 import javax.swing.event.*; 33 import javax.swing.plaf.*; 34 import javax.accessibility.*; 35 import sun.swing.SwingUtilities2; 36 37 import java.io.Serializable; 38 import java.io.ObjectOutputStream; 39 import java.io.ObjectInputStream; 40 import java.io.IOException; 41 42 /** 43 * A component that lets the user switch between a group of components by 44 * clicking on a tab with a given title and/or icon. 45 * For examples and information on using tabbed panes see 46 * <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/tabbedpane.html">How to Use Tabbed Panes</a>, 47 * a section in <em>The Java Tutorial</em>. 48 * <p> 49 * Tabs/components are added to a <code>TabbedPane</code> object by using the 50 * <code>addTab</code> and <code>insertTab</code> methods. 51 * A tab is represented by an index corresponding 52 * to the position it was added in, where the first tab has an index equal to 0 53 * and the last tab has an index equal to the tab count minus 1. 54 * <p> 55 * The <code>TabbedPane</code> uses a <code>SingleSelectionModel</code> 56 * to represent the set 57 * of tab indices and the currently selected index. If the tab count 58 * is greater than 0, then there will always be a selected index, which 59 * by default will be initialized to the first tab. If the tab count is 60 * 0, then the selected index will be -1. 61 * <p> 62 * The tab title can be rendered by a <code>Component</code>. 63 * For example, the following produce similar results: 64 * <pre> 65 * // In this case the look and feel renders the title for the tab. 66 * tabbedPane.addTab("Tab", myComponent); 67 * // In this case the custom component is responsible for rendering the 68 * // title of the tab. 69 * tabbedPane.addTab(null, myComponent); 70 * tabbedPane.setTabComponentAt(0, new JLabel("Tab")); 71 * </pre> 72 * The latter is typically used when you want a more complex user interaction 73 * that requires custom components on the tab. For example, you could 74 * provide a custom component that animates or one that has widgets for 75 * closing the tab. 76 * <p> 77 * If you specify a component for a tab, the <code>JTabbedPane</code> 78 * will not render any text or icon you have specified for the tab. 79 * <p> 80 * <strong>Note:</strong> 81 * Do not use <code>setVisible</code> directly on a tab component to make it visible, 82 * use <code>setSelectedComponent</code> or <code>setSelectedIndex</code> methods instead. 83 * <p> 84 * <strong>Warning:</strong> Swing is not thread safe. For more 85 * information see <a 86 * href="package-summary.html#threading">Swing's Threading 87 * Policy</a>. 88 * <p> 89 * <strong>Warning:</strong> 90 * Serialized objects of this class will not be compatible with 91 * future Swing releases. The current serialization support is 92 * appropriate for short term storage or RMI between applications running 93 * the same version of Swing. As of 1.4, support for long term storage 94 * of all JavaBeans™ 95 * has been added to the <code>java.beans</code> package. 96 * Please see {@link java.beans.XMLEncoder}. 97 * 98 * @beaninfo 99 * attribute: isContainer true 100 * description: A component which provides a tab folder metaphor for 101 * displaying one component from a set of components. 102 * 103 * @author Dave Moore 104 * @author Philip Milne 105 * @author Amy Fowler 106 * 107 * @see SingleSelectionModel 108 * @since 1.2 109 */ 110 @SuppressWarnings("serial") // Same-version serialization only 111 public class JTabbedPane extends JComponent 112 implements Serializable, Accessible, SwingConstants { 113 114 /** 115 * The tab layout policy for wrapping tabs in multiple runs when all 129 /** 130 * @see #getUIClassID 131 * @see #readObject 132 */ 133 private static final String uiClassID = "TabbedPaneUI"; 134 135 /** 136 * Where the tabs are placed. 137 * @see #setTabPlacement 138 */ 139 protected int tabPlacement = TOP; 140 141 private int tabLayoutPolicy; 142 143 /** The default selection model */ 144 protected SingleSelectionModel model; 145 146 private boolean haveRegistered; 147 148 /** 149 * The <code>changeListener</code> is the listener we add to the 150 * model. 151 */ 152 protected ChangeListener changeListener = null; 153 154 private final java.util.List<Page> pages; 155 156 /* The component that is currently visible */ 157 private Component visComp = null; 158 159 /** 160 * Only one <code>ChangeEvent</code> is needed per <code>TabPane</code> 161 * instance since the 162 * event's only (read-only) state is the source property. The source 163 * of events generated here is always "this". 164 */ 165 protected transient ChangeEvent changeEvent = null; 166 167 /** 168 * Creates an empty <code>TabbedPane</code> with a default 169 * tab placement of <code>JTabbedPane.TOP</code>. 170 * @see #addTab 171 */ 172 public JTabbedPane() { 173 this(TOP, WRAP_TAB_LAYOUT); 174 } 175 176 /** 177 * Creates an empty <code>TabbedPane</code> with the specified tab placement 178 * of either: <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>, 179 * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>. 180 * 181 * @param tabPlacement the placement for the tabs relative to the content 182 * @see #addTab 183 */ 184 public JTabbedPane(int tabPlacement) { 185 this(tabPlacement, WRAP_TAB_LAYOUT); 186 } 187 188 /** 189 * Creates an empty <code>TabbedPane</code> with the specified tab placement 190 * and tab layout policy. Tab placement may be either: 191 * <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>, 192 * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>. 193 * Tab layout policy may be either: <code>JTabbedPane.WRAP_TAB_LAYOUT</code> 194 * or <code>JTabbedPane.SCROLL_TAB_LAYOUT</code>. 195 * 196 * @param tabPlacement the placement for the tabs relative to the content 197 * @param tabLayoutPolicy the policy for laying out tabs when all tabs will not fit on one run 198 * @exception IllegalArgumentException if tab placement or tab layout policy are not 199 * one of the above supported values 200 * @see #addTab 201 * @since 1.4 202 */ 203 public JTabbedPane(int tabPlacement, int tabLayoutPolicy) { 204 setTabPlacement(tabPlacement); 205 setTabLayoutPolicy(tabLayoutPolicy); 206 pages = new ArrayList<Page>(1); 207 setModel(new DefaultSingleSelectionModel()); 208 updateUI(); 209 } 210 211 /** 212 * Returns the UI object which implements the L&F for this component. 213 * 214 * @return a <code>TabbedPaneUI</code> object 215 * @see #setUI 216 */ 217 public TabbedPaneUI getUI() { 218 return (TabbedPaneUI)ui; 219 } 220 221 /** 222 * Sets the UI object which implements the L&F for this component. 223 * 224 * @param ui the new UI object 225 * @see UIDefaults#getUI 226 * @beaninfo 227 * bound: true 228 * hidden: true 229 * attribute: visualUpdate true 230 * description: The UI object that implements the tabbedpane's LookAndFeel 231 */ 232 public void setUI(TabbedPaneUI ui) { 233 super.setUI(ui); 234 // disabled icons are generated by LF so they should be unset here 247 */ 248 public void updateUI() { 249 setUI((TabbedPaneUI)UIManager.getUI(this)); 250 } 251 252 253 /** 254 * Returns the name of the UI class that implements the 255 * L&F for this component. 256 * 257 * @return the string "TabbedPaneUI" 258 * @see JComponent#getUIClassID 259 * @see UIDefaults#getUI 260 */ 261 public String getUIClassID() { 262 return uiClassID; 263 } 264 265 266 /** 267 * We pass <code>ModelChanged</code> events along to the listeners with 268 * the tabbedpane (instead of the model itself) as the event source. 269 */ 270 protected class ModelListener implements ChangeListener, Serializable { 271 public void stateChanged(ChangeEvent e) { 272 fireStateChanged(); 273 } 274 } 275 276 /** 277 * Subclasses that want to handle <code>ChangeEvents</code> differently 278 * can override this to return a subclass of <code>ModelListener</code> or 279 * another <code>ChangeListener</code> implementation. 280 * 281 * @return a {@code ChangeListener} 282 * @see #fireStateChanged 283 */ 284 protected ChangeListener createChangeListener() { 285 return new ModelListener(); 286 } 287 288 /** 289 * Adds a <code>ChangeListener</code> to this tabbedpane. 290 * 291 * @param l the <code>ChangeListener</code> to add 292 * @see #fireStateChanged 293 * @see #removeChangeListener 294 */ 295 public void addChangeListener(ChangeListener l) { 296 listenerList.add(ChangeListener.class, l); 297 } 298 299 /** 300 * Removes a <code>ChangeListener</code> from this tabbedpane. 301 * 302 * @param l the <code>ChangeListener</code> to remove 303 * @see #fireStateChanged 304 * @see #addChangeListener 305 */ 306 public void removeChangeListener(ChangeListener l) { 307 listenerList.remove(ChangeListener.class, l); 308 } 309 310 /** 311 * Returns an array of all the <code>ChangeListener</code>s added 312 * to this <code>JTabbedPane</code> with <code>addChangeListener</code>. 313 * 314 * @return all of the <code>ChangeListener</code>s added or an empty 315 * array if no listeners have been added 316 * @since 1.4 317 */ 318 public ChangeListener[] getChangeListeners() { 319 return listenerList.getListeners(ChangeListener.class); 320 } 321 322 /** 323 * Sends a {@code ChangeEvent}, with this {@code JTabbedPane} as the source, 324 * to each registered listener. This method is called each time there is 325 * a change to either the selected index or the selected tab in the 326 * {@code JTabbedPane}. Usually, the selected index and selected tab change 327 * together. However, there are some cases, such as tab addition, where the 328 * selected index changes and the same tab remains selected. There are other 329 * cases, such as deleting the selected tab, where the index remains the 330 * same, but a new tab moves to that index. Events are fired for all of 331 * these cases. 332 * 333 * @see #addChangeListener 334 * @see EventListenerList 456 model.addChangeListener(changeListener); 457 } 458 459 firePropertyChange("model", oldModel, model); 460 repaint(); 461 } 462 463 /** 464 * Returns the placement of the tabs for this tabbedpane. 465 * 466 * @return an {@code int} specifying the placement for the tabs 467 * @see #setTabPlacement 468 */ 469 public int getTabPlacement() { 470 return tabPlacement; 471 } 472 473 /** 474 * Sets the tab placement for this tabbedpane. 475 * Possible values are:<ul> 476 * <li><code>JTabbedPane.TOP</code> 477 * <li><code>JTabbedPane.BOTTOM</code> 478 * <li><code>JTabbedPane.LEFT</code> 479 * <li><code>JTabbedPane.RIGHT</code> 480 * </ul> 481 * The default value, if not set, is <code>SwingConstants.TOP</code>. 482 * 483 * @param tabPlacement the placement for the tabs relative to the content 484 * @exception IllegalArgumentException if tab placement value isn't one 485 * of the above valid values 486 * 487 * @beaninfo 488 * preferred: true 489 * bound: true 490 * attribute: visualUpdate true 491 * enum: TOP JTabbedPane.TOP 492 * LEFT JTabbedPane.LEFT 493 * BOTTOM JTabbedPane.BOTTOM 494 * RIGHT JTabbedPane.RIGHT 495 * description: The tabbedpane's tab placement. 496 * 497 */ 498 public void setTabPlacement(int tabPlacement) { 499 checkTabPlacement(tabPlacement); 500 if (this.tabPlacement != tabPlacement) { 501 int oldValue = this.tabPlacement; 514 } 515 } 516 517 /** 518 * Returns the policy used by the tabbedpane to layout the tabs when all the 519 * tabs will not fit within a single run. 520 * 521 * @return an {@code int} specifying the policy used to layout the tabs 522 * @see #setTabLayoutPolicy 523 * @since 1.4 524 */ 525 public int getTabLayoutPolicy() { 526 return tabLayoutPolicy; 527 } 528 529 /** 530 * Sets the policy which the tabbedpane will use in laying out the tabs 531 * when all the tabs will not fit within a single run. 532 * Possible values are: 533 * <ul> 534 * <li><code>JTabbedPane.WRAP_TAB_LAYOUT</code> 535 * <li><code>JTabbedPane.SCROLL_TAB_LAYOUT</code> 536 * </ul> 537 * 538 * The default value, if not set by the UI, is <code>JTabbedPane.WRAP_TAB_LAYOUT</code>. 539 * <p> 540 * Some look and feels might only support a subset of the possible 541 * layout policies, in which case the value of this property may be 542 * ignored. 543 * 544 * @param tabLayoutPolicy the policy used to layout the tabs 545 * @exception IllegalArgumentException if layoutPolicy value isn't one 546 * of the above valid values 547 * @see #getTabLayoutPolicy 548 * @since 1.4 549 * 550 * @beaninfo 551 * preferred: true 552 * bound: true 553 * attribute: visualUpdate true 554 * enum: WRAP_TAB_LAYOUT JTabbedPane.WRAP_TAB_LAYOUT 555 * SCROLL_TAB_LAYOUT JTabbedPane.SCROLL_TAB_LAYOUT 556 * description: The tabbedpane's policy for laying out the tabs 557 * 558 */ 646 } 647 648 if (oldPage != null) { 649 oldPage.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 650 AccessibleState.SELECTED, null); 651 } 652 653 if (newPage != null) { 654 newPage.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 655 null, AccessibleState.SELECTED); 656 } 657 658 accessibleContext.firePropertyChange( 659 AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 660 oldName, 661 accessibleContext.getAccessibleName()); 662 } 663 664 /** 665 * Returns the currently selected component for this tabbedpane. 666 * Returns <code>null</code> if there is no currently selected tab. 667 * 668 * @return the component corresponding to the selected tab 669 * @see #setSelectedComponent 670 */ 671 @Transient 672 public Component getSelectedComponent() { 673 int index = getSelectedIndex(); 674 if (index == -1) { 675 return null; 676 } 677 return getComponentAt(index); 678 } 679 680 /** 681 * Sets the selected component for this tabbedpane. This 682 * will automatically set the <code>selectedIndex</code> to the index 683 * corresponding to the specified component. 684 * 685 * @param c the selected {@code Component} for this {@code TabbedPane} 686 * @exception IllegalArgumentException if component not found in tabbed 687 * pane 688 * @see #getSelectedComponent 689 * @beaninfo 690 * preferred: true 691 * description: The tabbedpane's selected component. 692 */ 693 public void setSelectedComponent(Component c) { 694 int index = indexOfComponent(c); 695 if (index != -1) { 696 setSelectedIndex(index); 697 } else { 698 throw new IllegalArgumentException("component not found in tabbed pane"); 699 } 700 } 701 702 /** 754 755 if (selectedIndex >= newIndex) { 756 setSelectedIndexImpl(selectedIndex + 1, false); 757 } 758 759 if (!haveRegistered && tip != null) { 760 ToolTipManager.sharedInstance().registerComponent(this); 761 haveRegistered = true; 762 } 763 764 if (accessibleContext != null) { 765 accessibleContext.firePropertyChange( 766 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 767 null, component); 768 } 769 revalidate(); 770 repaint(); 771 } 772 773 /** 774 * Adds a <code>component</code> and <code>tip</code> 775 * represented by a <code>title</code> and/or <code>icon</code>, 776 * either of which can be <code>null</code>. 777 * Cover method for <code>insertTab</code>. 778 * 779 * @param title the title to be displayed in this tab 780 * @param icon the icon to be displayed in this tab 781 * @param component the component to be displayed when this tab is clicked 782 * @param tip the tooltip to be displayed for this tab 783 * 784 * @see #insertTab 785 * @see #removeTabAt 786 */ 787 public void addTab(String title, Icon icon, Component component, String tip) { 788 insertTab(title, icon, component, tip, pages.size()); 789 } 790 791 /** 792 * Adds a <code>component</code> represented by a <code>title</code> 793 * and/or <code>icon</code>, either of which can be <code>null</code>. 794 * Cover method for <code>insertTab</code>. 795 * 796 * @param title the title to be displayed in this tab 797 * @param icon the icon to be displayed in this tab 798 * @param component the component to be displayed when this tab is clicked 799 * 800 * @see #insertTab 801 * @see #removeTabAt 802 */ 803 public void addTab(String title, Icon icon, Component component) { 804 insertTab(title, icon, component, null, pages.size()); 805 } 806 807 /** 808 * Adds a <code>component</code> represented by a <code>title</code> 809 * and no icon. 810 * Cover method for <code>insertTab</code>. 811 * 812 * @param title the title to be displayed in this tab 813 * @param component the component to be displayed when this tab is clicked 814 * 815 * @see #insertTab 816 * @see #removeTabAt 817 */ 818 public void addTab(String title, Component component) { 819 insertTab(title, null, component, null, pages.size()); 820 } 821 822 /** 823 * Adds a <code>component</code> with a tab title defaulting to 824 * the name of the component which is the result of calling 825 * <code>component.getName</code>. 826 * Cover method for <code>insertTab</code>. 827 * 828 * @param component the component to be displayed when this tab is clicked 829 * @return the component 830 * 831 * @see #insertTab 832 * @see #removeTabAt 833 */ 834 public Component add(Component component) { 835 if (!(component instanceof UIResource)) { 836 addTab(component.getName(), component); 837 } else { 838 super.add(component); 839 } 840 return component; 841 } 842 843 /** 844 * Adds a <code>component</code> with the specified tab title. 845 * Cover method for <code>insertTab</code>. 846 * 847 * @param title the title to be displayed in this tab 848 * @param component the component to be displayed when this tab is clicked 849 * @return the component 850 * 851 * @see #insertTab 852 * @see #removeTabAt 853 */ 854 public Component add(String title, Component component) { 855 if (!(component instanceof UIResource)) { 856 addTab(title, component); 857 } else { 858 super.add(title, component); 859 } 860 return component; 861 } 862 863 /** 864 * Adds a <code>component</code> at the specified tab index with a tab 865 * title defaulting to the name of the component. 866 * Cover method for <code>insertTab</code>. 867 * 868 * @param component the component to be displayed when this tab is clicked 869 * @param index the position to insert this new tab 870 * @return the component 871 * 872 * @see #insertTab 873 * @see #removeTabAt 874 */ 875 public Component add(Component component, int index) { 876 if (!(component instanceof UIResource)) { 877 // Container.add() interprets -1 as "append", so convert 878 // the index appropriately to be handled by the vector 879 insertTab(component.getName(), null, component, null, 880 index == -1? getTabCount() : index); 881 } else { 882 super.add(component, index); 883 } 884 return component; 885 } 886 887 /** 888 * Adds a <code>component</code> to the tabbed pane. 889 * If <code>constraints</code> is a <code>String</code> or an 890 * <code>Icon</code>, it will be used for the tab title, 891 * otherwise the component's name will be used as the tab title. 892 * Cover method for <code>insertTab</code>. 893 * 894 * @param component the component to be displayed when this tab is clicked 895 * @param constraints the object to be displayed in the tab 896 * 897 * @see #insertTab 898 * @see #removeTabAt 899 */ 900 public void add(Component component, Object constraints) { 901 if (!(component instanceof UIResource)) { 902 if (constraints instanceof String) { 903 addTab((String)constraints, component); 904 } else if (constraints instanceof Icon) { 905 addTab(null, (Icon)constraints, component); 906 } else { 907 add(component); 908 } 909 } else { 910 super.add(component, constraints); 911 } 912 } 913 914 /** 915 * Adds a <code>component</code> at the specified tab index. 916 * If <code>constraints</code> is a <code>String</code> or an 917 * <code>Icon</code>, it will be used for the tab title, 918 * otherwise the component's name will be used as the tab title. 919 * Cover method for <code>insertTab</code>. 920 * 921 * @param component the component to be displayed when this tab is clicked 922 * @param constraints the object to be displayed in the tab 923 * @param index the position to insert this new tab 924 * 925 * @see #insertTab 926 * @see #removeTabAt 927 */ 928 public void add(Component component, Object constraints, int index) { 929 if (!(component instanceof UIResource)) { 930 931 Icon icon = constraints instanceof Icon? (Icon)constraints : null; 932 String title = constraints instanceof String? (String)constraints : null; 933 // Container.add() interprets -1 as "append", so convert 934 // the index appropriately to be handled by the vector 935 insertTab(title, icon, component, null, index == -1? getTabCount() : index); 936 } else { 937 super.add(component, constraints, index); 938 } 939 } 940 941 /** 942 * Removes the tab at <code>index</code>. 943 * After the component associated with <code>index</code> is removed, 944 * its visibility is reset to true to ensure it will be visible 945 * if added to other containers. 946 * @param index the index of the tab to be removed 947 * @exception IndexOutOfBoundsException if index is out of range 948 * {@code (index < 0 || index >= tab count)} 949 * 950 * @see #addTab 951 * @see #insertTab 952 */ 953 @SuppressWarnings("deprecation") 954 public void removeTabAt(int index) { 955 checkIndex(index); 956 957 Component component = getComponentAt(index); 958 boolean shouldChangeFocus = false; 959 int selected = getSelectedIndex(); 960 String oldName = null; 961 962 /* if we're about to remove the visible component */ 963 if (component == visComp) { 1017 if (component != null) { 1018 Component components[] = getComponents(); 1019 for (int i = components.length; --i >= 0; ) { 1020 if (components[i] == component) { 1021 super.remove(i); 1022 component.setVisible(true); 1023 break; 1024 } 1025 } 1026 } 1027 1028 if (shouldChangeFocus) { 1029 SwingUtilities2.tabbedPaneChangeFocusTo(getSelectedComponent()); 1030 } 1031 1032 revalidate(); 1033 repaint(); 1034 } 1035 1036 /** 1037 * Removes the specified <code>Component</code> from the 1038 * <code>JTabbedPane</code>. The method does nothing 1039 * if the <code>component</code> is null. 1040 * 1041 * @param component the component to remove from the tabbedpane 1042 * @see #addTab 1043 * @see #removeTabAt 1044 */ 1045 public void remove(Component component) { 1046 int index = indexOfComponent(component); 1047 if (index != -1) { 1048 removeTabAt(index); 1049 } else { 1050 // Container#remove(comp) invokes Container#remove(int) 1051 // so make sure JTabbedPane#remove(int) isn't called here 1052 Component children[] = getComponents(); 1053 for (int i=0; i < children.length; i++) { 1054 if (component == children[i]) { 1055 super.remove(i); 1056 break; 1057 } 1058 } 1059 } 1060 } 1061 1062 /** 1063 * Removes the tab and component which corresponds to the specified index. 1064 * 1065 * @param index the index of the component to remove from the 1066 * <code>tabbedpane</code> 1067 * @exception IndexOutOfBoundsException if index is out of range 1068 * {@code (index < 0 || index >= tab count)} 1069 * @see #addTab 1070 * @see #removeTabAt 1071 */ 1072 public void remove(int index) { 1073 removeTabAt(index); 1074 } 1075 1076 /** 1077 * Removes all the tabs and their corresponding components 1078 * from the <code>tabbedpane</code>. 1079 * 1080 * @see #addTab 1081 * @see #removeTabAt 1082 */ 1083 public void removeAll() { 1084 setSelectedIndexImpl(-1, true); 1085 1086 int tabCount = getTabCount(); 1087 // We invoke removeTabAt for each tab, otherwise we may end up 1088 // removing Components added by the UI. 1089 while (tabCount-- > 0) { 1090 removeTabAt(tabCount); 1091 } 1092 } 1093 1094 /** 1095 * Returns the number of tabs in this <code>tabbedpane</code>. 1096 * 1097 * @return an integer specifying the number of tabbed pages 1098 */ 1099 public int getTabCount() { 1100 return pages.size(); 1101 } 1102 1103 /** 1104 * Returns the number of tab runs currently used to display 1105 * the tabs. 1106 * @return an integer giving the number of rows if the 1107 * <code>tabPlacement</code> 1108 * is <code>TOP</code> or <code>BOTTOM</code> 1109 * and the number of columns if 1110 * <code>tabPlacement</code> 1111 * is <code>LEFT</code> or <code>RIGHT</code>, 1112 * or 0 if there is no UI set on this <code>tabbedpane</code> 1113 */ 1114 public int getTabRunCount() { 1115 if (ui != null) { 1116 return ((TabbedPaneUI)ui).getTabRunCount(this); 1117 } 1118 return 0; 1119 } 1120 1121 1122 // Getters for the Pages 1123 1124 /** 1125 * Returns the tab title at <code>index</code>. 1126 * 1127 * @param index the index of the item being queried 1128 * @return the title at <code>index</code> 1129 * @exception IndexOutOfBoundsException if index is out of range 1130 * {@code (index < 0 || index >= tab count)} 1131 * @see #setTitleAt 1132 */ 1133 public String getTitleAt(int index) { 1134 return pages.get(index).title; 1135 } 1136 1137 /** 1138 * Returns the tab icon at <code>index</code>. 1139 * 1140 * @param index the index of the item being queried 1141 * @return the icon at <code>index</code> 1142 * @exception IndexOutOfBoundsException if index is out of range 1143 * {@code (index < 0 || index >= tab count)} 1144 * 1145 * @see #setIconAt 1146 */ 1147 public Icon getIconAt(int index) { 1148 return pages.get(index).icon; 1149 } 1150 1151 /** 1152 * Returns the tab disabled icon at <code>index</code>. 1153 * If the tab disabled icon doesn't exist at <code>index</code> 1154 * this will forward the call to the look and feel to construct 1155 * an appropriate disabled Icon from the corresponding enabled 1156 * Icon. Some look and feels might not render the disabled Icon, 1157 * in which case it won't be created. 1158 * 1159 * @param index the index of the item being queried 1160 * @return the icon at <code>index</code> 1161 * @exception IndexOutOfBoundsException if index is out of range 1162 * {@code (index < 0 || index >= tab count)} 1163 * 1164 * @see #setDisabledIconAt 1165 */ 1166 public Icon getDisabledIconAt(int index) { 1167 Page page = pages.get(index); 1168 if (page.disabledIcon == null) { 1169 page.disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, page.icon); 1170 } 1171 return page.disabledIcon; 1172 } 1173 1174 /** 1175 * Returns the tab tooltip text at <code>index</code>. 1176 * 1177 * @param index the index of the item being queried 1178 * @return a string containing the tool tip text at <code>index</code> 1179 * @exception IndexOutOfBoundsException if index is out of range 1180 * {@code (index < 0 || index >= tab count)} 1181 * 1182 * @see #setToolTipTextAt 1183 * @since 1.3 1184 */ 1185 public String getToolTipTextAt(int index) { 1186 return pages.get(index).tip; 1187 } 1188 1189 /** 1190 * Returns the tab background color at <code>index</code>. 1191 * 1192 * @param index the index of the item being queried 1193 * @return the <code>Color</code> of the tab background at 1194 * <code>index</code> 1195 * @exception IndexOutOfBoundsException if index is out of range 1196 * {@code (index < 0 || index >= tab count)} 1197 * 1198 * @see #setBackgroundAt 1199 */ 1200 public Color getBackgroundAt(int index) { 1201 return pages.get(index).getBackground(); 1202 } 1203 1204 /** 1205 * Returns the tab foreground color at <code>index</code>. 1206 * 1207 * @param index the index of the item being queried 1208 * @return the <code>Color</code> of the tab foreground at 1209 * <code>index</code> 1210 * @exception IndexOutOfBoundsException if index is out of range 1211 * {@code (index < 0 || index >= tab count)} 1212 * 1213 * @see #setForegroundAt 1214 */ 1215 public Color getForegroundAt(int index) { 1216 return pages.get(index).getForeground(); 1217 } 1218 1219 /** 1220 * Returns whether or not the tab at <code>index</code> is 1221 * currently enabled. 1222 * 1223 * @param index the index of the item being queried 1224 * @return true if the tab at <code>index</code> is enabled; 1225 * false otherwise 1226 * @exception IndexOutOfBoundsException if index is out of range 1227 * {@code (index < 0 || index >= tab count)} 1228 * 1229 * @see #setEnabledAt 1230 */ 1231 public boolean isEnabledAt(int index) { 1232 return pages.get(index).isEnabled(); 1233 } 1234 1235 /** 1236 * Returns the component at <code>index</code>. 1237 * 1238 * @param index the index of the item being queried 1239 * @return the <code>Component</code> at <code>index</code> 1240 * @exception IndexOutOfBoundsException if index is out of range 1241 * {@code (index < 0 || index >= tab count)} 1242 * 1243 * @see #setComponentAt 1244 */ 1245 public Component getComponentAt(int index) { 1246 return pages.get(index).component; 1247 } 1248 1249 /** 1250 * Returns the keyboard mnemonic for accessing the specified tab. 1251 * The mnemonic is the key which when combined with the look and feel's 1252 * mouseless modifier (usually Alt) will activate the specified 1253 * tab. 1254 * 1255 * @since 1.4 1256 * @param tabIndex the index of the tab that the mnemonic refers to 1257 * @return the key code which represents the mnemonic; 1258 * -1 if a mnemonic is not specified for the tab 1259 * @exception IndexOutOfBoundsException if index is out of range 1260 * (<code>tabIndex</code> < 0 || 1261 * <code>tabIndex</code> >= tab count) 1262 * @see #setDisplayedMnemonicIndexAt(int,int) 1263 * @see #setMnemonicAt(int,int) 1264 */ 1265 public int getMnemonicAt(int tabIndex) { 1266 checkIndex(tabIndex); 1267 1268 Page page = pages.get(tabIndex); 1269 return page.getMnemonic(); 1270 } 1271 1272 /** 1273 * Returns the character, as an index, that the look and feel should 1274 * provide decoration for as representing the mnemonic character. 1275 * 1276 * @since 1.4 1277 * @param tabIndex the index of the tab that the mnemonic refers to 1278 * @return index representing mnemonic character if one exists; 1279 * otherwise returns -1 1280 * @exception IndexOutOfBoundsException if index is out of range 1281 * (<code>tabIndex</code> < 0 || 1282 * <code>tabIndex</code> >= tab count) 1283 * @see #setDisplayedMnemonicIndexAt(int,int) 1284 * @see #setMnemonicAt(int,int) 1285 */ 1286 public int getDisplayedMnemonicIndexAt(int tabIndex) { 1287 checkIndex(tabIndex); 1288 1289 Page page = pages.get(tabIndex); 1290 return page.getDisplayedMnemonicIndex(); 1291 } 1292 1293 /** 1294 * Returns the tab bounds at <code>index</code>. If the tab at 1295 * this index is not currently visible in the UI, then returns 1296 * <code>null</code>. 1297 * If there is no UI set on this <code>tabbedpane</code>, 1298 * then returns <code>null</code>. 1299 * 1300 * @param index the index to be queried 1301 * @return a <code>Rectangle</code> containing the tab bounds at 1302 * <code>index</code>, or <code>null</code> if tab at 1303 * <code>index</code> is not currently visible in the UI, 1304 * or if there is no UI set on this <code>tabbedpane</code> 1305 * @exception IndexOutOfBoundsException if index is out of range 1306 * {@code (index < 0 || index >= tab count)} 1307 */ 1308 public Rectangle getBoundsAt(int index) { 1309 checkIndex(index); 1310 if (ui != null) { 1311 return ((TabbedPaneUI)ui).getTabBounds(this, index); 1312 } 1313 return null; 1314 } 1315 1316 1317 // Setters for the Pages 1318 1319 /** 1320 * Sets the title at <code>index</code> to <code>title</code> which 1321 * can be <code>null</code>. 1322 * The title is not shown if a tab component for this tab was specified. 1323 * An internal exception is raised if there is no tab at that index. 1324 * 1325 * @param index the tab index where the title should be set 1326 * @param title the title to be displayed in the tab 1327 * @exception IndexOutOfBoundsException if index is out of range 1328 * {@code (index < 0 || index >= tab count)} 1329 * 1330 * @see #getTitleAt 1331 * @see #setTabComponentAt 1332 * @beaninfo 1333 * preferred: true 1334 * attribute: visualUpdate true 1335 * description: The title at the specified tab index. 1336 */ 1337 public void setTitleAt(int index, String title) { 1338 Page page = pages.get(index); 1339 String oldTitle =page.title; 1340 page.title = title; 1341 1342 if (oldTitle != title) { 1343 firePropertyChange("indexForTitle", -1, index); 1344 } 1345 page.updateDisplayedMnemonicIndex(); 1346 if ((oldTitle != title) && (accessibleContext != null)) { 1347 accessibleContext.firePropertyChange( 1348 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1349 oldTitle, title); 1350 } 1351 if (title == null || oldTitle == null || 1352 !title.equals(oldTitle)) { 1353 revalidate(); 1354 repaint(); 1355 } 1356 } 1357 1358 /** 1359 * Sets the icon at <code>index</code> to <code>icon</code> which can be 1360 * <code>null</code>. This does not set disabled icon at <code>icon</code>. 1361 * If the new Icon is different than the current Icon and disabled icon 1362 * is not explicitly set, the LookAndFeel will be asked to generate a disabled 1363 * Icon. To explicitly set disabled icon, use <code>setDisableIconAt()</code>. 1364 * The icon is not shown if a tab component for this tab was specified. 1365 * An internal exception is raised if there is no tab at that index. 1366 * 1367 * @param index the tab index where the icon should be set 1368 * @param icon the icon to be displayed in the tab 1369 * @exception IndexOutOfBoundsException if index is out of range 1370 * {@code (index < 0 || index >= tab count)} 1371 * 1372 * @see #setDisabledIconAt 1373 * @see #getIconAt 1374 * @see #getDisabledIconAt 1375 * @see #setTabComponentAt 1376 * @beaninfo 1377 * preferred: true 1378 * attribute: visualUpdate true 1379 * description: The icon at the specified tab index. 1380 */ 1381 public void setIconAt(int index, Icon icon) { 1382 Page page = pages.get(index); 1383 Icon oldIcon = page.icon; 1387 /* If the default icon has really changed and we had 1388 * generated the disabled icon for this page, then 1389 * clear the disabledIcon field of the page. 1390 */ 1391 if (page.disabledIcon instanceof UIResource) { 1392 page.disabledIcon = null; 1393 } 1394 1395 // Fire the accessibility Visible data change 1396 if (accessibleContext != null) { 1397 accessibleContext.firePropertyChange( 1398 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1399 oldIcon, icon); 1400 } 1401 revalidate(); 1402 repaint(); 1403 } 1404 } 1405 1406 /** 1407 * Sets the disabled icon at <code>index</code> to <code>icon</code> 1408 * which can be <code>null</code>. 1409 * An internal exception is raised if there is no tab at that index. 1410 * 1411 * @param index the tab index where the disabled icon should be set 1412 * @param disabledIcon the icon to be displayed in the tab when disabled 1413 * @exception IndexOutOfBoundsException if index is out of range 1414 * {@code (index < 0 || index >= tab count)} 1415 * 1416 * @see #getDisabledIconAt 1417 * @beaninfo 1418 * preferred: true 1419 * attribute: visualUpdate true 1420 * description: The disabled icon at the specified tab index. 1421 */ 1422 public void setDisabledIconAt(int index, Icon disabledIcon) { 1423 Icon oldIcon = pages.get(index).disabledIcon; 1424 pages.get(index).disabledIcon = disabledIcon; 1425 if (disabledIcon != oldIcon && !isEnabledAt(index)) { 1426 revalidate(); 1427 repaint(); 1428 } 1429 } 1430 1431 /** 1432 * Sets the tooltip text at <code>index</code> to <code>toolTipText</code> 1433 * which can be <code>null</code>. 1434 * An internal exception is raised if there is no tab at that index. 1435 * 1436 * @param index the tab index where the tooltip text should be set 1437 * @param toolTipText the tooltip text to be displayed for the tab 1438 * @exception IndexOutOfBoundsException if index is out of range 1439 * {@code (index < 0 || index >= tab count)} 1440 * 1441 * @see #getToolTipTextAt 1442 * @beaninfo 1443 * preferred: true 1444 * description: The tooltip text at the specified tab index. 1445 * @since 1.3 1446 */ 1447 public void setToolTipTextAt(int index, String toolTipText) { 1448 String oldToolTipText = pages.get(index).tip; 1449 pages.get(index).tip = toolTipText; 1450 1451 if ((oldToolTipText != toolTipText) && (accessibleContext != null)) { 1452 accessibleContext.firePropertyChange( 1453 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1454 oldToolTipText, toolTipText); 1455 } 1456 if (!haveRegistered && toolTipText != null) { 1457 ToolTipManager.sharedInstance().registerComponent(this); 1458 haveRegistered = true; 1459 } 1460 } 1461 1462 /** 1463 * Sets the background color at <code>index</code> to 1464 * <code>background</code> 1465 * which can be <code>null</code>, in which case the tab's background color 1466 * will default to the background color of the <code>tabbedpane</code>. 1467 * An internal exception is raised if there is no tab at that index. 1468 * <p> 1469 * It is up to the look and feel to honor this property, some may 1470 * choose to ignore it. 1471 * 1472 * @param index the tab index where the background should be set 1473 * @param background the color to be displayed in the tab's background 1474 * @exception IndexOutOfBoundsException if index is out of range 1475 * {@code (index < 0 || index >= tab count)} 1476 * 1477 * @see #getBackgroundAt 1478 * @beaninfo 1479 * preferred: true 1480 * attribute: visualUpdate true 1481 * description: The background color at the specified tab index. 1482 */ 1483 public void setBackgroundAt(int index, Color background) { 1484 Color oldBg = pages.get(index).background; 1485 pages.get(index).setBackground(background); 1486 if (background == null || oldBg == null || 1487 !background.equals(oldBg)) { 1488 Rectangle tabBounds = getBoundsAt(index); 1489 if (tabBounds != null) { 1490 repaint(tabBounds); 1491 } 1492 } 1493 } 1494 1495 /** 1496 * Sets the foreground color at <code>index</code> to 1497 * <code>foreground</code> which can be 1498 * <code>null</code>, in which case the tab's foreground color 1499 * will default to the foreground color of this <code>tabbedpane</code>. 1500 * An internal exception is raised if there is no tab at that index. 1501 * <p> 1502 * It is up to the look and feel to honor this property, some may 1503 * choose to ignore it. 1504 * 1505 * @param index the tab index where the foreground should be set 1506 * @param foreground the color to be displayed as the tab's foreground 1507 * @exception IndexOutOfBoundsException if index is out of range 1508 * {@code (index < 0 || index >= tab count)} 1509 * 1510 * @see #getForegroundAt 1511 * @beaninfo 1512 * preferred: true 1513 * attribute: visualUpdate true 1514 * description: The foreground color at the specified tab index. 1515 */ 1516 public void setForegroundAt(int index, Color foreground) { 1517 Color oldFg = pages.get(index).foreground; 1518 pages.get(index).setForeground(foreground); 1519 if (foreground == null || oldFg == null || 1520 !foreground.equals(oldFg)) { 1521 Rectangle tabBounds = getBoundsAt(index); 1522 if (tabBounds != null) { 1523 repaint(tabBounds); 1524 } 1525 } 1526 } 1527 1528 /** 1529 * Sets whether or not the tab at <code>index</code> is enabled. 1530 * An internal exception is raised if there is no tab at that index. 1531 * 1532 * @param index the tab index which should be enabled/disabled 1533 * @param enabled whether or not the tab should be enabled 1534 * @exception IndexOutOfBoundsException if index is out of range 1535 * {@code (index < 0 || index >= tab count)} 1536 * 1537 * @see #isEnabledAt 1538 */ 1539 public void setEnabledAt(int index, boolean enabled) { 1540 boolean oldEnabled = pages.get(index).isEnabled(); 1541 pages.get(index).setEnabled(enabled); 1542 if (enabled != oldEnabled) { 1543 revalidate(); 1544 repaint(); 1545 } 1546 } 1547 1548 /** 1549 * Sets the component at <code>index</code> to <code>component</code>. 1550 * An internal exception is raised if there is no tab at that index. 1551 * 1552 * @param index the tab index where this component is being placed 1553 * @param component the component for the tab 1554 * @exception IndexOutOfBoundsException if index is out of range 1555 * {@code (index < 0 || index >= tab count)} 1556 * 1557 * @see #getComponentAt 1558 * @beaninfo 1559 * attribute: visualUpdate true 1560 * description: The component at the specified tab index. 1561 */ 1562 @SuppressWarnings("deprecation") 1563 public void setComponentAt(int index, Component component) { 1564 Page page = pages.get(index); 1565 if (component != page.component) { 1566 boolean shouldChangeFocus = false; 1567 1568 if (page.component != null) { 1569 shouldChangeFocus = 1601 } 1602 1603 revalidate(); 1604 } 1605 } 1606 1607 /** 1608 * Provides a hint to the look and feel as to which character in the 1609 * text should be decorated to represent the mnemonic. Not all look and 1610 * feels may support this. A value of -1 indicates either there is 1611 * no mnemonic for this tab, or you do not wish the mnemonic to be 1612 * displayed for this tab. 1613 * <p> 1614 * The value of this is updated as the properties relating to the 1615 * mnemonic change (such as the mnemonic itself, the text...). 1616 * You should only ever have to call this if 1617 * you do not wish the default character to be underlined. For example, if 1618 * the text at tab index 3 was 'Apple Price', with a mnemonic of 'p', 1619 * and you wanted the 'P' 1620 * to be decorated, as 'Apple <u>P</u>rice', you would have to invoke 1621 * <code>setDisplayedMnemonicIndex(3, 6)</code> after invoking 1622 * <code>setMnemonicAt(3, KeyEvent.VK_P)</code>. 1623 * <p>Note that it is the programmer's responsibility to ensure 1624 * that each tab has a unique mnemonic or unpredictable results may 1625 * occur. 1626 * 1627 * @since 1.4 1628 * @param tabIndex the index of the tab that the mnemonic refers to 1629 * @param mnemonicIndex index into the <code>String</code> to underline 1630 * @exception IndexOutOfBoundsException if <code>tabIndex</code> is 1631 * out of range ({@code tabIndex < 0 || tabIndex >= tab 1632 * count}) 1633 * @exception IllegalArgumentException will be thrown if 1634 * <code>mnemonicIndex</code> is >= length of the tab 1635 * title , or < -1 1636 * @see #setMnemonicAt(int,int) 1637 * @see #getDisplayedMnemonicIndexAt(int) 1638 * 1639 * @beaninfo 1640 * bound: true 1641 * attribute: visualUpdate true 1642 * description: the index into the String to draw the keyboard character 1643 * mnemonic at 1644 */ 1645 public void setDisplayedMnemonicIndexAt(int tabIndex, int mnemonicIndex) { 1646 checkIndex(tabIndex); 1647 1648 Page page = pages.get(tabIndex); 1649 1650 page.setDisplayedMnemonicIndex(mnemonicIndex); 1651 } 1652 1653 /** 1654 * Sets the keyboard mnemonic for accessing the specified tab. 1655 * The mnemonic is the key which when combined with the look and feel's 1656 * mouseless modifier (usually Alt) will activate the specified 1657 * tab. 1658 * <p> 1659 * A mnemonic must correspond to a single key on the keyboard 1660 * and should be specified using one of the <code>VK_XXX</code> 1661 * keycodes defined in <code>java.awt.event.KeyEvent</code> 1662 * or one of the extended keycodes obtained through 1663 * <code>java.awt.event.KeyEvent.getExtendedKeyCodeForChar</code>. 1664 * Mnemonics are case-insensitive, therefore a key event 1665 * with the corresponding keycode would cause the button to be 1666 * activated whether or not the Shift modifier was pressed. 1667 * <p> 1668 * This will update the displayed mnemonic property for the specified 1669 * tab. 1670 * 1671 * @since 1.4 1672 * @param tabIndex the index of the tab that the mnemonic refers to 1673 * @param mnemonic the key code which represents the mnemonic 1674 * @exception IndexOutOfBoundsException if <code>tabIndex</code> is out 1675 * of range ({@code tabIndex < 0 || tabIndex >= tab count}) 1676 * @see #getMnemonicAt(int) 1677 * @see #setDisplayedMnemonicIndexAt(int,int) 1678 * 1679 * @beaninfo 1680 * bound: true 1681 * attribute: visualUpdate true 1682 * description: The keyboard mnenmonic, as a KeyEvent VK constant, 1683 * for the specified tab 1684 */ 1685 public void setMnemonicAt(int tabIndex, int mnemonic) { 1686 checkIndex(tabIndex); 1687 1688 Page page = pages.get(tabIndex); 1689 page.setMnemonic(mnemonic); 1690 1691 firePropertyChange("mnemonicAt", null, null); 1692 } 1693 1694 // end of Page setters 1695 1696 /** 1697 * Returns the first tab index with a given <code>title</code>, or 1698 * -1 if no tab has this title. 1699 * 1700 * @param title the title for the tab 1701 * @return the first tab index which matches <code>title</code>, or 1702 * -1 if no tab has this title 1703 */ 1704 public int indexOfTab(String title) { 1705 for(int i = 0; i < getTabCount(); i++) { 1706 if (getTitleAt(i).equals(title == null? "" : title)) { 1707 return i; 1708 } 1709 } 1710 return -1; 1711 } 1712 1713 /** 1714 * Returns the first tab index with a given <code>icon</code>, 1715 * or -1 if no tab has this icon. 1716 * 1717 * @param icon the icon for the tab 1718 * @return the first tab index which matches <code>icon</code>, 1719 * or -1 if no tab has this icon 1720 */ 1721 public int indexOfTab(Icon icon) { 1722 for(int i = 0; i < getTabCount(); i++) { 1723 Icon tabIcon = getIconAt(i); 1724 if ((tabIcon != null && tabIcon.equals(icon)) || 1725 (tabIcon == null && tabIcon == icon)) { 1726 return i; 1727 } 1728 } 1729 return -1; 1730 } 1731 1732 /** 1733 * Returns the index of the tab for the specified component. 1734 * Returns -1 if there is no tab for this component. 1735 * 1736 * @param component the component for the tab 1737 * @return the first tab which matches this component, or -1 1738 * if there is no tab for this component 1754 * intersects the location. 1755 * 1756 * @param x the x location relative to this tabbedpane 1757 * @param y the y location relative to this tabbedpane 1758 * @return the tab index which intersects the location, or 1759 * -1 if no tab intersects the location 1760 * @since 1.4 1761 */ 1762 public int indexAtLocation(int x, int y) { 1763 if (ui != null) { 1764 return ((TabbedPaneUI)ui).tabForCoordinate(this, x, y); 1765 } 1766 return -1; 1767 } 1768 1769 1770 /** 1771 * Returns the tooltip text for the component determined by the 1772 * mouse event location. 1773 * 1774 * @param event the <code>MouseEvent</code> that tells where the 1775 * cursor is lingering 1776 * @return the <code>String</code> containing the tooltip text 1777 */ 1778 public String getToolTipText(MouseEvent event) { 1779 if (ui != null) { 1780 int index = ((TabbedPaneUI)ui).tabForCoordinate(this, event.getX(), event.getY()); 1781 1782 if (index != -1) { 1783 return pages.get(index).tip; 1784 } 1785 } 1786 return super.getToolTipText(event); 1787 } 1788 1789 private void checkIndex(int index) { 1790 if (index < 0 || index >= pages.size()) { 1791 throw new IndexOutOfBoundsException("Index: "+index+", Tab count: "+pages.size()); 1792 } 1793 } 1794 1795 1796 /** 1797 * See <code>readObject</code> and <code>writeObject</code> in 1798 * <code>JComponent</code> for more 1799 * information about serialization in Swing. 1800 */ 1801 private void writeObject(ObjectOutputStream s) throws IOException { 1802 s.defaultWriteObject(); 1803 if (getUIClassID().equals(uiClassID)) { 1804 byte count = JComponent.getWriteObjCounter(this); 1805 JComponent.setWriteObjCounter(this, --count); 1806 if (count == 0 && ui != null) { 1807 ui.installUI(this); 1808 } 1809 } 1810 } 1811 1812 /* Called from the <code>JComponent</code>'s 1813 * <code>EnableSerializationFocusListener</code> to 1814 * do any Swing-specific pre-serialization configuration. 1815 */ 1816 void compWriteObjectNotify() { 1817 super.compWriteObjectNotify(); 1818 // If ToolTipText != null, then the tooltip has already been 1819 // unregistered by JComponent.compWriteObjectNotify() 1820 if (getToolTipText() == null && haveRegistered) { 1821 ToolTipManager.sharedInstance().unregisterComponent(this); 1822 } 1823 } 1824 1825 /** 1826 * See <code>readObject</code> and <code>writeObject</code> in 1827 * <code>JComponent</code> for more 1828 * information about serialization in Swing. 1829 */ 1830 private void readObject(ObjectInputStream s) 1831 throws IOException, ClassNotFoundException 1832 { 1833 ObjectInputStream.GetField f = s.readFields(); 1834 1835 int newTabPlacement = f.get("tabPlacement", TOP); 1836 checkTabPlacement(newTabPlacement); 1837 tabPlacement = newTabPlacement; 1838 int newTabLayoutPolicy = f.get("tabLayoutPolicy", 0); 1839 checkTabLayoutPolicy(newTabLayoutPolicy); 1840 tabLayoutPolicy = newTabLayoutPolicy; 1841 model = (SingleSelectionModel) f.get("model", null); 1842 haveRegistered = f.get("haveRegistered", false); 1843 changeListener = (ChangeListener) f.get("changeListener", null); 1844 visComp = (Component) f.get("visComp", null); 1845 1846 if ((ui != null) && (getUIClassID().equals(uiClassID))) { 1847 ui.installUI(this); 1848 } 1849 // If ToolTipText != null, then the tooltip has already been 1850 // registered by JComponent.readObject() 1851 if (getToolTipText() == null && haveRegistered) { 1852 ToolTipManager.sharedInstance().registerComponent(this); 1853 } 1854 } 1855 1856 1857 /** 1858 * Returns a string representation of this <code>JTabbedPane</code>. 1859 * This method 1860 * is intended to be used only for debugging purposes, and the 1861 * content and format of the returned string may vary between 1862 * implementations. The returned string may be empty but may not 1863 * be <code>null</code>. 1864 * 1865 * @return a string representation of this JTabbedPane. 1866 */ 1867 protected String paramString() { 1868 String tabPlacementString; 1869 if (tabPlacement == TOP) { 1870 tabPlacementString = "TOP"; 1871 } else if (tabPlacement == BOTTOM) { 1872 tabPlacementString = "BOTTOM"; 1873 } else if (tabPlacement == LEFT) { 1874 tabPlacementString = "LEFT"; 1875 } else if (tabPlacement == RIGHT) { 1876 tabPlacementString = "RIGHT"; 1877 } else tabPlacementString = ""; 1878 String haveRegisteredString = (haveRegistered ? 1879 "true" : "false"); 1880 1881 return super.paramString() + 1882 ",haveRegistered=" + haveRegisteredString + 1883 ",tabPlacement=" + tabPlacementString; 1894 * A new AccessibleJTabbedPane instance is created if necessary. 1895 * 1896 * @return an AccessibleJTabbedPane that serves as the 1897 * AccessibleContext of this JTabbedPane 1898 */ 1899 public AccessibleContext getAccessibleContext() { 1900 if (accessibleContext == null) { 1901 accessibleContext = new AccessibleJTabbedPane(); 1902 1903 // initialize AccessibleContext for the existing pages 1904 int count = getTabCount(); 1905 for (int i = 0; i < count; i++) { 1906 pages.get(i).initAccessibleContext(); 1907 } 1908 } 1909 return accessibleContext; 1910 } 1911 1912 /** 1913 * This class implements accessibility support for the 1914 * <code>JTabbedPane</code> class. It provides an implementation of the 1915 * Java Accessibility API appropriate to tabbed pane user-interface 1916 * elements. 1917 * <p> 1918 * <strong>Warning:</strong> 1919 * Serialized objects of this class will not be compatible with 1920 * future Swing releases. The current serialization support is 1921 * appropriate for short term storage or RMI between applications running 1922 * the same version of Swing. As of 1.4, support for long term storage 1923 * of all JavaBeans™ 1924 * has been added to the <code>java.beans</code> package. 1925 * Please see {@link java.beans.XMLEncoder}. 1926 */ 1927 @SuppressWarnings("serial") // Same-version serialization only 1928 protected class AccessibleJTabbedPane extends AccessibleJComponent 1929 implements AccessibleSelection, ChangeListener { 1930 1931 /** 1932 * Returns the accessible name of this object, or {@code null} if 1933 * there is no accessible name. 1934 * 1935 * @return the accessible name of this object, nor {@code null}. 1936 * @since 1.6 1937 */ 1938 public String getAccessibleName() { 1939 if (accessibleName != null) { 1940 return accessibleName; 1941 } 1942 1943 String cp = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY); 1944 1986 */ 1987 public int getAccessibleChildrenCount() { 1988 return getTabCount(); 1989 } 1990 1991 /** 1992 * Return the specified Accessible child of the object. 1993 * 1994 * @param i zero-based index of child 1995 * @return the Accessible child of the object 1996 * @exception IllegalArgumentException if index is out of bounds 1997 */ 1998 public Accessible getAccessibleChild(int i) { 1999 if (i < 0 || i >= getTabCount()) { 2000 return null; 2001 } 2002 return pages.get(i); 2003 } 2004 2005 /** 2006 * Gets the <code>AccessibleSelection</code> associated with 2007 * this object. In the implementation of the Java 2008 * Accessibility API for this class, 2009 * returns this object, which is responsible for implementing the 2010 * <code>AccessibleSelection</code> interface on behalf of itself. 2011 * 2012 * @return this object 2013 */ 2014 public AccessibleSelection getAccessibleSelection() { 2015 return this; 2016 } 2017 2018 /** 2019 * Returns the <code>Accessible</code> child contained at 2020 * the local coordinate <code>Point</code>, if one exists. 2021 * Otherwise returns the currently selected tab. 2022 * 2023 * @return the <code>Accessible</code> at the specified 2024 * location, if it exists 2025 */ 2026 public Accessible getAccessibleAt(Point p) { 2027 int tab = ((TabbedPaneUI) ui).tabForCoordinate(JTabbedPane.this, 2028 p.x, p.y); 2029 if (tab == -1) { 2030 tab = getSelectedIndex(); 2031 } 2032 return getAccessibleChild(tab); 2033 } 2034 2035 public int getAccessibleSelectionCount() { 2036 return 1; 2037 } 2038 2039 public Accessible getAccessibleSelection(int i) { 2040 int index = getSelectedIndex(); 2041 if (index == -1) { 2042 return null; 2043 } 2363 ((ImageIcon)icon).getAccessibleContext(); 2364 accessibleIcon = (AccessibleIcon)ac; 2365 } else if (!enabled && disabledIcon instanceof ImageIcon) { 2366 AccessibleContext ac = 2367 ((ImageIcon)disabledIcon).getAccessibleContext(); 2368 accessibleIcon = (AccessibleIcon)ac; 2369 } 2370 if (accessibleIcon != null) { 2371 AccessibleIcon [] returnIcons = new AccessibleIcon[1]; 2372 returnIcons[0] = accessibleIcon; 2373 return returnIcons; 2374 } else { 2375 return null; 2376 } 2377 } 2378 } 2379 2380 /** 2381 * Sets the component that is responsible for rendering the 2382 * title for the specified tab. A null value means 2383 * <code>JTabbedPane</code> will render the title and/or icon for 2384 * the specified tab. A non-null value means the component will 2385 * render the title and <code>JTabbedPane</code> will not render 2386 * the title and/or icon. 2387 * <p> 2388 * Note: The component must not be one that the developer has 2389 * already added to the tabbed pane. 2390 * 2391 * @param index the tab index where the component should be set 2392 * @param component the component to render the title for the 2393 * specified tab 2394 * @exception IndexOutOfBoundsException if index is out of range 2395 * {@code (index < 0 || index >= tab count)} 2396 * @exception IllegalArgumentException if component has already been 2397 * added to this <code>JTabbedPane</code> 2398 * 2399 * @see #getTabComponentAt 2400 * @beaninfo 2401 * preferred: true 2402 * attribute: visualUpdate true 2403 * description: The tab component at the specified tab index. 2404 * @since 1.6 2405 */ 2406 public void setTabComponentAt(int index, Component component) { 2407 if (component != null && indexOfComponent(component) != -1) { 2408 throw new IllegalArgumentException("Component is already added to this JTabbedPane"); 2409 } 2410 Component oldValue = getTabComponentAt(index); 2411 if (component != oldValue) { 2412 int tabComponentIndex = indexOfTabComponent(component); 2413 if (tabComponentIndex != -1) { 2414 setTabComponentAt(tabComponentIndex, null); 2415 } 2416 pages.get(index).tabComponent = component; 2417 firePropertyChange("indexForTabComponent", -1, index); 2418 } 2419 } 2420 2421 /** 2422 * Returns the tab component at <code>index</code>. 2423 * 2424 * @param index the index of the item being queried 2425 * @return the tab component at <code>index</code> 2426 * @exception IndexOutOfBoundsException if index is out of range 2427 * {@code (index < 0 || index >= tab count)} 2428 * 2429 * @see #setTabComponentAt 2430 * @since 1.6 2431 */ 2432 public Component getTabComponentAt(int index) { 2433 return pages.get(index).tabComponent; 2434 } 2435 2436 /** 2437 * Returns the index of the tab for the specified tab component. 2438 * Returns -1 if there is no tab for this tab component. 2439 * 2440 * @param tabComponent the tab component for the tab 2441 * @return the first tab which matches this tab component, or -1 2442 * if there is no tab for this tab component 2443 * @see #setTabComponentAt 2444 * @see #getTabComponentAt 2445 * @since 1.6 | 29 import java.awt.event.*; 30 import java.beans.Transient; 31 import java.util.*; 32 import javax.swing.event.*; 33 import javax.swing.plaf.*; 34 import javax.accessibility.*; 35 import sun.swing.SwingUtilities2; 36 37 import java.io.Serializable; 38 import java.io.ObjectOutputStream; 39 import java.io.ObjectInputStream; 40 import java.io.IOException; 41 42 /** 43 * A component that lets the user switch between a group of components by 44 * clicking on a tab with a given title and/or icon. 45 * For examples and information on using tabbed panes see 46 * <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/tabbedpane.html">How to Use Tabbed Panes</a>, 47 * a section in <em>The Java Tutorial</em>. 48 * <p> 49 * Tabs/components are added to a {@code TabbedPane} object by using the 50 * {@code addTab} and {@code insertTab} methods. 51 * A tab is represented by an index corresponding 52 * to the position it was added in, where the first tab has an index equal to 0 53 * and the last tab has an index equal to the tab count minus 1. 54 * <p> 55 * The {@code TabbedPane} uses a {@code SingleSelectionModel} 56 * to represent the set 57 * of tab indices and the currently selected index. If the tab count 58 * is greater than 0, then there will always be a selected index, which 59 * by default will be initialized to the first tab. If the tab count is 60 * 0, then the selected index will be -1. 61 * <p> 62 * The tab title can be rendered by a {@code Component}. 63 * For example, the following produce similar results: 64 * <pre> 65 * // In this case the look and feel renders the title for the tab. 66 * tabbedPane.addTab("Tab", myComponent); 67 * // In this case the custom component is responsible for rendering the 68 * // title of the tab. 69 * tabbedPane.addTab(null, myComponent); 70 * tabbedPane.setTabComponentAt(0, new JLabel("Tab")); 71 * </pre> 72 * The latter is typically used when you want a more complex user interaction 73 * that requires custom components on the tab. For example, you could 74 * provide a custom component that animates or one that has widgets for 75 * closing the tab. 76 * <p> 77 * If you specify a component for a tab, the {@code JTabbedPane} 78 * will not render any text or icon you have specified for the tab. 79 * <p> 80 * <strong>Note:</strong> 81 * Do not use {@code setVisible} directly on a tab component to make it visible, 82 * use {@code setSelectedComponent} or {@code setSelectedIndex} methods instead. 83 * <p> 84 * <strong>Warning:</strong> Swing is not thread safe. For more 85 * information see <a 86 * href="package-summary.html#threading">Swing's Threading 87 * Policy</a>. 88 * <p> 89 * <strong>Warning:</strong> 90 * Serialized objects of this class will not be compatible with 91 * future Swing releases. The current serialization support is 92 * appropriate for short term storage or RMI between applications running 93 * the same version of Swing. As of 1.4, support for long term storage 94 * of all JavaBeans™ 95 * has been added to the {@code java.beans} package. 96 * Please see {@link java.beans.XMLEncoder}. 97 * 98 * @beaninfo 99 * attribute: isContainer true 100 * description: A component which provides a tab folder metaphor for 101 * displaying one component from a set of components. 102 * 103 * @author Dave Moore 104 * @author Philip Milne 105 * @author Amy Fowler 106 * 107 * @see SingleSelectionModel 108 * @since 1.2 109 */ 110 @SuppressWarnings("serial") // Same-version serialization only 111 public class JTabbedPane extends JComponent 112 implements Serializable, Accessible, SwingConstants { 113 114 /** 115 * The tab layout policy for wrapping tabs in multiple runs when all 129 /** 130 * @see #getUIClassID 131 * @see #readObject 132 */ 133 private static final String uiClassID = "TabbedPaneUI"; 134 135 /** 136 * Where the tabs are placed. 137 * @see #setTabPlacement 138 */ 139 protected int tabPlacement = TOP; 140 141 private int tabLayoutPolicy; 142 143 /** The default selection model */ 144 protected SingleSelectionModel model; 145 146 private boolean haveRegistered; 147 148 /** 149 * The {@code changeListener} is the listener we add to the 150 * model. 151 */ 152 protected ChangeListener changeListener = null; 153 154 private final java.util.List<Page> pages; 155 156 /* The component that is currently visible */ 157 private Component visComp = null; 158 159 /** 160 * Only one {@code ChangeEvent} is needed per {@code TabPane} 161 * instance since the 162 * event's only (read-only) state is the source property. The source 163 * of events generated here is always "this". 164 */ 165 protected transient ChangeEvent changeEvent = null; 166 167 /** 168 * Creates an empty {@code TabbedPane} with a default 169 * tab placement of {@code JTabbedPane.TOP}. 170 * @see #addTab 171 */ 172 public JTabbedPane() { 173 this(TOP, WRAP_TAB_LAYOUT); 174 } 175 176 /** 177 * Creates an empty {@code TabbedPane} with the specified tab placement 178 * of either: {@code JTabbedPane.TOP}, {@code JTabbedPane.BOTTOM}, 179 * {@code JTabbedPane.LEFT}, or {@code JTabbedPane.RIGHT}. 180 * 181 * @param tabPlacement the placement for the tabs relative to the content 182 * @see #addTab 183 */ 184 public JTabbedPane(int tabPlacement) { 185 this(tabPlacement, WRAP_TAB_LAYOUT); 186 } 187 188 /** 189 * Creates an empty {@code TabbedPane} with the specified tab placement 190 * and tab layout policy. Tab placement may be either: 191 * {@code JTabbedPane.TOP}, {@code JTabbedPane.BOTTOM}, 192 * {@code JTabbedPane.LEFT}, or {@code JTabbedPane.RIGHT}. 193 * Tab layout policy may be either: {@code JTabbedPane.WRAP_TAB_LAYOUT} 194 * or {@code JTabbedPane.SCROLL_TAB_LAYOUT}. 195 * 196 * @param tabPlacement the placement for the tabs relative to the content 197 * @param tabLayoutPolicy the policy for laying out tabs when all tabs will not fit on one run 198 * @exception IllegalArgumentException if tab placement or tab layout policy are not 199 * one of the above supported values 200 * @see #addTab 201 * @since 1.4 202 */ 203 public JTabbedPane(int tabPlacement, int tabLayoutPolicy) { 204 setTabPlacement(tabPlacement); 205 setTabLayoutPolicy(tabLayoutPolicy); 206 pages = new ArrayList<Page>(1); 207 setModel(new DefaultSingleSelectionModel()); 208 updateUI(); 209 } 210 211 /** 212 * Returns the UI object which implements the L&F for this component. 213 * 214 * @return a {@code TabbedPaneUI} object 215 * @see #setUI 216 */ 217 public TabbedPaneUI getUI() { 218 return (TabbedPaneUI)ui; 219 } 220 221 /** 222 * Sets the UI object which implements the L&F for this component. 223 * 224 * @param ui the new UI object 225 * @see UIDefaults#getUI 226 * @beaninfo 227 * bound: true 228 * hidden: true 229 * attribute: visualUpdate true 230 * description: The UI object that implements the tabbedpane's LookAndFeel 231 */ 232 public void setUI(TabbedPaneUI ui) { 233 super.setUI(ui); 234 // disabled icons are generated by LF so they should be unset here 247 */ 248 public void updateUI() { 249 setUI((TabbedPaneUI)UIManager.getUI(this)); 250 } 251 252 253 /** 254 * Returns the name of the UI class that implements the 255 * L&F for this component. 256 * 257 * @return the string "TabbedPaneUI" 258 * @see JComponent#getUIClassID 259 * @see UIDefaults#getUI 260 */ 261 public String getUIClassID() { 262 return uiClassID; 263 } 264 265 266 /** 267 * We pass {@code ModelChanged} events along to the listeners with 268 * the tabbedpane (instead of the model itself) as the event source. 269 */ 270 protected class ModelListener implements ChangeListener, Serializable { 271 public void stateChanged(ChangeEvent e) { 272 fireStateChanged(); 273 } 274 } 275 276 /** 277 * Subclasses that want to handle {@code ChangeEvents} differently 278 * can override this to return a subclass of {@code ModelListener} or 279 * another {@code ChangeListener} implementation. 280 * 281 * @return a {@code ChangeListener} 282 * @see #fireStateChanged 283 */ 284 protected ChangeListener createChangeListener() { 285 return new ModelListener(); 286 } 287 288 /** 289 * Adds a {@code ChangeListener} to this tabbedpane. 290 * 291 * @param l the {@code ChangeListener} to add 292 * @see #fireStateChanged 293 * @see #removeChangeListener 294 */ 295 public void addChangeListener(ChangeListener l) { 296 listenerList.add(ChangeListener.class, l); 297 } 298 299 /** 300 * Removes a {@code ChangeListener} from this tabbedpane. 301 * 302 * @param l the {@code ChangeListener} to remove 303 * @see #fireStateChanged 304 * @see #addChangeListener 305 */ 306 public void removeChangeListener(ChangeListener l) { 307 listenerList.remove(ChangeListener.class, l); 308 } 309 310 /** 311 * Returns an array of all the {@code ChangeListener}s added 312 * to this {@code JTabbedPane} with {@code addChangeListener}. 313 * 314 * @return all of the {@code ChangeListener}s added or an empty 315 * array if no listeners have been added 316 * @since 1.4 317 */ 318 public ChangeListener[] getChangeListeners() { 319 return listenerList.getListeners(ChangeListener.class); 320 } 321 322 /** 323 * Sends a {@code ChangeEvent}, with this {@code JTabbedPane} as the source, 324 * to each registered listener. This method is called each time there is 325 * a change to either the selected index or the selected tab in the 326 * {@code JTabbedPane}. Usually, the selected index and selected tab change 327 * together. However, there are some cases, such as tab addition, where the 328 * selected index changes and the same tab remains selected. There are other 329 * cases, such as deleting the selected tab, where the index remains the 330 * same, but a new tab moves to that index. Events are fired for all of 331 * these cases. 332 * 333 * @see #addChangeListener 334 * @see EventListenerList 456 model.addChangeListener(changeListener); 457 } 458 459 firePropertyChange("model", oldModel, model); 460 repaint(); 461 } 462 463 /** 464 * Returns the placement of the tabs for this tabbedpane. 465 * 466 * @return an {@code int} specifying the placement for the tabs 467 * @see #setTabPlacement 468 */ 469 public int getTabPlacement() { 470 return tabPlacement; 471 } 472 473 /** 474 * Sets the tab placement for this tabbedpane. 475 * Possible values are:<ul> 476 * <li>{@code JTabbedPane.TOP} 477 * <li>{@code JTabbedPane.BOTTOM} 478 * <li>{@code JTabbedPane.LEFT} 479 * <li>{@code JTabbedPane.RIGHT} 480 * </ul> 481 * The default value, if not set, is {@code SwingConstants.TOP}. 482 * 483 * @param tabPlacement the placement for the tabs relative to the content 484 * @exception IllegalArgumentException if tab placement value isn't one 485 * of the above valid values 486 * 487 * @beaninfo 488 * preferred: true 489 * bound: true 490 * attribute: visualUpdate true 491 * enum: TOP JTabbedPane.TOP 492 * LEFT JTabbedPane.LEFT 493 * BOTTOM JTabbedPane.BOTTOM 494 * RIGHT JTabbedPane.RIGHT 495 * description: The tabbedpane's tab placement. 496 * 497 */ 498 public void setTabPlacement(int tabPlacement) { 499 checkTabPlacement(tabPlacement); 500 if (this.tabPlacement != tabPlacement) { 501 int oldValue = this.tabPlacement; 514 } 515 } 516 517 /** 518 * Returns the policy used by the tabbedpane to layout the tabs when all the 519 * tabs will not fit within a single run. 520 * 521 * @return an {@code int} specifying the policy used to layout the tabs 522 * @see #setTabLayoutPolicy 523 * @since 1.4 524 */ 525 public int getTabLayoutPolicy() { 526 return tabLayoutPolicy; 527 } 528 529 /** 530 * Sets the policy which the tabbedpane will use in laying out the tabs 531 * when all the tabs will not fit within a single run. 532 * Possible values are: 533 * <ul> 534 * <li>{@code JTabbedPane.WRAP_TAB_LAYOUT} 535 * <li>{@code JTabbedPane.SCROLL_TAB_LAYOUT} 536 * </ul> 537 * 538 * The default value, if not set by the UI, is {@code JTabbedPane.WRAP_TAB_LAYOUT}. 539 * <p> 540 * Some look and feels might only support a subset of the possible 541 * layout policies, in which case the value of this property may be 542 * ignored. 543 * 544 * @param tabLayoutPolicy the policy used to layout the tabs 545 * @exception IllegalArgumentException if layoutPolicy value isn't one 546 * of the above valid values 547 * @see #getTabLayoutPolicy 548 * @since 1.4 549 * 550 * @beaninfo 551 * preferred: true 552 * bound: true 553 * attribute: visualUpdate true 554 * enum: WRAP_TAB_LAYOUT JTabbedPane.WRAP_TAB_LAYOUT 555 * SCROLL_TAB_LAYOUT JTabbedPane.SCROLL_TAB_LAYOUT 556 * description: The tabbedpane's policy for laying out the tabs 557 * 558 */ 646 } 647 648 if (oldPage != null) { 649 oldPage.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 650 AccessibleState.SELECTED, null); 651 } 652 653 if (newPage != null) { 654 newPage.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 655 null, AccessibleState.SELECTED); 656 } 657 658 accessibleContext.firePropertyChange( 659 AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 660 oldName, 661 accessibleContext.getAccessibleName()); 662 } 663 664 /** 665 * Returns the currently selected component for this tabbedpane. 666 * Returns {@code null} if there is no currently selected tab. 667 * 668 * @return the component corresponding to the selected tab 669 * @see #setSelectedComponent 670 */ 671 @Transient 672 public Component getSelectedComponent() { 673 int index = getSelectedIndex(); 674 if (index == -1) { 675 return null; 676 } 677 return getComponentAt(index); 678 } 679 680 /** 681 * Sets the selected component for this tabbedpane. This 682 * will automatically set the {@code selectedIndex} to the index 683 * corresponding to the specified component. 684 * 685 * @param c the selected {@code Component} for this {@code TabbedPane} 686 * @exception IllegalArgumentException if component not found in tabbed 687 * pane 688 * @see #getSelectedComponent 689 * @beaninfo 690 * preferred: true 691 * description: The tabbedpane's selected component. 692 */ 693 public void setSelectedComponent(Component c) { 694 int index = indexOfComponent(c); 695 if (index != -1) { 696 setSelectedIndex(index); 697 } else { 698 throw new IllegalArgumentException("component not found in tabbed pane"); 699 } 700 } 701 702 /** 754 755 if (selectedIndex >= newIndex) { 756 setSelectedIndexImpl(selectedIndex + 1, false); 757 } 758 759 if (!haveRegistered && tip != null) { 760 ToolTipManager.sharedInstance().registerComponent(this); 761 haveRegistered = true; 762 } 763 764 if (accessibleContext != null) { 765 accessibleContext.firePropertyChange( 766 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 767 null, component); 768 } 769 revalidate(); 770 repaint(); 771 } 772 773 /** 774 * Adds a {@code component} and {@code tip} 775 * represented by a {@code title} and/or {@code icon}, 776 * either of which can be {@code null}. 777 * Cover method for {@code insertTab}. 778 * 779 * @param title the title to be displayed in this tab 780 * @param icon the icon to be displayed in this tab 781 * @param component the component to be displayed when this tab is clicked 782 * @param tip the tooltip to be displayed for this tab 783 * 784 * @see #insertTab 785 * @see #removeTabAt 786 */ 787 public void addTab(String title, Icon icon, Component component, String tip) { 788 insertTab(title, icon, component, tip, pages.size()); 789 } 790 791 /** 792 * Adds a {@code component} represented by a {@code title} 793 * and/or {@code icon}, either of which can be {@code null}. 794 * Cover method for {@code insertTab}. 795 * 796 * @param title the title to be displayed in this tab 797 * @param icon the icon to be displayed in this tab 798 * @param component the component to be displayed when this tab is clicked 799 * 800 * @see #insertTab 801 * @see #removeTabAt 802 */ 803 public void addTab(String title, Icon icon, Component component) { 804 insertTab(title, icon, component, null, pages.size()); 805 } 806 807 /** 808 * Adds a {@code component} represented by a {@code title} 809 * and no icon. 810 * Cover method for {@code insertTab}. 811 * 812 * @param title the title to be displayed in this tab 813 * @param component the component to be displayed when this tab is clicked 814 * 815 * @see #insertTab 816 * @see #removeTabAt 817 */ 818 public void addTab(String title, Component component) { 819 insertTab(title, null, component, null, pages.size()); 820 } 821 822 /** 823 * Adds a {@code component} with a tab title defaulting to 824 * the name of the component which is the result of calling 825 * {@code component.getName}. 826 * Cover method for {@code insertTab}. 827 * 828 * @param component the component to be displayed when this tab is clicked 829 * @return the component 830 * 831 * @see #insertTab 832 * @see #removeTabAt 833 */ 834 public Component add(Component component) { 835 if (!(component instanceof UIResource)) { 836 addTab(component.getName(), component); 837 } else { 838 super.add(component); 839 } 840 return component; 841 } 842 843 /** 844 * Adds a {@code component} with the specified tab title. 845 * Cover method for {@code insertTab}. 846 * 847 * @param title the title to be displayed in this tab 848 * @param component the component to be displayed when this tab is clicked 849 * @return the component 850 * 851 * @see #insertTab 852 * @see #removeTabAt 853 */ 854 public Component add(String title, Component component) { 855 if (!(component instanceof UIResource)) { 856 addTab(title, component); 857 } else { 858 super.add(title, component); 859 } 860 return component; 861 } 862 863 /** 864 * Adds a {@code component} at the specified tab index with a tab 865 * title defaulting to the name of the component. 866 * Cover method for {@code insertTab}. 867 * 868 * @param component the component to be displayed when this tab is clicked 869 * @param index the position to insert this new tab 870 * @return the component 871 * 872 * @see #insertTab 873 * @see #removeTabAt 874 */ 875 public Component add(Component component, int index) { 876 if (!(component instanceof UIResource)) { 877 // Container.add() interprets -1 as "append", so convert 878 // the index appropriately to be handled by the vector 879 insertTab(component.getName(), null, component, null, 880 index == -1? getTabCount() : index); 881 } else { 882 super.add(component, index); 883 } 884 return component; 885 } 886 887 /** 888 * Adds a {@code component} to the tabbed pane. 889 * If {@code constraints} is a {@code String} or an 890 * {@code Icon}, it will be used for the tab title, 891 * otherwise the component's name will be used as the tab title. 892 * Cover method for {@code insertTab}. 893 * 894 * @param component the component to be displayed when this tab is clicked 895 * @param constraints the object to be displayed in the tab 896 * 897 * @see #insertTab 898 * @see #removeTabAt 899 */ 900 public void add(Component component, Object constraints) { 901 if (!(component instanceof UIResource)) { 902 if (constraints instanceof String) { 903 addTab((String)constraints, component); 904 } else if (constraints instanceof Icon) { 905 addTab(null, (Icon)constraints, component); 906 } else { 907 add(component); 908 } 909 } else { 910 super.add(component, constraints); 911 } 912 } 913 914 /** 915 * Adds a {@code component} at the specified tab index. 916 * If {@code constraints} is a {@code String} or an 917 * {@code Icon}, it will be used for the tab title, 918 * otherwise the component's name will be used as the tab title. 919 * Cover method for {@code insertTab}. 920 * 921 * @param component the component to be displayed when this tab is clicked 922 * @param constraints the object to be displayed in the tab 923 * @param index the position to insert this new tab 924 * 925 * @see #insertTab 926 * @see #removeTabAt 927 */ 928 public void add(Component component, Object constraints, int index) { 929 if (!(component instanceof UIResource)) { 930 931 Icon icon = constraints instanceof Icon? (Icon)constraints : null; 932 String title = constraints instanceof String? (String)constraints : null; 933 // Container.add() interprets -1 as "append", so convert 934 // the index appropriately to be handled by the vector 935 insertTab(title, icon, component, null, index == -1? getTabCount() : index); 936 } else { 937 super.add(component, constraints, index); 938 } 939 } 940 941 /** 942 * Removes the tab at {@code index}. 943 * After the component associated with {@code index} is removed, 944 * its visibility is reset to true to ensure it will be visible 945 * if added to other containers. 946 * @param index the index of the tab to be removed 947 * @exception IndexOutOfBoundsException if index is out of range 948 * {@code (index < 0 || index >= tab count)} 949 * 950 * @see #addTab 951 * @see #insertTab 952 */ 953 @SuppressWarnings("deprecation") 954 public void removeTabAt(int index) { 955 checkIndex(index); 956 957 Component component = getComponentAt(index); 958 boolean shouldChangeFocus = false; 959 int selected = getSelectedIndex(); 960 String oldName = null; 961 962 /* if we're about to remove the visible component */ 963 if (component == visComp) { 1017 if (component != null) { 1018 Component components[] = getComponents(); 1019 for (int i = components.length; --i >= 0; ) { 1020 if (components[i] == component) { 1021 super.remove(i); 1022 component.setVisible(true); 1023 break; 1024 } 1025 } 1026 } 1027 1028 if (shouldChangeFocus) { 1029 SwingUtilities2.tabbedPaneChangeFocusTo(getSelectedComponent()); 1030 } 1031 1032 revalidate(); 1033 repaint(); 1034 } 1035 1036 /** 1037 * Removes the specified {@code Component} from the 1038 * {@code JTabbedPane}. The method does nothing 1039 * if the {@code component} is null. 1040 * 1041 * @param component the component to remove from the tabbedpane 1042 * @see #addTab 1043 * @see #removeTabAt 1044 */ 1045 public void remove(Component component) { 1046 int index = indexOfComponent(component); 1047 if (index != -1) { 1048 removeTabAt(index); 1049 } else { 1050 // Container#remove(comp) invokes Container#remove(int) 1051 // so make sure JTabbedPane#remove(int) isn't called here 1052 Component children[] = getComponents(); 1053 for (int i=0; i < children.length; i++) { 1054 if (component == children[i]) { 1055 super.remove(i); 1056 break; 1057 } 1058 } 1059 } 1060 } 1061 1062 /** 1063 * Removes the tab and component which corresponds to the specified index. 1064 * 1065 * @param index the index of the component to remove from the 1066 * {@code tabbedpane} 1067 * @exception IndexOutOfBoundsException if index is out of range 1068 * {@code (index < 0 || index >= tab count)} 1069 * @see #addTab 1070 * @see #removeTabAt 1071 */ 1072 public void remove(int index) { 1073 removeTabAt(index); 1074 } 1075 1076 /** 1077 * Removes all the tabs and their corresponding components 1078 * from the {@code tabbedpane}. 1079 * 1080 * @see #addTab 1081 * @see #removeTabAt 1082 */ 1083 public void removeAll() { 1084 setSelectedIndexImpl(-1, true); 1085 1086 int tabCount = getTabCount(); 1087 // We invoke removeTabAt for each tab, otherwise we may end up 1088 // removing Components added by the UI. 1089 while (tabCount-- > 0) { 1090 removeTabAt(tabCount); 1091 } 1092 } 1093 1094 /** 1095 * Returns the number of tabs in this {@code tabbedpane}. 1096 * 1097 * @return an integer specifying the number of tabbed pages 1098 */ 1099 public int getTabCount() { 1100 return pages.size(); 1101 } 1102 1103 /** 1104 * Returns the number of tab runs currently used to display 1105 * the tabs. 1106 * @return an integer giving the number of rows if the 1107 * {@code tabPlacement} 1108 * is {@code TOP} or {@code BOTTOM} 1109 * and the number of columns if 1110 * {@code tabPlacement} 1111 * is {@code LEFT} or {@code RIGHT}, 1112 * or 0 if there is no UI set on this {@code tabbedpane} 1113 */ 1114 public int getTabRunCount() { 1115 if (ui != null) { 1116 return ((TabbedPaneUI)ui).getTabRunCount(this); 1117 } 1118 return 0; 1119 } 1120 1121 1122 // Getters for the Pages 1123 1124 /** 1125 * Returns the tab title at {@code index}. 1126 * 1127 * @param index the index of the item being queried 1128 * @return the title at {@code index} 1129 * @exception IndexOutOfBoundsException if index is out of range 1130 * {@code (index < 0 || index >= tab count)} 1131 * @see #setTitleAt 1132 */ 1133 public String getTitleAt(int index) { 1134 return pages.get(index).title; 1135 } 1136 1137 /** 1138 * Returns the tab icon at {@code index}. 1139 * 1140 * @param index the index of the item being queried 1141 * @return the icon at {@code index} 1142 * @exception IndexOutOfBoundsException if index is out of range 1143 * {@code (index < 0 || index >= tab count)} 1144 * 1145 * @see #setIconAt 1146 */ 1147 public Icon getIconAt(int index) { 1148 return pages.get(index).icon; 1149 } 1150 1151 /** 1152 * Returns the tab disabled icon at {@code index}. 1153 * If the tab disabled icon doesn't exist at {@code index} 1154 * this will forward the call to the look and feel to construct 1155 * an appropriate disabled Icon from the corresponding enabled 1156 * Icon. Some look and feels might not render the disabled Icon, 1157 * in which case it won't be created. 1158 * 1159 * @param index the index of the item being queried 1160 * @return the icon at {@code index} 1161 * @exception IndexOutOfBoundsException if index is out of range 1162 * {@code (index < 0 || index >= tab count)} 1163 * 1164 * @see #setDisabledIconAt 1165 */ 1166 public Icon getDisabledIconAt(int index) { 1167 Page page = pages.get(index); 1168 if (page.disabledIcon == null) { 1169 page.disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, page.icon); 1170 } 1171 return page.disabledIcon; 1172 } 1173 1174 /** 1175 * Returns the tab tooltip text at {@code index}. 1176 * 1177 * @param index the index of the item being queried 1178 * @return a string containing the tool tip text at {@code index} 1179 * @exception IndexOutOfBoundsException if index is out of range 1180 * {@code (index < 0 || index >= tab count)} 1181 * 1182 * @see #setToolTipTextAt 1183 * @since 1.3 1184 */ 1185 public String getToolTipTextAt(int index) { 1186 return pages.get(index).tip; 1187 } 1188 1189 /** 1190 * Returns the tab background color at {@code index}. 1191 * 1192 * @param index the index of the item being queried 1193 * @return the {@code Color} of the tab background at 1194 * {@code index} 1195 * @exception IndexOutOfBoundsException if index is out of range 1196 * {@code (index < 0 || index >= tab count)} 1197 * 1198 * @see #setBackgroundAt 1199 */ 1200 public Color getBackgroundAt(int index) { 1201 return pages.get(index).getBackground(); 1202 } 1203 1204 /** 1205 * Returns the tab foreground color at {@code index}. 1206 * 1207 * @param index the index of the item being queried 1208 * @return the {@code Color} of the tab foreground at 1209 * {@code index} 1210 * @exception IndexOutOfBoundsException if index is out of range 1211 * {@code (index < 0 || index >= tab count)} 1212 * 1213 * @see #setForegroundAt 1214 */ 1215 public Color getForegroundAt(int index) { 1216 return pages.get(index).getForeground(); 1217 } 1218 1219 /** 1220 * Returns whether or not the tab at {@code index} is 1221 * currently enabled. 1222 * 1223 * @param index the index of the item being queried 1224 * @return true if the tab at {@code index} is enabled; 1225 * false otherwise 1226 * @exception IndexOutOfBoundsException if index is out of range 1227 * {@code (index < 0 || index >= tab count)} 1228 * 1229 * @see #setEnabledAt 1230 */ 1231 public boolean isEnabledAt(int index) { 1232 return pages.get(index).isEnabled(); 1233 } 1234 1235 /** 1236 * Returns the component at {@code index}. 1237 * 1238 * @param index the index of the item being queried 1239 * @return the {@code Component} at {@code index} 1240 * @exception IndexOutOfBoundsException if index is out of range 1241 * {@code (index < 0 || index >= tab count)} 1242 * 1243 * @see #setComponentAt 1244 */ 1245 public Component getComponentAt(int index) { 1246 return pages.get(index).component; 1247 } 1248 1249 /** 1250 * Returns the keyboard mnemonic for accessing the specified tab. 1251 * The mnemonic is the key which when combined with the look and feel's 1252 * mouseless modifier (usually Alt) will activate the specified 1253 * tab. 1254 * 1255 * @since 1.4 1256 * @param tabIndex the index of the tab that the mnemonic refers to 1257 * @return the key code which represents the mnemonic; 1258 * -1 if a mnemonic is not specified for the tab 1259 * @exception IndexOutOfBoundsException if index is out of range 1260 * ({@code tabIndex < 0 || 1261 * tabIndex >= tab count}) 1262 * @see #setDisplayedMnemonicIndexAt(int,int) 1263 * @see #setMnemonicAt(int,int) 1264 */ 1265 public int getMnemonicAt(int tabIndex) { 1266 checkIndex(tabIndex); 1267 1268 Page page = pages.get(tabIndex); 1269 return page.getMnemonic(); 1270 } 1271 1272 /** 1273 * Returns the character, as an index, that the look and feel should 1274 * provide decoration for as representing the mnemonic character. 1275 * 1276 * @since 1.4 1277 * @param tabIndex the index of the tab that the mnemonic refers to 1278 * @return index representing mnemonic character if one exists; 1279 * otherwise returns -1 1280 * @exception IndexOutOfBoundsException if index is out of range 1281 * ({@code tabIndex < 0 || 1282 * tabIndex >= tab count}) 1283 * @see #setDisplayedMnemonicIndexAt(int,int) 1284 * @see #setMnemonicAt(int,int) 1285 */ 1286 public int getDisplayedMnemonicIndexAt(int tabIndex) { 1287 checkIndex(tabIndex); 1288 1289 Page page = pages.get(tabIndex); 1290 return page.getDisplayedMnemonicIndex(); 1291 } 1292 1293 /** 1294 * Returns the tab bounds at {@code index}. If the tab at 1295 * this index is not currently visible in the UI, then returns 1296 * {@code null}. 1297 * If there is no UI set on this {@code tabbedpane}, 1298 * then returns {@code null}. 1299 * 1300 * @param index the index to be queried 1301 * @return a {@code Rectangle} containing the tab bounds at 1302 * {@code index}, or {@code null} if tab at 1303 * {@code index} is not currently visible in the UI, 1304 * or if there is no UI set on this {@code tabbedpane} 1305 * @exception IndexOutOfBoundsException if index is out of range 1306 * {@code (index < 0 || index >= tab count)} 1307 */ 1308 public Rectangle getBoundsAt(int index) { 1309 checkIndex(index); 1310 if (ui != null) { 1311 return ((TabbedPaneUI)ui).getTabBounds(this, index); 1312 } 1313 return null; 1314 } 1315 1316 1317 // Setters for the Pages 1318 1319 /** 1320 * Sets the title at {@code index} to {@code title} which 1321 * can be {@code null}. 1322 * The title is not shown if a tab component for this tab was specified. 1323 * An internal exception is raised if there is no tab at that index. 1324 * 1325 * @param index the tab index where the title should be set 1326 * @param title the title to be displayed in the tab 1327 * @exception IndexOutOfBoundsException if index is out of range 1328 * {@code (index < 0 || index >= tab count)} 1329 * 1330 * @see #getTitleAt 1331 * @see #setTabComponentAt 1332 * @beaninfo 1333 * preferred: true 1334 * attribute: visualUpdate true 1335 * description: The title at the specified tab index. 1336 */ 1337 public void setTitleAt(int index, String title) { 1338 Page page = pages.get(index); 1339 String oldTitle =page.title; 1340 page.title = title; 1341 1342 if (oldTitle != title) { 1343 firePropertyChange("indexForTitle", -1, index); 1344 } 1345 page.updateDisplayedMnemonicIndex(); 1346 if ((oldTitle != title) && (accessibleContext != null)) { 1347 accessibleContext.firePropertyChange( 1348 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1349 oldTitle, title); 1350 } 1351 if (title == null || oldTitle == null || 1352 !title.equals(oldTitle)) { 1353 revalidate(); 1354 repaint(); 1355 } 1356 } 1357 1358 /** 1359 * Sets the icon at {@code index} to {@code icon} which can be 1360 * {@code null}. This does not set disabled icon at {@code icon}. 1361 * If the new Icon is different than the current Icon and disabled icon 1362 * is not explicitly set, the LookAndFeel will be asked to generate a disabled 1363 * Icon. To explicitly set disabled icon, use {@code setDisableIconAt()}. 1364 * The icon is not shown if a tab component for this tab was specified. 1365 * An internal exception is raised if there is no tab at that index. 1366 * 1367 * @param index the tab index where the icon should be set 1368 * @param icon the icon to be displayed in the tab 1369 * @exception IndexOutOfBoundsException if index is out of range 1370 * {@code (index < 0 || index >= tab count)} 1371 * 1372 * @see #setDisabledIconAt 1373 * @see #getIconAt 1374 * @see #getDisabledIconAt 1375 * @see #setTabComponentAt 1376 * @beaninfo 1377 * preferred: true 1378 * attribute: visualUpdate true 1379 * description: The icon at the specified tab index. 1380 */ 1381 public void setIconAt(int index, Icon icon) { 1382 Page page = pages.get(index); 1383 Icon oldIcon = page.icon; 1387 /* If the default icon has really changed and we had 1388 * generated the disabled icon for this page, then 1389 * clear the disabledIcon field of the page. 1390 */ 1391 if (page.disabledIcon instanceof UIResource) { 1392 page.disabledIcon = null; 1393 } 1394 1395 // Fire the accessibility Visible data change 1396 if (accessibleContext != null) { 1397 accessibleContext.firePropertyChange( 1398 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1399 oldIcon, icon); 1400 } 1401 revalidate(); 1402 repaint(); 1403 } 1404 } 1405 1406 /** 1407 * Sets the disabled icon at {@code index} to {@code icon} 1408 * which can be {@code null}. 1409 * An internal exception is raised if there is no tab at that index. 1410 * 1411 * @param index the tab index where the disabled icon should be set 1412 * @param disabledIcon the icon to be displayed in the tab when disabled 1413 * @exception IndexOutOfBoundsException if index is out of range 1414 * {@code (index < 0 || index >= tab count)} 1415 * 1416 * @see #getDisabledIconAt 1417 * @beaninfo 1418 * preferred: true 1419 * attribute: visualUpdate true 1420 * description: The disabled icon at the specified tab index. 1421 */ 1422 public void setDisabledIconAt(int index, Icon disabledIcon) { 1423 Icon oldIcon = pages.get(index).disabledIcon; 1424 pages.get(index).disabledIcon = disabledIcon; 1425 if (disabledIcon != oldIcon && !isEnabledAt(index)) { 1426 revalidate(); 1427 repaint(); 1428 } 1429 } 1430 1431 /** 1432 * Sets the tooltip text at {@code index} to {@code toolTipText} 1433 * which can be {@code null}. 1434 * An internal exception is raised if there is no tab at that index. 1435 * 1436 * @param index the tab index where the tooltip text should be set 1437 * @param toolTipText the tooltip text to be displayed for the tab 1438 * @exception IndexOutOfBoundsException if index is out of range 1439 * {@code (index < 0 || index >= tab count)} 1440 * 1441 * @see #getToolTipTextAt 1442 * @beaninfo 1443 * preferred: true 1444 * description: The tooltip text at the specified tab index. 1445 * @since 1.3 1446 */ 1447 public void setToolTipTextAt(int index, String toolTipText) { 1448 String oldToolTipText = pages.get(index).tip; 1449 pages.get(index).tip = toolTipText; 1450 1451 if ((oldToolTipText != toolTipText) && (accessibleContext != null)) { 1452 accessibleContext.firePropertyChange( 1453 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1454 oldToolTipText, toolTipText); 1455 } 1456 if (!haveRegistered && toolTipText != null) { 1457 ToolTipManager.sharedInstance().registerComponent(this); 1458 haveRegistered = true; 1459 } 1460 } 1461 1462 /** 1463 * Sets the background color at {@code index} to 1464 * {@code background} 1465 * which can be {@code null}, in which case the tab's background color 1466 * will default to the background color of the {@code tabbedpane}. 1467 * An internal exception is raised if there is no tab at that index. 1468 * <p> 1469 * It is up to the look and feel to honor this property, some may 1470 * choose to ignore it. 1471 * 1472 * @param index the tab index where the background should be set 1473 * @param background the color to be displayed in the tab's background 1474 * @exception IndexOutOfBoundsException if index is out of range 1475 * {@code (index < 0 || index >= tab count)} 1476 * 1477 * @see #getBackgroundAt 1478 * @beaninfo 1479 * preferred: true 1480 * attribute: visualUpdate true 1481 * description: The background color at the specified tab index. 1482 */ 1483 public void setBackgroundAt(int index, Color background) { 1484 Color oldBg = pages.get(index).background; 1485 pages.get(index).setBackground(background); 1486 if (background == null || oldBg == null || 1487 !background.equals(oldBg)) { 1488 Rectangle tabBounds = getBoundsAt(index); 1489 if (tabBounds != null) { 1490 repaint(tabBounds); 1491 } 1492 } 1493 } 1494 1495 /** 1496 * Sets the foreground color at {@code index} to 1497 * {@code foreground} which can be 1498 * {@code null}, in which case the tab's foreground color 1499 * will default to the foreground color of this {@code tabbedpane}. 1500 * An internal exception is raised if there is no tab at that index. 1501 * <p> 1502 * It is up to the look and feel to honor this property, some may 1503 * choose to ignore it. 1504 * 1505 * @param index the tab index where the foreground should be set 1506 * @param foreground the color to be displayed as the tab's foreground 1507 * @exception IndexOutOfBoundsException if index is out of range 1508 * {@code (index < 0 || index >= tab count)} 1509 * 1510 * @see #getForegroundAt 1511 * @beaninfo 1512 * preferred: true 1513 * attribute: visualUpdate true 1514 * description: The foreground color at the specified tab index. 1515 */ 1516 public void setForegroundAt(int index, Color foreground) { 1517 Color oldFg = pages.get(index).foreground; 1518 pages.get(index).setForeground(foreground); 1519 if (foreground == null || oldFg == null || 1520 !foreground.equals(oldFg)) { 1521 Rectangle tabBounds = getBoundsAt(index); 1522 if (tabBounds != null) { 1523 repaint(tabBounds); 1524 } 1525 } 1526 } 1527 1528 /** 1529 * Sets whether or not the tab at {@code index} is enabled. 1530 * An internal exception is raised if there is no tab at that index. 1531 * 1532 * @param index the tab index which should be enabled/disabled 1533 * @param enabled whether or not the tab should be enabled 1534 * @exception IndexOutOfBoundsException if index is out of range 1535 * {@code (index < 0 || index >= tab count)} 1536 * 1537 * @see #isEnabledAt 1538 */ 1539 public void setEnabledAt(int index, boolean enabled) { 1540 boolean oldEnabled = pages.get(index).isEnabled(); 1541 pages.get(index).setEnabled(enabled); 1542 if (enabled != oldEnabled) { 1543 revalidate(); 1544 repaint(); 1545 } 1546 } 1547 1548 /** 1549 * Sets the component at {@code index} to {@code component}. 1550 * An internal exception is raised if there is no tab at that index. 1551 * 1552 * @param index the tab index where this component is being placed 1553 * @param component the component for the tab 1554 * @exception IndexOutOfBoundsException if index is out of range 1555 * {@code (index < 0 || index >= tab count)} 1556 * 1557 * @see #getComponentAt 1558 * @beaninfo 1559 * attribute: visualUpdate true 1560 * description: The component at the specified tab index. 1561 */ 1562 @SuppressWarnings("deprecation") 1563 public void setComponentAt(int index, Component component) { 1564 Page page = pages.get(index); 1565 if (component != page.component) { 1566 boolean shouldChangeFocus = false; 1567 1568 if (page.component != null) { 1569 shouldChangeFocus = 1601 } 1602 1603 revalidate(); 1604 } 1605 } 1606 1607 /** 1608 * Provides a hint to the look and feel as to which character in the 1609 * text should be decorated to represent the mnemonic. Not all look and 1610 * feels may support this. A value of -1 indicates either there is 1611 * no mnemonic for this tab, or you do not wish the mnemonic to be 1612 * displayed for this tab. 1613 * <p> 1614 * The value of this is updated as the properties relating to the 1615 * mnemonic change (such as the mnemonic itself, the text...). 1616 * You should only ever have to call this if 1617 * you do not wish the default character to be underlined. For example, if 1618 * the text at tab index 3 was 'Apple Price', with a mnemonic of 'p', 1619 * and you wanted the 'P' 1620 * to be decorated, as 'Apple <u>P</u>rice', you would have to invoke 1621 * {@code setDisplayedMnemonicIndex(3, 6)} after invoking 1622 * {@code setMnemonicAt(3, KeyEvent.VK_P)}. 1623 * <p>Note that it is the programmer's responsibility to ensure 1624 * that each tab has a unique mnemonic or unpredictable results may 1625 * occur. 1626 * 1627 * @since 1.4 1628 * @param tabIndex the index of the tab that the mnemonic refers to 1629 * @param mnemonicIndex index into the {@code String} to underline 1630 * @exception IndexOutOfBoundsException if {@code tabIndex} is 1631 * out of range ({@code tabIndex < 0 || tabIndex >= tab 1632 * count}) 1633 * @exception IllegalArgumentException will be thrown if 1634 * {@code mnemonicIndex} is >= length of the tab 1635 * title , or < -1 1636 * @see #setMnemonicAt(int,int) 1637 * @see #getDisplayedMnemonicIndexAt(int) 1638 * 1639 * @beaninfo 1640 * bound: true 1641 * attribute: visualUpdate true 1642 * description: the index into the String to draw the keyboard character 1643 * mnemonic at 1644 */ 1645 public void setDisplayedMnemonicIndexAt(int tabIndex, int mnemonicIndex) { 1646 checkIndex(tabIndex); 1647 1648 Page page = pages.get(tabIndex); 1649 1650 page.setDisplayedMnemonicIndex(mnemonicIndex); 1651 } 1652 1653 /** 1654 * Sets the keyboard mnemonic for accessing the specified tab. 1655 * The mnemonic is the key which when combined with the look and feel's 1656 * mouseless modifier (usually Alt) will activate the specified 1657 * tab. 1658 * <p> 1659 * A mnemonic must correspond to a single key on the keyboard 1660 * and should be specified using one of the {@code VK_XXX} 1661 * keycodes defined in {@code java.awt.event.KeyEvent} 1662 * or one of the extended keycodes obtained through 1663 * {@code java.awt.event.KeyEvent.getExtendedKeyCodeForChar}. 1664 * Mnemonics are case-insensitive, therefore a key event 1665 * with the corresponding keycode would cause the button to be 1666 * activated whether or not the Shift modifier was pressed. 1667 * <p> 1668 * This will update the displayed mnemonic property for the specified 1669 * tab. 1670 * 1671 * @since 1.4 1672 * @param tabIndex the index of the tab that the mnemonic refers to 1673 * @param mnemonic the key code which represents the mnemonic 1674 * @exception IndexOutOfBoundsException if {@code tabIndex} is out 1675 * of range ({@code tabIndex < 0 || tabIndex >= tab count}) 1676 * @see #getMnemonicAt(int) 1677 * @see #setDisplayedMnemonicIndexAt(int,int) 1678 * 1679 * @beaninfo 1680 * bound: true 1681 * attribute: visualUpdate true 1682 * description: The keyboard mnenmonic, as a KeyEvent VK constant, 1683 * for the specified tab 1684 */ 1685 public void setMnemonicAt(int tabIndex, int mnemonic) { 1686 checkIndex(tabIndex); 1687 1688 Page page = pages.get(tabIndex); 1689 page.setMnemonic(mnemonic); 1690 1691 firePropertyChange("mnemonicAt", null, null); 1692 } 1693 1694 // end of Page setters 1695 1696 /** 1697 * Returns the first tab index with a given {@code title}, or 1698 * -1 if no tab has this title. 1699 * 1700 * @param title the title for the tab 1701 * @return the first tab index which matches {@code title}, or 1702 * -1 if no tab has this title 1703 */ 1704 public int indexOfTab(String title) { 1705 for(int i = 0; i < getTabCount(); i++) { 1706 if (getTitleAt(i).equals(title == null? "" : title)) { 1707 return i; 1708 } 1709 } 1710 return -1; 1711 } 1712 1713 /** 1714 * Returns the first tab index with a given {@code icon}, 1715 * or -1 if no tab has this icon. 1716 * 1717 * @param icon the icon for the tab 1718 * @return the first tab index which matches {@code icon}, 1719 * or -1 if no tab has this icon 1720 */ 1721 public int indexOfTab(Icon icon) { 1722 for(int i = 0; i < getTabCount(); i++) { 1723 Icon tabIcon = getIconAt(i); 1724 if ((tabIcon != null && tabIcon.equals(icon)) || 1725 (tabIcon == null && tabIcon == icon)) { 1726 return i; 1727 } 1728 } 1729 return -1; 1730 } 1731 1732 /** 1733 * Returns the index of the tab for the specified component. 1734 * Returns -1 if there is no tab for this component. 1735 * 1736 * @param component the component for the tab 1737 * @return the first tab which matches this component, or -1 1738 * if there is no tab for this component 1754 * intersects the location. 1755 * 1756 * @param x the x location relative to this tabbedpane 1757 * @param y the y location relative to this tabbedpane 1758 * @return the tab index which intersects the location, or 1759 * -1 if no tab intersects the location 1760 * @since 1.4 1761 */ 1762 public int indexAtLocation(int x, int y) { 1763 if (ui != null) { 1764 return ((TabbedPaneUI)ui).tabForCoordinate(this, x, y); 1765 } 1766 return -1; 1767 } 1768 1769 1770 /** 1771 * Returns the tooltip text for the component determined by the 1772 * mouse event location. 1773 * 1774 * @param event the {@code MouseEvent} that tells where the 1775 * cursor is lingering 1776 * @return the {@code String} containing the tooltip text 1777 */ 1778 public String getToolTipText(MouseEvent event) { 1779 if (ui != null) { 1780 int index = ((TabbedPaneUI)ui).tabForCoordinate(this, event.getX(), event.getY()); 1781 1782 if (index != -1) { 1783 return pages.get(index).tip; 1784 } 1785 } 1786 return super.getToolTipText(event); 1787 } 1788 1789 private void checkIndex(int index) { 1790 if (index < 0 || index >= pages.size()) { 1791 throw new IndexOutOfBoundsException("Index: "+index+", Tab count: "+pages.size()); 1792 } 1793 } 1794 1795 1796 /** 1797 * See {@code readObject} and {@code writeObject} in 1798 * {@code JComponent} for more 1799 * information about serialization in Swing. 1800 */ 1801 private void writeObject(ObjectOutputStream s) throws IOException { 1802 s.defaultWriteObject(); 1803 if (getUIClassID().equals(uiClassID)) { 1804 byte count = JComponent.getWriteObjCounter(this); 1805 JComponent.setWriteObjCounter(this, --count); 1806 if (count == 0 && ui != null) { 1807 ui.installUI(this); 1808 } 1809 } 1810 } 1811 1812 /* Called from the {@code JComponent}'s 1813 * {@code EnableSerializationFocusListener} to 1814 * do any Swing-specific pre-serialization configuration. 1815 */ 1816 void compWriteObjectNotify() { 1817 super.compWriteObjectNotify(); 1818 // If ToolTipText != null, then the tooltip has already been 1819 // unregistered by JComponent.compWriteObjectNotify() 1820 if (getToolTipText() == null && haveRegistered) { 1821 ToolTipManager.sharedInstance().unregisterComponent(this); 1822 } 1823 } 1824 1825 /** 1826 * See {@code readObject} and {@code writeObject} in 1827 * {@code JComponent} for more 1828 * information about serialization in Swing. 1829 */ 1830 private void readObject(ObjectInputStream s) 1831 throws IOException, ClassNotFoundException 1832 { 1833 ObjectInputStream.GetField f = s.readFields(); 1834 1835 int newTabPlacement = f.get("tabPlacement", TOP); 1836 checkTabPlacement(newTabPlacement); 1837 tabPlacement = newTabPlacement; 1838 int newTabLayoutPolicy = f.get("tabLayoutPolicy", 0); 1839 checkTabLayoutPolicy(newTabLayoutPolicy); 1840 tabLayoutPolicy = newTabLayoutPolicy; 1841 model = (SingleSelectionModel) f.get("model", null); 1842 haveRegistered = f.get("haveRegistered", false); 1843 changeListener = (ChangeListener) f.get("changeListener", null); 1844 visComp = (Component) f.get("visComp", null); 1845 1846 if ((ui != null) && (getUIClassID().equals(uiClassID))) { 1847 ui.installUI(this); 1848 } 1849 // If ToolTipText != null, then the tooltip has already been 1850 // registered by JComponent.readObject() 1851 if (getToolTipText() == null && haveRegistered) { 1852 ToolTipManager.sharedInstance().registerComponent(this); 1853 } 1854 } 1855 1856 1857 /** 1858 * Returns a string representation of this {@code JTabbedPane}. 1859 * This method 1860 * is intended to be used only for debugging purposes, and the 1861 * content and format of the returned string may vary between 1862 * implementations. The returned string may be empty but may not 1863 * be {@code null}. 1864 * 1865 * @return a string representation of this JTabbedPane. 1866 */ 1867 protected String paramString() { 1868 String tabPlacementString; 1869 if (tabPlacement == TOP) { 1870 tabPlacementString = "TOP"; 1871 } else if (tabPlacement == BOTTOM) { 1872 tabPlacementString = "BOTTOM"; 1873 } else if (tabPlacement == LEFT) { 1874 tabPlacementString = "LEFT"; 1875 } else if (tabPlacement == RIGHT) { 1876 tabPlacementString = "RIGHT"; 1877 } else tabPlacementString = ""; 1878 String haveRegisteredString = (haveRegistered ? 1879 "true" : "false"); 1880 1881 return super.paramString() + 1882 ",haveRegistered=" + haveRegisteredString + 1883 ",tabPlacement=" + tabPlacementString; 1894 * A new AccessibleJTabbedPane instance is created if necessary. 1895 * 1896 * @return an AccessibleJTabbedPane that serves as the 1897 * AccessibleContext of this JTabbedPane 1898 */ 1899 public AccessibleContext getAccessibleContext() { 1900 if (accessibleContext == null) { 1901 accessibleContext = new AccessibleJTabbedPane(); 1902 1903 // initialize AccessibleContext for the existing pages 1904 int count = getTabCount(); 1905 for (int i = 0; i < count; i++) { 1906 pages.get(i).initAccessibleContext(); 1907 } 1908 } 1909 return accessibleContext; 1910 } 1911 1912 /** 1913 * This class implements accessibility support for the 1914 * {@code JTabbedPane} class. It provides an implementation of the 1915 * Java Accessibility API appropriate to tabbed pane user-interface 1916 * elements. 1917 * <p> 1918 * <strong>Warning:</strong> 1919 * Serialized objects of this class will not be compatible with 1920 * future Swing releases. The current serialization support is 1921 * appropriate for short term storage or RMI between applications running 1922 * the same version of Swing. As of 1.4, support for long term storage 1923 * of all JavaBeans™ 1924 * has been added to the {@code java.beans} package. 1925 * Please see {@link java.beans.XMLEncoder}. 1926 */ 1927 @SuppressWarnings("serial") // Same-version serialization only 1928 protected class AccessibleJTabbedPane extends AccessibleJComponent 1929 implements AccessibleSelection, ChangeListener { 1930 1931 /** 1932 * Returns the accessible name of this object, or {@code null} if 1933 * there is no accessible name. 1934 * 1935 * @return the accessible name of this object, nor {@code null}. 1936 * @since 1.6 1937 */ 1938 public String getAccessibleName() { 1939 if (accessibleName != null) { 1940 return accessibleName; 1941 } 1942 1943 String cp = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY); 1944 1986 */ 1987 public int getAccessibleChildrenCount() { 1988 return getTabCount(); 1989 } 1990 1991 /** 1992 * Return the specified Accessible child of the object. 1993 * 1994 * @param i zero-based index of child 1995 * @return the Accessible child of the object 1996 * @exception IllegalArgumentException if index is out of bounds 1997 */ 1998 public Accessible getAccessibleChild(int i) { 1999 if (i < 0 || i >= getTabCount()) { 2000 return null; 2001 } 2002 return pages.get(i); 2003 } 2004 2005 /** 2006 * Gets the {@code AccessibleSelection} associated with 2007 * this object. In the implementation of the Java 2008 * Accessibility API for this class, 2009 * returns this object, which is responsible for implementing the 2010 * {@code AccessibleSelection} interface on behalf of itself. 2011 * 2012 * @return this object 2013 */ 2014 public AccessibleSelection getAccessibleSelection() { 2015 return this; 2016 } 2017 2018 /** 2019 * Returns the {@code Accessible} child contained at 2020 * the local coordinate {@code Point}, if one exists. 2021 * Otherwise returns the currently selected tab. 2022 * 2023 * @return the {@code Accessible} at the specified 2024 * location, if it exists 2025 */ 2026 public Accessible getAccessibleAt(Point p) { 2027 int tab = ((TabbedPaneUI) ui).tabForCoordinate(JTabbedPane.this, 2028 p.x, p.y); 2029 if (tab == -1) { 2030 tab = getSelectedIndex(); 2031 } 2032 return getAccessibleChild(tab); 2033 } 2034 2035 public int getAccessibleSelectionCount() { 2036 return 1; 2037 } 2038 2039 public Accessible getAccessibleSelection(int i) { 2040 int index = getSelectedIndex(); 2041 if (index == -1) { 2042 return null; 2043 } 2363 ((ImageIcon)icon).getAccessibleContext(); 2364 accessibleIcon = (AccessibleIcon)ac; 2365 } else if (!enabled && disabledIcon instanceof ImageIcon) { 2366 AccessibleContext ac = 2367 ((ImageIcon)disabledIcon).getAccessibleContext(); 2368 accessibleIcon = (AccessibleIcon)ac; 2369 } 2370 if (accessibleIcon != null) { 2371 AccessibleIcon [] returnIcons = new AccessibleIcon[1]; 2372 returnIcons[0] = accessibleIcon; 2373 return returnIcons; 2374 } else { 2375 return null; 2376 } 2377 } 2378 } 2379 2380 /** 2381 * Sets the component that is responsible for rendering the 2382 * title for the specified tab. A null value means 2383 * {@code JTabbedPane} will render the title and/or icon for 2384 * the specified tab. A non-null value means the component will 2385 * render the title and {@code JTabbedPane} will not render 2386 * the title and/or icon. 2387 * <p> 2388 * Note: The component must not be one that the developer has 2389 * already added to the tabbed pane. 2390 * 2391 * @param index the tab index where the component should be set 2392 * @param component the component to render the title for the 2393 * specified tab 2394 * @exception IndexOutOfBoundsException if index is out of range 2395 * {@code (index < 0 || index >= tab count)} 2396 * @exception IllegalArgumentException if component has already been 2397 * added to this {@code JTabbedPane} 2398 * 2399 * @see #getTabComponentAt 2400 * @beaninfo 2401 * preferred: true 2402 * attribute: visualUpdate true 2403 * description: The tab component at the specified tab index. 2404 * @since 1.6 2405 */ 2406 public void setTabComponentAt(int index, Component component) { 2407 if (component != null && indexOfComponent(component) != -1) { 2408 throw new IllegalArgumentException("Component is already added to this JTabbedPane"); 2409 } 2410 Component oldValue = getTabComponentAt(index); 2411 if (component != oldValue) { 2412 int tabComponentIndex = indexOfTabComponent(component); 2413 if (tabComponentIndex != -1) { 2414 setTabComponentAt(tabComponentIndex, null); 2415 } 2416 pages.get(index).tabComponent = component; 2417 firePropertyChange("indexForTabComponent", -1, index); 2418 } 2419 } 2420 2421 /** 2422 * Returns the tab component at {@code index}. 2423 * 2424 * @param index the index of the item being queried 2425 * @return the tab component at {@code index} 2426 * @exception IndexOutOfBoundsException if index is out of range 2427 * {@code (index < 0 || index >= tab count)} 2428 * 2429 * @see #setTabComponentAt 2430 * @since 1.6 2431 */ 2432 public Component getTabComponentAt(int index) { 2433 return pages.get(index).tabComponent; 2434 } 2435 2436 /** 2437 * Returns the index of the tab for the specified tab component. 2438 * Returns -1 if there is no tab for this tab component. 2439 * 2440 * @param tabComponent the tab component for the tab 2441 * @return the first tab which matches this tab component, or -1 2442 * if there is no tab for this tab component 2443 * @see #setTabComponentAt 2444 * @see #getTabComponentAt 2445 * @since 1.6 |