1 /* 2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing; 27 28 import java.awt.*; 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<sup><font size="-2">TM</font></sup> 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 */ 109 public class JTabbedPane extends JComponent 110 implements Serializable, Accessible, SwingConstants { 111 112 /** 113 * The tab layout policy for wrapping tabs in multiple runs when all 114 * tabs will not fit within a single run. 115 */ 116 public static final int WRAP_TAB_LAYOUT = 0; 117 118 /** 119 * Tab layout policy for providing a subset of available tabs when all 120 * the tabs will not fit within a single run. If all the tabs do 121 * not fit within a single run the look and feel will provide a way 122 * to navigate to hidden tabs. 123 */ 124 public static final int SCROLL_TAB_LAYOUT = 1; 125 126 127 /** 128 * @see #getUIClassID 129 * @see #readObject 130 */ 131 private static final String uiClassID = "TabbedPaneUI"; 132 133 /** 134 * Where the tabs are placed. 135 * @see #setTabPlacement 136 */ 137 protected int tabPlacement = TOP; 138 139 private int tabLayoutPolicy; 140 141 /** The default selection model */ 142 protected SingleSelectionModel model; 143 144 private boolean haveRegistered; 145 146 /** 147 * The <code>changeListener</code> is the listener we add to the 148 * model. 149 */ 150 protected ChangeListener changeListener = null; 151 152 private final java.util.List<Page> pages; 153 154 /* The component that is currently visible */ 155 private Component visComp = null; 156 157 /** 158 * Only one <code>ChangeEvent</code> is needed per <code>TabPane</code> 159 * instance since the 160 * event's only (read-only) state is the source property. The source 161 * of events generated here is always "this". 162 */ 163 protected transient ChangeEvent changeEvent = null; 164 165 /** 166 * Creates an empty <code>TabbedPane</code> with a default 167 * tab placement of <code>JTabbedPane.TOP</code>. 168 * @see #addTab 169 */ 170 public JTabbedPane() { 171 this(TOP, WRAP_TAB_LAYOUT); 172 } 173 174 /** 175 * Creates an empty <code>TabbedPane</code> with the specified tab placement 176 * of either: <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>, 177 * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>. 178 * 179 * @param tabPlacement the placement for the tabs relative to the content 180 * @see #addTab 181 */ 182 public JTabbedPane(int tabPlacement) { 183 this(tabPlacement, WRAP_TAB_LAYOUT); 184 } 185 186 /** 187 * Creates an empty <code>TabbedPane</code> with the specified tab placement 188 * and tab layout policy. Tab placement may be either: 189 * <code>JTabbedPane.TOP</code>, <code>JTabbedPane.BOTTOM</code>, 190 * <code>JTabbedPane.LEFT</code>, or <code>JTabbedPane.RIGHT</code>. 191 * Tab layout policy may be either: <code>JTabbedPane.WRAP_TAB_LAYOUT</code> 192 * or <code>JTabbedPane.SCROLL_TAB_LAYOUT</code>. 193 * 194 * @param tabPlacement the placement for the tabs relative to the content 195 * @param tabLayoutPolicy the policy for laying out tabs when all tabs will not fit on one run 196 * @exception IllegalArgumentException if tab placement or tab layout policy are not 197 * one of the above supported values 198 * @see #addTab 199 * @since 1.4 200 */ 201 public JTabbedPane(int tabPlacement, int tabLayoutPolicy) { 202 setTabPlacement(tabPlacement); 203 setTabLayoutPolicy(tabLayoutPolicy); 204 pages = new ArrayList<Page>(1); 205 setModel(new DefaultSingleSelectionModel()); 206 updateUI(); 207 } 208 209 /** 210 * Returns the UI object which implements the L&F for this component. 211 * 212 * @return a <code>TabbedPaneUI</code> object 213 * @see #setUI 214 */ 215 public TabbedPaneUI getUI() { 216 return (TabbedPaneUI)ui; 217 } 218 219 /** 220 * Sets the UI object which implements the L&F for this component. 221 * 222 * @param ui the new UI object 223 * @see UIDefaults#getUI 224 * @beaninfo 225 * bound: true 226 * hidden: true 227 * attribute: visualUpdate true 228 * description: The UI object that implements the tabbedpane's LookAndFeel 229 */ 230 public void setUI(TabbedPaneUI ui) { 231 super.setUI(ui); 232 // disabled icons are generated by LF so they should be unset here 233 for (int i = 0; i < getTabCount(); i++) { 234 Icon icon = pages.get(i).disabledIcon; 235 if (icon instanceof UIResource) { 236 setDisabledIconAt(i, null); 237 } 238 } 239 } 240 241 /** 242 * Resets the UI property to a value from the current look and feel. 243 * 244 * @see JComponent#updateUI 245 */ 246 public void updateUI() { 247 setUI((TabbedPaneUI)UIManager.getUI(this)); 248 } 249 250 251 /** 252 * Returns the name of the UI class that implements the 253 * L&F for this component. 254 * 255 * @return the string "TabbedPaneUI" 256 * @see JComponent#getUIClassID 257 * @see UIDefaults#getUI 258 */ 259 public String getUIClassID() { 260 return uiClassID; 261 } 262 263 264 /** 265 * We pass <code>ModelChanged</code> events along to the listeners with 266 * the tabbedpane (instead of the model itself) as the event source. 267 */ 268 protected class ModelListener implements ChangeListener, Serializable { 269 public void stateChanged(ChangeEvent e) { 270 fireStateChanged(); 271 } 272 } 273 274 /** 275 * Subclasses that want to handle <code>ChangeEvents</code> differently 276 * can override this to return a subclass of <code>ModelListener</code> or 277 * another <code>ChangeListener</code> implementation. 278 * 279 * @see #fireStateChanged 280 */ 281 protected ChangeListener createChangeListener() { 282 return new ModelListener(); 283 } 284 285 /** 286 * Adds a <code>ChangeListener</code> to this tabbedpane. 287 * 288 * @param l the <code>ChangeListener</code> to add 289 * @see #fireStateChanged 290 * @see #removeChangeListener 291 */ 292 public void addChangeListener(ChangeListener l) { 293 listenerList.add(ChangeListener.class, l); 294 } 295 296 /** 297 * Removes a <code>ChangeListener</code> from this tabbedpane. 298 * 299 * @param l the <code>ChangeListener</code> to remove 300 * @see #fireStateChanged 301 * @see #addChangeListener 302 */ 303 public void removeChangeListener(ChangeListener l) { 304 listenerList.remove(ChangeListener.class, l); 305 } 306 307 /** 308 * Returns an array of all the <code>ChangeListener</code>s added 309 * to this <code>JTabbedPane</code> with <code>addChangeListener</code>. 310 * 311 * @return all of the <code>ChangeListener</code>s added or an empty 312 * array if no listeners have been added 313 * @since 1.4 314 */ 315 public ChangeListener[] getChangeListeners() { 316 return listenerList.getListeners(ChangeListener.class); 317 } 318 319 /** 320 * Sends a {@code ChangeEvent}, with this {@code JTabbedPane} as the source, 321 * to each registered listener. This method is called each time there is 322 * a change to either the selected index or the selected tab in the 323 * {@code JTabbedPane}. Usually, the selected index and selected tab change 324 * together. However, there are some cases, such as tab addition, where the 325 * selected index changes and the same tab remains selected. There are other 326 * cases, such as deleting the selected tab, where the index remains the 327 * same, but a new tab moves to that index. Events are fired for all of 328 * these cases. 329 * 330 * @see #addChangeListener 331 * @see EventListenerList 332 */ 333 protected void fireStateChanged() { 334 /* --- Begin code to deal with visibility --- */ 335 336 /* This code deals with changing the visibility of components to 337 * hide and show the contents for the selected tab. It duplicates 338 * logic already present in BasicTabbedPaneUI, logic that is 339 * processed during the layout pass. This code exists to allow 340 * developers to do things that are quite difficult to accomplish 341 * with the previous model of waiting for the layout pass to process 342 * visibility changes; such as requesting focus on the new visible 343 * component. 344 * 345 * For the average code, using the typical JTabbedPane methods, 346 * all visibility changes will now be processed here. However, 347 * the code in BasicTabbedPaneUI still exists, for the purposes 348 * of backward compatibility. Therefore, when making changes to 349 * this code, ensure that the BasicTabbedPaneUI code is kept in 350 * synch. 351 */ 352 353 int selIndex = getSelectedIndex(); 354 355 /* if the selection is now nothing */ 356 if (selIndex < 0) { 357 /* if there was a previous visible component */ 358 if (visComp != null && visComp.isVisible()) { 359 /* make it invisible */ 360 visComp.setVisible(false); 361 } 362 363 /* now there's no visible component */ 364 visComp = null; 365 366 /* else - the selection is now something */ 367 } else { 368 /* Fetch the component for the new selection */ 369 Component newComp = getComponentAt(selIndex); 370 371 /* if the new component is non-null and different */ 372 if (newComp != null && newComp != visComp) { 373 boolean shouldChangeFocus = false; 374 375 /* Note: the following (clearing of the old visible component) 376 * is inside this if-statement for good reason: Tabbed pane 377 * should continue to show the previously visible component 378 * if there is no component for the chosen tab. 379 */ 380 381 /* if there was a previous visible component */ 382 if (visComp != null) { 383 shouldChangeFocus = 384 (SwingUtilities.findFocusOwner(visComp) != null); 385 386 /* if it's still visible */ 387 if (visComp.isVisible()) { 388 /* make it invisible */ 389 visComp.setVisible(false); 390 } 391 } 392 393 if (!newComp.isVisible()) { 394 newComp.setVisible(true); 395 } 396 397 if (shouldChangeFocus) { 398 SwingUtilities2.tabbedPaneChangeFocusTo(newComp); 399 } 400 401 visComp = newComp; 402 } /* else - the visible component shouldn't changed */ 403 } 404 405 /* --- End code to deal with visibility --- */ 406 407 // Guaranteed to return a non-null array 408 Object[] listeners = listenerList.getListenerList(); 409 // Process the listeners last to first, notifying 410 // those that are interested in this event 411 for (int i = listeners.length-2; i>=0; i-=2) { 412 if (listeners[i]==ChangeListener.class) { 413 // Lazily create the event: 414 if (changeEvent == null) 415 changeEvent = new ChangeEvent(this); 416 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent); 417 } 418 } 419 } 420 421 /** 422 * Returns the model associated with this tabbedpane. 423 * 424 * @see #setModel 425 */ 426 public SingleSelectionModel getModel() { 427 return model; 428 } 429 430 /** 431 * Sets the model to be used with this tabbedpane. 432 * 433 * @param model the model to be used 434 * @see #getModel 435 * @beaninfo 436 * bound: true 437 * description: The tabbedpane's SingleSelectionModel. 438 */ 439 public void setModel(SingleSelectionModel model) { 440 SingleSelectionModel oldModel = getModel(); 441 442 if (oldModel != null) { 443 oldModel.removeChangeListener(changeListener); 444 changeListener = null; 445 } 446 447 this.model = model; 448 449 if (model != null) { 450 changeListener = createChangeListener(); 451 model.addChangeListener(changeListener); 452 } 453 454 firePropertyChange("model", oldModel, model); 455 repaint(); 456 } 457 458 /** 459 * Returns the placement of the tabs for this tabbedpane. 460 * @see #setTabPlacement 461 */ 462 public int getTabPlacement() { 463 return tabPlacement; 464 } 465 466 /** 467 * Sets the tab placement for this tabbedpane. 468 * Possible values are:<ul> 469 * <li><code>JTabbedPane.TOP</code> 470 * <li><code>JTabbedPane.BOTTOM</code> 471 * <li><code>JTabbedPane.LEFT</code> 472 * <li><code>JTabbedPane.RIGHT</code> 473 * </ul> 474 * The default value, if not set, is <code>SwingConstants.TOP</code>. 475 * 476 * @param tabPlacement the placement for the tabs relative to the content 477 * @exception IllegalArgumentException if tab placement value isn't one 478 * of the above valid values 479 * 480 * @beaninfo 481 * preferred: true 482 * bound: true 483 * attribute: visualUpdate true 484 * enum: TOP JTabbedPane.TOP 485 * LEFT JTabbedPane.LEFT 486 * BOTTOM JTabbedPane.BOTTOM 487 * RIGHT JTabbedPane.RIGHT 488 * description: The tabbedpane's tab placement. 489 * 490 */ 491 public void setTabPlacement(int tabPlacement) { 492 if (tabPlacement != TOP && tabPlacement != LEFT && 493 tabPlacement != BOTTOM && tabPlacement != RIGHT) { 494 throw new IllegalArgumentException("illegal tab placement: must be TOP, BOTTOM, LEFT, or RIGHT"); 495 } 496 if (this.tabPlacement != tabPlacement) { 497 int oldValue = this.tabPlacement; 498 this.tabPlacement = tabPlacement; 499 firePropertyChange("tabPlacement", oldValue, tabPlacement); 500 revalidate(); 501 repaint(); 502 } 503 } 504 505 /** 506 * Returns the policy used by the tabbedpane to layout the tabs when all the 507 * tabs will not fit within a single run. 508 * @see #setTabLayoutPolicy 509 * @since 1.4 510 */ 511 public int getTabLayoutPolicy() { 512 return tabLayoutPolicy; 513 } 514 515 /** 516 * Sets the policy which the tabbedpane will use in laying out the tabs 517 * when all the tabs will not fit within a single run. 518 * Possible values are: 519 * <ul> 520 * <li><code>JTabbedPane.WRAP_TAB_LAYOUT</code> 521 * <li><code>JTabbedPane.SCROLL_TAB_LAYOUT</code> 522 * </ul> 523 * 524 * The default value, if not set by the UI, is <code>JTabbedPane.WRAP_TAB_LAYOUT</code>. 525 * <p> 526 * Some look and feels might only support a subset of the possible 527 * layout policies, in which case the value of this property may be 528 * ignored. 529 * 530 * @param tabLayoutPolicy the policy used to layout the tabs 531 * @exception IllegalArgumentException if layoutPolicy value isn't one 532 * of the above valid values 533 * @see #getTabLayoutPolicy 534 * @since 1.4 535 * 536 * @beaninfo 537 * preferred: true 538 * bound: true 539 * attribute: visualUpdate true 540 * enum: WRAP_TAB_LAYOUT JTabbedPane.WRAP_TAB_LAYOUT 541 * SCROLL_TAB_LAYOUT JTabbedPane.SCROLL_TAB_LAYOUT 542 * description: The tabbedpane's policy for laying out the tabs 543 * 544 */ 545 public void setTabLayoutPolicy(int tabLayoutPolicy) { 546 if (tabLayoutPolicy != WRAP_TAB_LAYOUT && tabLayoutPolicy != SCROLL_TAB_LAYOUT) { 547 throw new IllegalArgumentException("illegal tab layout policy: must be WRAP_TAB_LAYOUT or SCROLL_TAB_LAYOUT"); 548 } 549 if (this.tabLayoutPolicy != tabLayoutPolicy) { 550 int oldValue = this.tabLayoutPolicy; 551 this.tabLayoutPolicy = tabLayoutPolicy; 552 firePropertyChange("tabLayoutPolicy", oldValue, tabLayoutPolicy); 553 revalidate(); 554 repaint(); 555 } 556 } 557 558 /** 559 * Returns the currently selected index for this tabbedpane. 560 * Returns -1 if there is no currently selected tab. 561 * 562 * @return the index of the selected tab 563 * @see #setSelectedIndex 564 */ 565 @Transient 566 public int getSelectedIndex() { 567 return model.getSelectedIndex(); 568 } 569 570 /** 571 * Sets the selected index for this tabbedpane. The index must be 572 * a valid tab index or -1, which indicates that no tab should be selected 573 * (can also be used when there are no tabs in the tabbedpane). If a -1 574 * value is specified when the tabbedpane contains one or more tabs, then 575 * the results will be implementation defined. 576 * 577 * @param index the index to be selected 578 * @exception IndexOutOfBoundsException if index is out of range 579 * (index < -1 || index >= tab count) 580 * 581 * @see #getSelectedIndex 582 * @see SingleSelectionModel#setSelectedIndex 583 * @beaninfo 584 * preferred: true 585 * description: The tabbedpane's selected tab index. 586 */ 587 public void setSelectedIndex(int index) { 588 if (index != -1) { 589 checkIndex(index); 590 } 591 setSelectedIndexImpl(index, true); 592 } 593 594 595 private void setSelectedIndexImpl(int index, boolean doAccessibleChanges) { 596 int oldIndex = model.getSelectedIndex(); 597 Page oldPage = null, newPage = null; 598 String oldName = null; 599 600 doAccessibleChanges = doAccessibleChanges && (oldIndex != index); 601 602 if (doAccessibleChanges) { 603 if (accessibleContext != null) { 604 oldName = accessibleContext.getAccessibleName(); 605 } 606 607 if (oldIndex >= 0) { 608 oldPage = pages.get(oldIndex); 609 } 610 611 if (index >= 0) { 612 newPage = pages.get(index); 613 } 614 } 615 616 model.setSelectedIndex(index); 617 618 if (doAccessibleChanges) { 619 changeAccessibleSelection(oldPage, oldName, newPage); 620 } 621 } 622 623 private void changeAccessibleSelection(Page oldPage, String oldName, Page newPage) { 624 if (accessibleContext == null) { 625 return; 626 } 627 628 if (oldPage != null) { 629 oldPage.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 630 AccessibleState.SELECTED, null); 631 } 632 633 if (newPage != null) { 634 newPage.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 635 null, AccessibleState.SELECTED); 636 } 637 638 accessibleContext.firePropertyChange( 639 AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 640 oldName, 641 accessibleContext.getAccessibleName()); 642 } 643 644 /** 645 * Returns the currently selected component for this tabbedpane. 646 * Returns <code>null</code> if there is no currently selected tab. 647 * 648 * @return the component corresponding to the selected tab 649 * @see #setSelectedComponent 650 */ 651 @Transient 652 public Component getSelectedComponent() { 653 int index = getSelectedIndex(); 654 if (index == -1) { 655 return null; 656 } 657 return getComponentAt(index); 658 } 659 660 /** 661 * Sets the selected component for this tabbedpane. This 662 * will automatically set the <code>selectedIndex</code> to the index 663 * corresponding to the specified component. 664 * 665 * @exception IllegalArgumentException if component not found in tabbed 666 * pane 667 * @see #getSelectedComponent 668 * @beaninfo 669 * preferred: true 670 * description: The tabbedpane's selected component. 671 */ 672 public void setSelectedComponent(Component c) { 673 int index = indexOfComponent(c); 674 if (index != -1) { 675 setSelectedIndex(index); 676 } else { 677 throw new IllegalArgumentException("component not found in tabbed pane"); 678 } 679 } 680 681 /** 682 * Inserts a new tab for the given component, at the given index, 683 * represented by the given title and/or icon, either of which may 684 * be {@code null}. 685 * 686 * @param title the title to be displayed on the tab 687 * @param icon the icon to be displayed on the tab 688 * @param component the component to be displayed when this tab is clicked. 689 * @param tip the tooltip to be displayed for this tab 690 * @param index the position to insert this new tab 691 * ({@code > 0 and <= getTabCount()}) 692 * 693 * @throws IndexOutOfBoundsException if the index is out of range 694 * ({@code < 0 or > getTabCount()}) 695 * 696 * @see #addTab 697 * @see #removeTabAt 698 */ 699 public void insertTab(String title, Icon icon, Component component, String tip, int index) { 700 int newIndex = index; 701 702 // If component already exists, remove corresponding 703 // tab so that new tab gets added correctly 704 // Note: we are allowing component=null because of compatibility, 705 // but we really should throw an exception because much of the 706 // rest of the JTabbedPane implementation isn't designed to deal 707 // with null components for tabs. 708 int removeIndex = indexOfComponent(component); 709 if (component != null && removeIndex != -1) { 710 removeTabAt(removeIndex); 711 if (newIndex > removeIndex) { 712 newIndex--; 713 } 714 } 715 716 int selectedIndex = getSelectedIndex(); 717 718 pages.add( 719 newIndex, 720 new Page(this, title != null? title : "", icon, null, component, tip)); 721 722 723 if (component != null) { 724 addImpl(component, null, -1); 725 component.setVisible(false); 726 } else { 727 firePropertyChange("indexForNullComponent", -1, index); 728 } 729 730 if (pages.size() == 1) { 731 setSelectedIndex(0); 732 } 733 734 if (selectedIndex >= newIndex) { 735 setSelectedIndexImpl(selectedIndex + 1, false); 736 } 737 738 if (!haveRegistered && tip != null) { 739 ToolTipManager.sharedInstance().registerComponent(this); 740 haveRegistered = true; 741 } 742 743 if (accessibleContext != null) { 744 accessibleContext.firePropertyChange( 745 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 746 null, component); 747 } 748 revalidate(); 749 repaint(); 750 } 751 752 /** 753 * Adds a <code>component</code> and <code>tip</code> 754 * represented by a <code>title</code> and/or <code>icon</code>, 755 * either of which can be <code>null</code>. 756 * Cover method for <code>insertTab</code>. 757 * 758 * @param title the title to be displayed in this tab 759 * @param icon the icon to be displayed in this tab 760 * @param component the component to be displayed when this tab is clicked 761 * @param tip the tooltip to be displayed for this tab 762 * 763 * @see #insertTab 764 * @see #removeTabAt 765 */ 766 public void addTab(String title, Icon icon, Component component, String tip) { 767 insertTab(title, icon, component, tip, pages.size()); 768 } 769 770 /** 771 * Adds a <code>component</code> represented by a <code>title</code> 772 * and/or <code>icon</code>, either of which can be <code>null</code>. 773 * Cover method for <code>insertTab</code>. 774 * 775 * @param title the title to be displayed in this tab 776 * @param icon the icon to be displayed in this tab 777 * @param component the component to be displayed when this tab is clicked 778 * 779 * @see #insertTab 780 * @see #removeTabAt 781 */ 782 public void addTab(String title, Icon icon, Component component) { 783 insertTab(title, icon, component, null, pages.size()); 784 } 785 786 /** 787 * Adds a <code>component</code> represented by a <code>title</code> 788 * and no icon. 789 * Cover method for <code>insertTab</code>. 790 * 791 * @param title the title to be displayed in this tab 792 * @param component the component to be displayed when this tab is clicked 793 * 794 * @see #insertTab 795 * @see #removeTabAt 796 */ 797 public void addTab(String title, Component component) { 798 insertTab(title, null, component, null, pages.size()); 799 } 800 801 /** 802 * Adds a <code>component</code> with a tab title defaulting to 803 * the name of the component which is the result of calling 804 * <code>component.getName</code>. 805 * Cover method for <code>insertTab</code>. 806 * 807 * @param component the component to be displayed when this tab is clicked 808 * @return the component 809 * 810 * @see #insertTab 811 * @see #removeTabAt 812 */ 813 public Component add(Component component) { 814 if (!(component instanceof UIResource)) { 815 addTab(component.getName(), component); 816 } else { 817 super.add(component); 818 } 819 return component; 820 } 821 822 /** 823 * Adds a <code>component</code> with the specified tab title. 824 * Cover method for <code>insertTab</code>. 825 * 826 * @param title the title to be displayed in this tab 827 * @param component the component to be displayed when this tab is clicked 828 * @return the component 829 * 830 * @see #insertTab 831 * @see #removeTabAt 832 */ 833 public Component add(String title, Component component) { 834 if (!(component instanceof UIResource)) { 835 addTab(title, component); 836 } else { 837 super.add(title, component); 838 } 839 return component; 840 } 841 842 /** 843 * Adds a <code>component</code> at the specified tab index with a tab 844 * title defaulting to the name of the component. 845 * Cover method for <code>insertTab</code>. 846 * 847 * @param component the component to be displayed when this tab is clicked 848 * @param index the position to insert this new tab 849 * @return the component 850 * 851 * @see #insertTab 852 * @see #removeTabAt 853 */ 854 public Component add(Component component, int index) { 855 if (!(component instanceof UIResource)) { 856 // Container.add() interprets -1 as "append", so convert 857 // the index appropriately to be handled by the vector 858 insertTab(component.getName(), null, component, null, 859 index == -1? getTabCount() : index); 860 } else { 861 super.add(component, index); 862 } 863 return component; 864 } 865 866 /** 867 * Adds a <code>component</code> to the tabbed pane. 868 * If <code>constraints</code> is a <code>String</code> or an 869 * <code>Icon</code>, it will be used for the tab title, 870 * otherwise the component's name will be used as the tab title. 871 * Cover method for <code>insertTab</code>. 872 * 873 * @param component the component to be displayed when this tab is clicked 874 * @param constraints the object to be displayed in the tab 875 * 876 * @see #insertTab 877 * @see #removeTabAt 878 */ 879 public void add(Component component, Object constraints) { 880 if (!(component instanceof UIResource)) { 881 if (constraints instanceof String) { 882 addTab((String)constraints, component); 883 } else if (constraints instanceof Icon) { 884 addTab(null, (Icon)constraints, component); 885 } else { 886 add(component); 887 } 888 } else { 889 super.add(component, constraints); 890 } 891 } 892 893 /** 894 * Adds a <code>component</code> at the specified tab index. 895 * If <code>constraints</code> is a <code>String</code> or an 896 * <code>Icon</code>, it will be used for the tab title, 897 * otherwise the component's name will be used as the tab title. 898 * Cover method for <code>insertTab</code>. 899 * 900 * @param component the component to be displayed when this tab is clicked 901 * @param constraints the object to be displayed in the tab 902 * @param index the position to insert this new tab 903 * 904 * @see #insertTab 905 * @see #removeTabAt 906 */ 907 public void add(Component component, Object constraints, int index) { 908 if (!(component instanceof UIResource)) { 909 910 Icon icon = constraints instanceof Icon? (Icon)constraints : null; 911 String title = constraints instanceof String? (String)constraints : null; 912 // Container.add() interprets -1 as "append", so convert 913 // the index appropriately to be handled by the vector 914 insertTab(title, icon, component, null, index == -1? getTabCount() : index); 915 } else { 916 super.add(component, constraints, index); 917 } 918 } 919 920 /** 921 * Removes the tab at <code>index</code>. 922 * After the component associated with <code>index</code> is removed, 923 * its visibility is reset to true to ensure it will be visible 924 * if added to other containers. 925 * @param index the index of the tab to be removed 926 * @exception IndexOutOfBoundsException if index is out of range 927 * (index < 0 || index >= tab count) 928 * 929 * @see #addTab 930 * @see #insertTab 931 */ 932 public void removeTabAt(int index) { 933 checkIndex(index); 934 935 Component component = getComponentAt(index); 936 boolean shouldChangeFocus = false; 937 int selected = getSelectedIndex(); 938 String oldName = null; 939 940 /* if we're about to remove the visible component */ 941 if (component == visComp) { 942 shouldChangeFocus = (SwingUtilities.findFocusOwner(visComp) != null); 943 visComp = null; 944 } 945 946 if (accessibleContext != null) { 947 /* if we're removing the selected page */ 948 if (index == selected) { 949 /* fire an accessible notification that it's unselected */ 950 pages.get(index).firePropertyChange( 951 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 952 AccessibleState.SELECTED, null); 953 954 oldName = accessibleContext.getAccessibleName(); 955 } 956 957 accessibleContext.firePropertyChange( 958 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 959 component, null); 960 } 961 962 // Force the tabComponent to be cleaned up. 963 setTabComponentAt(index, null); 964 pages.remove(index); 965 966 // NOTE 4/15/2002 (joutwate): 967 // This fix is implemented using client properties since there is 968 // currently no IndexPropertyChangeEvent. Once 969 // IndexPropertyChangeEvents have been added this code should be 970 // modified to use it. 971 putClientProperty("__index_to_remove__", Integer.valueOf(index)); 972 973 /* if the selected tab is after the removal */ 974 if (selected > index) { 975 setSelectedIndexImpl(selected - 1, false); 976 977 /* if the selected tab is the last tab */ 978 } else if (selected >= getTabCount()) { 979 setSelectedIndexImpl(selected - 1, false); 980 Page newSelected = (selected != 0) 981 ? pages.get(selected - 1) 982 : null; 983 984 changeAccessibleSelection(null, oldName, newSelected); 985 986 /* selected index hasn't changed, but the associated tab has */ 987 } else if (index == selected) { 988 fireStateChanged(); 989 changeAccessibleSelection(null, oldName, pages.get(index)); 990 } 991 992 // We can't assume the tab indices correspond to the 993 // container's children array indices, so make sure we 994 // remove the correct child! 995 if (component != null) { 996 Component components[] = getComponents(); 997 for (int i = components.length; --i >= 0; ) { 998 if (components[i] == component) { 999 super.remove(i); 1000 component.setVisible(true); 1001 break; 1002 } 1003 } 1004 } 1005 1006 if (shouldChangeFocus) { 1007 SwingUtilities2.tabbedPaneChangeFocusTo(getSelectedComponent()); 1008 } 1009 1010 revalidate(); 1011 repaint(); 1012 } 1013 1014 /** 1015 * Removes the specified <code>Component</code> from the 1016 * <code>JTabbedPane</code>. The method does nothing 1017 * if the <code>component</code> is null. 1018 * 1019 * @param component the component to remove from the tabbedpane 1020 * @see #addTab 1021 * @see #removeTabAt 1022 */ 1023 public void remove(Component component) { 1024 int index = indexOfComponent(component); 1025 if (index != -1) { 1026 removeTabAt(index); 1027 } else { 1028 // Container#remove(comp) invokes Container#remove(int) 1029 // so make sure JTabbedPane#remove(int) isn't called here 1030 Component children[] = getComponents(); 1031 for (int i=0; i < children.length; i++) { 1032 if (component == children[i]) { 1033 super.remove(i); 1034 break; 1035 } 1036 } 1037 } 1038 } 1039 1040 /** 1041 * Removes the tab and component which corresponds to the specified index. 1042 * 1043 * @param index the index of the component to remove from the 1044 * <code>tabbedpane</code> 1045 * @exception IndexOutOfBoundsException if index is out of range 1046 * (index < 0 || index >= tab count) 1047 * @see #addTab 1048 * @see #removeTabAt 1049 */ 1050 public void remove(int index) { 1051 removeTabAt(index); 1052 } 1053 1054 /** 1055 * Removes all the tabs and their corresponding components 1056 * from the <code>tabbedpane</code>. 1057 * 1058 * @see #addTab 1059 * @see #removeTabAt 1060 */ 1061 public void removeAll() { 1062 setSelectedIndexImpl(-1, true); 1063 1064 int tabCount = getTabCount(); 1065 // We invoke removeTabAt for each tab, otherwise we may end up 1066 // removing Components added by the UI. 1067 while (tabCount-- > 0) { 1068 removeTabAt(tabCount); 1069 } 1070 } 1071 1072 /** 1073 * Returns the number of tabs in this <code>tabbedpane</code>. 1074 * 1075 * @return an integer specifying the number of tabbed pages 1076 */ 1077 public int getTabCount() { 1078 return pages.size(); 1079 } 1080 1081 /** 1082 * Returns the number of tab runs currently used to display 1083 * the tabs. 1084 * @return an integer giving the number of rows if the 1085 * <code>tabPlacement</code> 1086 * is <code>TOP</code> or <code>BOTTOM</code> 1087 * and the number of columns if 1088 * <code>tabPlacement</code> 1089 * is <code>LEFT</code> or <code>RIGHT</code>, 1090 * or 0 if there is no UI set on this <code>tabbedpane</code> 1091 */ 1092 public int getTabRunCount() { 1093 if (ui != null) { 1094 return ((TabbedPaneUI)ui).getTabRunCount(this); 1095 } 1096 return 0; 1097 } 1098 1099 1100 // Getters for the Pages 1101 1102 /** 1103 * Returns the tab title at <code>index</code>. 1104 * 1105 * @param index the index of the item being queried 1106 * @return the title at <code>index</code> 1107 * @exception IndexOutOfBoundsException if index is out of range 1108 * (index < 0 || index >= tab count) 1109 * @see #setTitleAt 1110 */ 1111 public String getTitleAt(int index) { 1112 return pages.get(index).title; 1113 } 1114 1115 /** 1116 * Returns the tab icon at <code>index</code>. 1117 * 1118 * @param index the index of the item being queried 1119 * @return the icon at <code>index</code> 1120 * @exception IndexOutOfBoundsException if index is out of range 1121 * (index < 0 || index >= tab count) 1122 * 1123 * @see #setIconAt 1124 */ 1125 public Icon getIconAt(int index) { 1126 return pages.get(index).icon; 1127 } 1128 1129 /** 1130 * Returns the tab disabled icon at <code>index</code>. 1131 * If the tab disabled icon doesn't exist at <code>index</code> 1132 * this will forward the call to the look and feel to construct 1133 * an appropriate disabled Icon from the corresponding enabled 1134 * Icon. Some look and feels might not render the disabled Icon, 1135 * in which case it won't be created. 1136 * 1137 * @param index the index of the item being queried 1138 * @return the icon at <code>index</code> 1139 * @exception IndexOutOfBoundsException if index is out of range 1140 * (index < 0 || index >= tab count) 1141 * 1142 * @see #setDisabledIconAt 1143 */ 1144 public Icon getDisabledIconAt(int index) { 1145 Page page = pages.get(index); 1146 if (page.disabledIcon == null) { 1147 page.disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(this, page.icon); 1148 } 1149 return page.disabledIcon; 1150 } 1151 1152 /** 1153 * Returns the tab tooltip text at <code>index</code>. 1154 * 1155 * @param index the index of the item being queried 1156 * @return a string containing the tool tip text at <code>index</code> 1157 * @exception IndexOutOfBoundsException if index is out of range 1158 * (index < 0 || index >= tab count) 1159 * 1160 * @see #setToolTipTextAt 1161 * @since 1.3 1162 */ 1163 public String getToolTipTextAt(int index) { 1164 return pages.get(index).tip; 1165 } 1166 1167 /** 1168 * Returns the tab background color at <code>index</code>. 1169 * 1170 * @param index the index of the item being queried 1171 * @return the <code>Color</code> of the tab background at 1172 * <code>index</code> 1173 * @exception IndexOutOfBoundsException if index is out of range 1174 * (index < 0 || index >= tab count) 1175 * 1176 * @see #setBackgroundAt 1177 */ 1178 public Color getBackgroundAt(int index) { 1179 return pages.get(index).getBackground(); 1180 } 1181 1182 /** 1183 * Returns the tab foreground color at <code>index</code>. 1184 * 1185 * @param index the index of the item being queried 1186 * @return the <code>Color</code> of the tab foreground at 1187 * <code>index</code> 1188 * @exception IndexOutOfBoundsException if index is out of range 1189 * (index < 0 || index >= tab count) 1190 * 1191 * @see #setForegroundAt 1192 */ 1193 public Color getForegroundAt(int index) { 1194 return pages.get(index).getForeground(); 1195 } 1196 1197 /** 1198 * Returns whether or not the tab at <code>index</code> is 1199 * currently enabled. 1200 * 1201 * @param index the index of the item being queried 1202 * @return true if the tab at <code>index</code> is enabled; 1203 * false otherwise 1204 * @exception IndexOutOfBoundsException if index is out of range 1205 * (index < 0 || index >= tab count) 1206 * 1207 * @see #setEnabledAt 1208 */ 1209 public boolean isEnabledAt(int index) { 1210 return pages.get(index).isEnabled(); 1211 } 1212 1213 /** 1214 * Returns the component at <code>index</code>. 1215 * 1216 * @param index the index of the item being queried 1217 * @return the <code>Component</code> at <code>index</code> 1218 * @exception IndexOutOfBoundsException if index is out of range 1219 * (index < 0 || index >= tab count) 1220 * 1221 * @see #setComponentAt 1222 */ 1223 public Component getComponentAt(int index) { 1224 return pages.get(index).component; 1225 } 1226 1227 /** 1228 * Returns the keyboard mnemonic for accessing the specified tab. 1229 * The mnemonic is the key which when combined with the look and feel's 1230 * mouseless modifier (usually Alt) will activate the specified 1231 * tab. 1232 * 1233 * @since 1.4 1234 * @param tabIndex the index of the tab that the mnemonic refers to 1235 * @return the key code which represents the mnemonic; 1236 * -1 if a mnemonic is not specified for the tab 1237 * @exception IndexOutOfBoundsException if index is out of range 1238 * (<code>tabIndex</code> < 0 || 1239 * <code>tabIndex</code> >= tab count) 1240 * @see #setDisplayedMnemonicIndexAt(int,int) 1241 * @see #setMnemonicAt(int,int) 1242 */ 1243 public int getMnemonicAt(int tabIndex) { 1244 checkIndex(tabIndex); 1245 1246 Page page = pages.get(tabIndex); 1247 return page.getMnemonic(); 1248 } 1249 1250 /** 1251 * Returns the character, as an index, that the look and feel should 1252 * provide decoration for as representing the mnemonic character. 1253 * 1254 * @since 1.4 1255 * @param tabIndex the index of the tab that the mnemonic refers to 1256 * @return index representing mnemonic character if one exists; 1257 * otherwise returns -1 1258 * @exception IndexOutOfBoundsException if index is out of range 1259 * (<code>tabIndex</code> < 0 || 1260 * <code>tabIndex</code> >= tab count) 1261 * @see #setDisplayedMnemonicIndexAt(int,int) 1262 * @see #setMnemonicAt(int,int) 1263 */ 1264 public int getDisplayedMnemonicIndexAt(int tabIndex) { 1265 checkIndex(tabIndex); 1266 1267 Page page = pages.get(tabIndex); 1268 return page.getDisplayedMnemonicIndex(); 1269 } 1270 1271 /** 1272 * Returns the tab bounds at <code>index</code>. If the tab at 1273 * this index is not currently visible in the UI, then returns 1274 * <code>null</code>. 1275 * If there is no UI set on this <code>tabbedpane</code>, 1276 * then returns <code>null</code>. 1277 * 1278 * @param index the index to be queried 1279 * @return a <code>Rectangle</code> containing the tab bounds at 1280 * <code>index</code>, or <code>null</code> if tab at 1281 * <code>index</code> is not currently visible in the UI, 1282 * or if there is no UI set on this <code>tabbedpane</code> 1283 * @exception IndexOutOfBoundsException if index is out of range 1284 * (index < 0 || index >= tab count) 1285 */ 1286 public Rectangle getBoundsAt(int index) { 1287 checkIndex(index); 1288 if (ui != null) { 1289 return ((TabbedPaneUI)ui).getTabBounds(this, index); 1290 } 1291 return null; 1292 } 1293 1294 1295 // Setters for the Pages 1296 1297 /** 1298 * Sets the title at <code>index</code> to <code>title</code> which 1299 * can be <code>null</code>. 1300 * The title is not shown if a tab component for this tab was specified. 1301 * An internal exception is raised if there is no tab at that index. 1302 * 1303 * @param index the tab index where the title should be set 1304 * @param title the title to be displayed in the tab 1305 * @exception IndexOutOfBoundsException if index is out of range 1306 * (index < 0 || index >= tab count) 1307 * 1308 * @see #getTitleAt 1309 * @see #setTabComponentAt 1310 * @beaninfo 1311 * preferred: true 1312 * attribute: visualUpdate true 1313 * description: The title at the specified tab index. 1314 */ 1315 public void setTitleAt(int index, String title) { 1316 Page page = pages.get(index); 1317 String oldTitle =page.title; 1318 page.title = title; 1319 1320 if (oldTitle != title) { 1321 firePropertyChange("indexForTitle", -1, index); 1322 } 1323 page.updateDisplayedMnemonicIndex(); 1324 if ((oldTitle != title) && (accessibleContext != null)) { 1325 accessibleContext.firePropertyChange( 1326 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1327 oldTitle, title); 1328 } 1329 if (title == null || oldTitle == null || 1330 !title.equals(oldTitle)) { 1331 revalidate(); 1332 repaint(); 1333 } 1334 } 1335 1336 /** 1337 * Sets the icon at <code>index</code> to <code>icon</code> which can be 1338 * <code>null</code>. This does not set disabled icon at <code>icon</code>. 1339 * If the new Icon is different than the current Icon and disabled icon 1340 * is not explicitly set, the LookAndFeel will be asked to generate a disabled 1341 * Icon. To explicitly set disabled icon, use <code>setDisableIconAt()</code>. 1342 * The icon is not shown if a tab component for this tab was specified. 1343 * An internal exception is raised if there is no tab at that index. 1344 * 1345 * @param index the tab index where the icon should be set 1346 * @param icon the icon to be displayed in the tab 1347 * @exception IndexOutOfBoundsException if index is out of range 1348 * (index < 0 || index >= tab count) 1349 * 1350 * @see #setDisabledIconAt 1351 * @see #getIconAt 1352 * @see #getDisabledIconAt 1353 * @see #setTabComponentAt 1354 * @beaninfo 1355 * preferred: true 1356 * attribute: visualUpdate true 1357 * description: The icon at the specified tab index. 1358 */ 1359 public void setIconAt(int index, Icon icon) { 1360 Page page = pages.get(index); 1361 Icon oldIcon = page.icon; 1362 if (icon != oldIcon) { 1363 page.icon = icon; 1364 1365 /* If the default icon has really changed and we had 1366 * generated the disabled icon for this page, then 1367 * clear the disabledIcon field of the page. 1368 */ 1369 if (page.disabledIcon instanceof UIResource) { 1370 page.disabledIcon = null; 1371 } 1372 1373 // Fire the accessibility Visible data change 1374 if (accessibleContext != null) { 1375 accessibleContext.firePropertyChange( 1376 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1377 oldIcon, icon); 1378 } 1379 revalidate(); 1380 repaint(); 1381 } 1382 } 1383 1384 /** 1385 * Sets the disabled icon at <code>index</code> to <code>icon</code> 1386 * which can be <code>null</code>. 1387 * An internal exception is raised if there is no tab at that index. 1388 * 1389 * @param index the tab index where the disabled icon should be set 1390 * @param disabledIcon the icon to be displayed in the tab when disabled 1391 * @exception IndexOutOfBoundsException if index is out of range 1392 * (index < 0 || index >= tab count) 1393 * 1394 * @see #getDisabledIconAt 1395 * @beaninfo 1396 * preferred: true 1397 * attribute: visualUpdate true 1398 * description: The disabled icon at the specified tab index. 1399 */ 1400 public void setDisabledIconAt(int index, Icon disabledIcon) { 1401 Icon oldIcon = pages.get(index).disabledIcon; 1402 pages.get(index).disabledIcon = disabledIcon; 1403 if (disabledIcon != oldIcon && !isEnabledAt(index)) { 1404 revalidate(); 1405 repaint(); 1406 } 1407 } 1408 1409 /** 1410 * Sets the tooltip text at <code>index</code> to <code>toolTipText</code> 1411 * which can be <code>null</code>. 1412 * An internal exception is raised if there is no tab at that index. 1413 * 1414 * @param index the tab index where the tooltip text should be set 1415 * @param toolTipText the tooltip text to be displayed for the tab 1416 * @exception IndexOutOfBoundsException if index is out of range 1417 * (index < 0 || index >= tab count) 1418 * 1419 * @see #getToolTipTextAt 1420 * @beaninfo 1421 * preferred: true 1422 * description: The tooltip text at the specified tab index. 1423 * @since 1.3 1424 */ 1425 public void setToolTipTextAt(int index, String toolTipText) { 1426 String oldToolTipText = pages.get(index).tip; 1427 pages.get(index).tip = toolTipText; 1428 1429 if ((oldToolTipText != toolTipText) && (accessibleContext != null)) { 1430 accessibleContext.firePropertyChange( 1431 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1432 oldToolTipText, toolTipText); 1433 } 1434 if (!haveRegistered && toolTipText != null) { 1435 ToolTipManager.sharedInstance().registerComponent(this); 1436 haveRegistered = true; 1437 } 1438 } 1439 1440 /** 1441 * Sets the background color at <code>index</code> to 1442 * <code>background</code> 1443 * which can be <code>null</code>, in which case the tab's background color 1444 * will default to the background color of the <code>tabbedpane</code>. 1445 * An internal exception is raised if there is no tab at that index. 1446 * <p/> 1447 * It is up to the look and feel to honor this property, some may 1448 * choose to ignore it. 1449 * 1450 * @param index the tab index where the background should be set 1451 * @param background the color to be displayed in the tab's background 1452 * @exception IndexOutOfBoundsException if index is out of range 1453 * (index < 0 || index >= tab count) 1454 * 1455 * @see #getBackgroundAt 1456 * @beaninfo 1457 * preferred: true 1458 * attribute: visualUpdate true 1459 * description: The background color at the specified tab index. 1460 */ 1461 public void setBackgroundAt(int index, Color background) { 1462 Color oldBg = pages.get(index).background; 1463 pages.get(index).setBackground(background); 1464 if (background == null || oldBg == null || 1465 !background.equals(oldBg)) { 1466 Rectangle tabBounds = getBoundsAt(index); 1467 if (tabBounds != null) { 1468 repaint(tabBounds); 1469 } 1470 } 1471 } 1472 1473 /** 1474 * Sets the foreground color at <code>index</code> to 1475 * <code>foreground</code> which can be 1476 * <code>null</code>, in which case the tab's foreground color 1477 * will default to the foreground color of this <code>tabbedpane</code>. 1478 * An internal exception is raised if there is no tab at that index. 1479 * <p/> 1480 * It is up to the look and feel to honor this property, some may 1481 * choose to ignore it. 1482 * 1483 * @param index the tab index where the foreground should be set 1484 * @param foreground the color to be displayed as the tab's foreground 1485 * @exception IndexOutOfBoundsException if index is out of range 1486 * (index < 0 || index >= tab count) 1487 * 1488 * @see #getForegroundAt 1489 * @beaninfo 1490 * preferred: true 1491 * attribute: visualUpdate true 1492 * description: The foreground color at the specified tab index. 1493 */ 1494 public void setForegroundAt(int index, Color foreground) { 1495 Color oldFg = pages.get(index).foreground; 1496 pages.get(index).setForeground(foreground); 1497 if (foreground == null || oldFg == null || 1498 !foreground.equals(oldFg)) { 1499 Rectangle tabBounds = getBoundsAt(index); 1500 if (tabBounds != null) { 1501 repaint(tabBounds); 1502 } 1503 } 1504 } 1505 1506 /** 1507 * Sets whether or not the tab at <code>index</code> is enabled. 1508 * An internal exception is raised if there is no tab at that index. 1509 * 1510 * @param index the tab index which should be enabled/disabled 1511 * @param enabled whether or not the tab should be enabled 1512 * @exception IndexOutOfBoundsException if index is out of range 1513 * (index < 0 || index >= tab count) 1514 * 1515 * @see #isEnabledAt 1516 */ 1517 public void setEnabledAt(int index, boolean enabled) { 1518 boolean oldEnabled = pages.get(index).isEnabled(); 1519 pages.get(index).setEnabled(enabled); 1520 if (enabled != oldEnabled) { 1521 revalidate(); 1522 repaint(); 1523 } 1524 } 1525 1526 /** 1527 * Sets the component at <code>index</code> to <code>component</code>. 1528 * An internal exception is raised if there is no tab at that index. 1529 * 1530 * @param index the tab index where this component is being placed 1531 * @param component the component for the tab 1532 * @exception IndexOutOfBoundsException if index is out of range 1533 * (index < 0 || index >= tab count) 1534 * 1535 * @see #getComponentAt 1536 * @beaninfo 1537 * attribute: visualUpdate true 1538 * description: The component at the specified tab index. 1539 */ 1540 public void setComponentAt(int index, Component component) { 1541 Page page = pages.get(index); 1542 if (component != page.component) { 1543 boolean shouldChangeFocus = false; 1544 1545 if (page.component != null) { 1546 shouldChangeFocus = 1547 (SwingUtilities.findFocusOwner(page.component) != null); 1548 1549 // REMIND(aim): this is really silly; 1550 // why not if (page.component.getParent() == this) remove(component) 1551 synchronized(getTreeLock()) { 1552 int count = getComponentCount(); 1553 Component children[] = getComponents(); 1554 for (int i = 0; i < count; i++) { 1555 if (children[i] == page.component) { 1556 super.remove(i); 1557 } 1558 } 1559 } 1560 } 1561 1562 page.component = component; 1563 boolean selectedPage = (getSelectedIndex() == index); 1564 1565 if (selectedPage) { 1566 this.visComp = component; 1567 } 1568 1569 if (component != null) { 1570 component.setVisible(selectedPage); 1571 addImpl(component, null, -1); 1572 1573 if (shouldChangeFocus) { 1574 SwingUtilities2.tabbedPaneChangeFocusTo(component); 1575 } 1576 } else { 1577 repaint(); 1578 } 1579 1580 revalidate(); 1581 } 1582 } 1583 1584 /** 1585 * Provides a hint to the look and feel as to which character in the 1586 * text should be decorated to represent the mnemonic. Not all look and 1587 * feels may support this. A value of -1 indicates either there is 1588 * no mnemonic for this tab, or you do not wish the mnemonic to be 1589 * displayed for this tab. 1590 * <p> 1591 * The value of this is updated as the properties relating to the 1592 * mnemonic change (such as the mnemonic itself, the text...). 1593 * You should only ever have to call this if 1594 * you do not wish the default character to be underlined. For example, if 1595 * the text at tab index 3 was 'Apple Price', with a mnemonic of 'p', 1596 * and you wanted the 'P' 1597 * to be decorated, as 'Apple <u>P</u>rice', you would have to invoke 1598 * <code>setDisplayedMnemonicIndex(3, 6)</code> after invoking 1599 * <code>setMnemonicAt(3, KeyEvent.VK_P)</code>. 1600 * <p>Note that it is the programmer's responsibility to ensure 1601 * that each tab has a unique mnemonic or unpredictable results may 1602 * occur. 1603 * 1604 * @since 1.4 1605 * @param tabIndex the index of the tab that the mnemonic refers to 1606 * @param mnemonicIndex index into the <code>String</code> to underline 1607 * @exception IndexOutOfBoundsException if <code>tabIndex</code> is 1608 * out of range (<code>tabIndex < 0 || tabIndex >= tab 1609 * count</code>) 1610 * @exception IllegalArgumentException will be thrown if 1611 * <code>mnemonicIndex</code> is >= length of the tab 1612 * title , or < -1 1613 * @see #setMnemonicAt(int,int) 1614 * @see #getDisplayedMnemonicIndexAt(int) 1615 * 1616 * @beaninfo 1617 * bound: true 1618 * attribute: visualUpdate true 1619 * description: the index into the String to draw the keyboard character 1620 * mnemonic at 1621 */ 1622 public void setDisplayedMnemonicIndexAt(int tabIndex, int mnemonicIndex) { 1623 checkIndex(tabIndex); 1624 1625 Page page = pages.get(tabIndex); 1626 1627 page.setDisplayedMnemonicIndex(mnemonicIndex); 1628 } 1629 1630 /** 1631 * Sets the keyboard mnemonic for accessing the specified tab. 1632 * The mnemonic is the key which when combined with the look and feel's 1633 * mouseless modifier (usually Alt) will activate the specified 1634 * tab. 1635 * <p> 1636 * A mnemonic must correspond to a single key on the keyboard 1637 * and should be specified using one of the <code>VK_XXX</code> 1638 * keycodes defined in <code>java.awt.event.KeyEvent</code> 1639 * or one of the extended keycodes obtained through 1640 * <code>java.awt.event.KeyEvent.getExtendedKeyCodeForChar</code>. 1641 * Mnemonics are case-insensitive, therefore a key event 1642 * with the corresponding keycode would cause the button to be 1643 * activated whether or not the Shift modifier was pressed. 1644 * <p> 1645 * This will update the displayed mnemonic property for the specified 1646 * tab. 1647 * 1648 * @since 1.4 1649 * @param tabIndex the index of the tab that the mnemonic refers to 1650 * @param mnemonic the key code which represents the mnemonic 1651 * @exception IndexOutOfBoundsException if <code>tabIndex</code> is out 1652 * of range (<code>tabIndex < 0 || tabIndex >= tab count</code>) 1653 * @see #getMnemonicAt(int) 1654 * @see #setDisplayedMnemonicIndexAt(int,int) 1655 * 1656 * @beaninfo 1657 * bound: true 1658 * attribute: visualUpdate true 1659 * description: The keyboard mnenmonic, as a KeyEvent VK constant, 1660 * for the specified tab 1661 */ 1662 public void setMnemonicAt(int tabIndex, int mnemonic) { 1663 checkIndex(tabIndex); 1664 1665 Page page = pages.get(tabIndex); 1666 page.setMnemonic(mnemonic); 1667 1668 firePropertyChange("mnemonicAt", null, null); 1669 } 1670 1671 // end of Page setters 1672 1673 /** 1674 * Returns the first tab index with a given <code>title</code>, or 1675 * -1 if no tab has this title. 1676 * 1677 * @param title the title for the tab 1678 * @return the first tab index which matches <code>title</code>, or 1679 * -1 if no tab has this title 1680 */ 1681 public int indexOfTab(String title) { 1682 for(int i = 0; i < getTabCount(); i++) { 1683 if (getTitleAt(i).equals(title == null? "" : title)) { 1684 return i; 1685 } 1686 } 1687 return -1; 1688 } 1689 1690 /** 1691 * Returns the first tab index with a given <code>icon</code>, 1692 * or -1 if no tab has this icon. 1693 * 1694 * @param icon the icon for the tab 1695 * @return the first tab index which matches <code>icon</code>, 1696 * or -1 if no tab has this icon 1697 */ 1698 public int indexOfTab(Icon icon) { 1699 for(int i = 0; i < getTabCount(); i++) { 1700 Icon tabIcon = getIconAt(i); 1701 if ((tabIcon != null && tabIcon.equals(icon)) || 1702 (tabIcon == null && tabIcon == icon)) { 1703 return i; 1704 } 1705 } 1706 return -1; 1707 } 1708 1709 /** 1710 * Returns the index of the tab for the specified component. 1711 * Returns -1 if there is no tab for this component. 1712 * 1713 * @param component the component for the tab 1714 * @return the first tab which matches this component, or -1 1715 * if there is no tab for this component 1716 */ 1717 public int indexOfComponent(Component component) { 1718 for(int i = 0; i < getTabCount(); i++) { 1719 Component c = getComponentAt(i); 1720 if ((c != null && c.equals(component)) || 1721 (c == null && c == component)) { 1722 return i; 1723 } 1724 } 1725 return -1; 1726 } 1727 1728 /** 1729 * Returns the tab index corresponding to the tab whose bounds 1730 * intersect the specified location. Returns -1 if no tab 1731 * intersects the location. 1732 * 1733 * @param x the x location relative to this tabbedpane 1734 * @param y the y location relative to this tabbedpane 1735 * @return the tab index which intersects the location, or 1736 * -1 if no tab intersects the location 1737 * @since 1.4 1738 */ 1739 public int indexAtLocation(int x, int y) { 1740 if (ui != null) { 1741 return ((TabbedPaneUI)ui).tabForCoordinate(this, x, y); 1742 } 1743 return -1; 1744 } 1745 1746 1747 /** 1748 * Returns the tooltip text for the component determined by the 1749 * mouse event location. 1750 * 1751 * @param event the <code>MouseEvent</code> that tells where the 1752 * cursor is lingering 1753 * @return the <code>String</code> containing the tooltip text 1754 */ 1755 public String getToolTipText(MouseEvent event) { 1756 if (ui != null) { 1757 int index = ((TabbedPaneUI)ui).tabForCoordinate(this, event.getX(), event.getY()); 1758 1759 if (index != -1) { 1760 return pages.get(index).tip; 1761 } 1762 } 1763 return super.getToolTipText(event); 1764 } 1765 1766 private void checkIndex(int index) { 1767 if (index < 0 || index >= pages.size()) { 1768 throw new IndexOutOfBoundsException("Index: "+index+", Tab count: "+pages.size()); 1769 } 1770 } 1771 1772 1773 /** 1774 * See <code>readObject</code> and <code>writeObject</code> in 1775 * <code>JComponent</code> for more 1776 * information about serialization in Swing. 1777 */ 1778 private void writeObject(ObjectOutputStream s) throws IOException { 1779 s.defaultWriteObject(); 1780 if (getUIClassID().equals(uiClassID)) { 1781 byte count = JComponent.getWriteObjCounter(this); 1782 JComponent.setWriteObjCounter(this, --count); 1783 if (count == 0 && ui != null) { 1784 ui.installUI(this); 1785 } 1786 } 1787 } 1788 1789 /* Called from the <code>JComponent</code>'s 1790 * <code>EnableSerializationFocusListener</code> to 1791 * do any Swing-specific pre-serialization configuration. 1792 */ 1793 void compWriteObjectNotify() { 1794 super.compWriteObjectNotify(); 1795 // If ToolTipText != null, then the tooltip has already been 1796 // unregistered by JComponent.compWriteObjectNotify() 1797 if (getToolTipText() == null && haveRegistered) { 1798 ToolTipManager.sharedInstance().unregisterComponent(this); 1799 } 1800 } 1801 1802 /** 1803 * See <code>readObject</code> and <code>writeObject</code> in 1804 * <code>JComponent</code> for more 1805 * information about serialization in Swing. 1806 */ 1807 private void readObject(ObjectInputStream s) 1808 throws IOException, ClassNotFoundException 1809 { 1810 s.defaultReadObject(); 1811 if ((ui != null) && (getUIClassID().equals(uiClassID))) { 1812 ui.installUI(this); 1813 } 1814 // If ToolTipText != null, then the tooltip has already been 1815 // registered by JComponent.readObject() 1816 if (getToolTipText() == null && haveRegistered) { 1817 ToolTipManager.sharedInstance().registerComponent(this); 1818 } 1819 } 1820 1821 1822 /** 1823 * Returns a string representation of this <code>JTabbedPane</code>. 1824 * This method 1825 * is intended to be used only for debugging purposes, and the 1826 * content and format of the returned string may vary between 1827 * implementations. The returned string may be empty but may not 1828 * be <code>null</code>. 1829 * 1830 * @return a string representation of this JTabbedPane. 1831 */ 1832 protected String paramString() { 1833 String tabPlacementString; 1834 if (tabPlacement == TOP) { 1835 tabPlacementString = "TOP"; 1836 } else if (tabPlacement == BOTTOM) { 1837 tabPlacementString = "BOTTOM"; 1838 } else if (tabPlacement == LEFT) { 1839 tabPlacementString = "LEFT"; 1840 } else if (tabPlacement == RIGHT) { 1841 tabPlacementString = "RIGHT"; 1842 } else tabPlacementString = ""; 1843 String haveRegisteredString = (haveRegistered ? 1844 "true" : "false"); 1845 1846 return super.paramString() + 1847 ",haveRegistered=" + haveRegisteredString + 1848 ",tabPlacement=" + tabPlacementString; 1849 } 1850 1851 ///////////////// 1852 // Accessibility support 1853 //////////////// 1854 1855 /** 1856 * Gets the AccessibleContext associated with this JTabbedPane. 1857 * For tabbed panes, the AccessibleContext takes the form of an 1858 * AccessibleJTabbedPane. 1859 * A new AccessibleJTabbedPane instance is created if necessary. 1860 * 1861 * @return an AccessibleJTabbedPane that serves as the 1862 * AccessibleContext of this JTabbedPane 1863 */ 1864 public AccessibleContext getAccessibleContext() { 1865 if (accessibleContext == null) { 1866 accessibleContext = new AccessibleJTabbedPane(); 1867 1868 // initialize AccessibleContext for the existing pages 1869 int count = getTabCount(); 1870 for (int i = 0; i < count; i++) { 1871 pages.get(i).initAccessibleContext(); 1872 } 1873 } 1874 return accessibleContext; 1875 } 1876 1877 /** 1878 * This class implements accessibility support for the 1879 * <code>JTabbedPane</code> class. It provides an implementation of the 1880 * Java Accessibility API appropriate to tabbed pane user-interface 1881 * elements. 1882 * <p> 1883 * <strong>Warning:</strong> 1884 * Serialized objects of this class will not be compatible with 1885 * future Swing releases. The current serialization support is 1886 * appropriate for short term storage or RMI between applications running 1887 * the same version of Swing. As of 1.4, support for long term storage 1888 * of all JavaBeans<sup><font size="-2">TM</font></sup> 1889 * has been added to the <code>java.beans</code> package. 1890 * Please see {@link java.beans.XMLEncoder}. 1891 */ 1892 protected class AccessibleJTabbedPane extends AccessibleJComponent 1893 implements AccessibleSelection, ChangeListener { 1894 1895 /** 1896 * Returns the accessible name of this object, or {@code null} if 1897 * there is no accessible name. 1898 * 1899 * @return the accessible name of this object, nor {@code null}. 1900 * @since 1.6 1901 */ 1902 public String getAccessibleName() { 1903 if (accessibleName != null) { 1904 return accessibleName; 1905 } 1906 1907 String cp = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY); 1908 1909 if (cp != null) { 1910 return cp; 1911 } 1912 1913 int index = getSelectedIndex(); 1914 1915 if (index >= 0) { 1916 return pages.get(index).getAccessibleName(); 1917 } 1918 1919 return super.getAccessibleName(); 1920 } 1921 1922 /** 1923 * Constructs an AccessibleJTabbedPane 1924 */ 1925 public AccessibleJTabbedPane() { 1926 super(); 1927 JTabbedPane.this.model.addChangeListener(this); 1928 } 1929 1930 public void stateChanged(ChangeEvent e) { 1931 Object o = e.getSource(); 1932 firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY, 1933 null, o); 1934 } 1935 1936 /** 1937 * Get the role of this object. 1938 * 1939 * @return an instance of AccessibleRole describing the role of 1940 * the object 1941 */ 1942 public AccessibleRole getAccessibleRole() { 1943 return AccessibleRole.PAGE_TAB_LIST; 1944 } 1945 1946 /** 1947 * Returns the number of accessible children in the object. 1948 * 1949 * @return the number of accessible children in the object. 1950 */ 1951 public int getAccessibleChildrenCount() { 1952 return getTabCount(); 1953 } 1954 1955 /** 1956 * Return the specified Accessible child of the object. 1957 * 1958 * @param i zero-based index of child 1959 * @return the Accessible child of the object 1960 * @exception IllegalArgumentException if index is out of bounds 1961 */ 1962 public Accessible getAccessibleChild(int i) { 1963 if (i < 0 || i >= getTabCount()) { 1964 return null; 1965 } 1966 return pages.get(i); 1967 } 1968 1969 /** 1970 * Gets the <code>AccessibleSelection</code> associated with 1971 * this object. In the implementation of the Java 1972 * Accessibility API for this class, 1973 * returns this object, which is responsible for implementing the 1974 * <code>AccessibleSelection</code> interface on behalf of itself. 1975 * 1976 * @return this object 1977 */ 1978 public AccessibleSelection getAccessibleSelection() { 1979 return this; 1980 } 1981 1982 /** 1983 * Returns the <code>Accessible</code> child contained at 1984 * the local coordinate <code>Point</code>, if one exists. 1985 * Otherwise returns the currently selected tab. 1986 * 1987 * @return the <code>Accessible</code> at the specified 1988 * location, if it exists 1989 */ 1990 public Accessible getAccessibleAt(Point p) { 1991 int tab = ((TabbedPaneUI) ui).tabForCoordinate(JTabbedPane.this, 1992 p.x, p.y); 1993 if (tab == -1) { 1994 tab = getSelectedIndex(); 1995 } 1996 return getAccessibleChild(tab); 1997 } 1998 1999 public int getAccessibleSelectionCount() { 2000 return 1; 2001 } 2002 2003 public Accessible getAccessibleSelection(int i) { 2004 int index = getSelectedIndex(); 2005 if (index == -1) { 2006 return null; 2007 } 2008 return pages.get(index); 2009 } 2010 2011 public boolean isAccessibleChildSelected(int i) { 2012 return (i == getSelectedIndex()); 2013 } 2014 2015 public void addAccessibleSelection(int i) { 2016 setSelectedIndex(i); 2017 } 2018 2019 public void removeAccessibleSelection(int i) { 2020 // can't do 2021 } 2022 2023 public void clearAccessibleSelection() { 2024 // can't do 2025 } 2026 2027 public void selectAllAccessibleSelection() { 2028 // can't do 2029 } 2030 } 2031 2032 private class Page extends AccessibleContext 2033 implements Serializable, Accessible, AccessibleComponent { 2034 String title; 2035 Color background; 2036 Color foreground; 2037 Icon icon; 2038 Icon disabledIcon; 2039 JTabbedPane parent; 2040 Component component; 2041 String tip; 2042 boolean enabled = true; 2043 boolean needsUIUpdate; 2044 int mnemonic = -1; 2045 int mnemonicIndex = -1; 2046 Component tabComponent; 2047 2048 Page(JTabbedPane parent, 2049 String title, Icon icon, Icon disabledIcon, Component component, String tip) { 2050 this.title = title; 2051 this.icon = icon; 2052 this.disabledIcon = disabledIcon; 2053 this.parent = parent; 2054 this.setAccessibleParent(parent); 2055 this.component = component; 2056 this.tip = tip; 2057 2058 initAccessibleContext(); 2059 } 2060 2061 /* 2062 * initializes the AccessibleContext for the page 2063 */ 2064 void initAccessibleContext() { 2065 if (JTabbedPane.this.accessibleContext != null && 2066 component instanceof Accessible) { 2067 /* 2068 * Do initialization if the AccessibleJTabbedPane 2069 * has been instantiated. We do not want to load 2070 * Accessibility classes unnecessarily. 2071 */ 2072 AccessibleContext ac; 2073 ac = component.getAccessibleContext(); 2074 if (ac != null) { 2075 ac.setAccessibleParent(this); 2076 } 2077 } 2078 } 2079 2080 void setMnemonic(int mnemonic) { 2081 this.mnemonic = mnemonic; 2082 updateDisplayedMnemonicIndex(); 2083 } 2084 2085 int getMnemonic() { 2086 return mnemonic; 2087 } 2088 2089 /* 2090 * Sets the page displayed mnemonic index 2091 */ 2092 void setDisplayedMnemonicIndex(int mnemonicIndex) { 2093 if (this.mnemonicIndex != mnemonicIndex) { 2094 if (mnemonicIndex != -1 && (title == null || 2095 mnemonicIndex < 0 || 2096 mnemonicIndex >= title.length())) { 2097 throw new IllegalArgumentException( 2098 "Invalid mnemonic index: " + mnemonicIndex); 2099 } 2100 this.mnemonicIndex = mnemonicIndex; 2101 JTabbedPane.this.firePropertyChange("displayedMnemonicIndexAt", 2102 null, null); 2103 } 2104 } 2105 2106 /* 2107 * Returns the page displayed mnemonic index 2108 */ 2109 int getDisplayedMnemonicIndex() { 2110 return this.mnemonicIndex; 2111 } 2112 2113 void updateDisplayedMnemonicIndex() { 2114 setDisplayedMnemonicIndex( 2115 SwingUtilities.findDisplayedMnemonicIndex(title, mnemonic)); 2116 } 2117 2118 ///////////////// 2119 // Accessibility support 2120 //////////////// 2121 2122 public AccessibleContext getAccessibleContext() { 2123 return this; 2124 } 2125 2126 2127 // AccessibleContext methods 2128 2129 public String getAccessibleName() { 2130 if (accessibleName != null) { 2131 return accessibleName; 2132 } else if (title != null) { 2133 return title; 2134 } 2135 return null; 2136 } 2137 2138 public String getAccessibleDescription() { 2139 if (accessibleDescription != null) { 2140 return accessibleDescription; 2141 } else if (tip != null) { 2142 return tip; 2143 } 2144 return null; 2145 } 2146 2147 public AccessibleRole getAccessibleRole() { 2148 return AccessibleRole.PAGE_TAB; 2149 } 2150 2151 public AccessibleStateSet getAccessibleStateSet() { 2152 AccessibleStateSet states; 2153 states = parent.getAccessibleContext().getAccessibleStateSet(); 2154 states.add(AccessibleState.SELECTABLE); 2155 int i = parent.indexOfTab(title); 2156 if (i == parent.getSelectedIndex()) { 2157 states.add(AccessibleState.SELECTED); 2158 } 2159 return states; 2160 } 2161 2162 public int getAccessibleIndexInParent() { 2163 return parent.indexOfTab(title); 2164 } 2165 2166 public int getAccessibleChildrenCount() { 2167 if (component instanceof Accessible) { 2168 return 1; 2169 } else { 2170 return 0; 2171 } 2172 } 2173 2174 public Accessible getAccessibleChild(int i) { 2175 if (component instanceof Accessible) { 2176 return (Accessible) component; 2177 } else { 2178 return null; 2179 } 2180 } 2181 2182 public Locale getLocale() { 2183 return parent.getLocale(); 2184 } 2185 2186 public AccessibleComponent getAccessibleComponent() { 2187 return this; 2188 } 2189 2190 2191 // AccessibleComponent methods 2192 2193 public Color getBackground() { 2194 return background != null? background : parent.getBackground(); 2195 } 2196 2197 public void setBackground(Color c) { 2198 background = c; 2199 } 2200 2201 public Color getForeground() { 2202 return foreground != null? foreground : parent.getForeground(); 2203 } 2204 2205 public void setForeground(Color c) { 2206 foreground = c; 2207 } 2208 2209 public Cursor getCursor() { 2210 return parent.getCursor(); 2211 } 2212 2213 public void setCursor(Cursor c) { 2214 parent.setCursor(c); 2215 } 2216 2217 public Font getFont() { 2218 return parent.getFont(); 2219 } 2220 2221 public void setFont(Font f) { 2222 parent.setFont(f); 2223 } 2224 2225 public FontMetrics getFontMetrics(Font f) { 2226 return parent.getFontMetrics(f); 2227 } 2228 2229 public boolean isEnabled() { 2230 return enabled; 2231 } 2232 2233 public void setEnabled(boolean b) { 2234 enabled = b; 2235 } 2236 2237 public boolean isVisible() { 2238 return parent.isVisible(); 2239 } 2240 2241 public void setVisible(boolean b) { 2242 parent.setVisible(b); 2243 } 2244 2245 public boolean isShowing() { 2246 return parent.isShowing(); 2247 } 2248 2249 public boolean contains(Point p) { 2250 Rectangle r = getBounds(); 2251 return r.contains(p); 2252 } 2253 2254 public Point getLocationOnScreen() { 2255 Point parentLocation = parent.getLocationOnScreen(); 2256 Point componentLocation = getLocation(); 2257 componentLocation.translate(parentLocation.x, parentLocation.y); 2258 return componentLocation; 2259 } 2260 2261 public Point getLocation() { 2262 Rectangle r = getBounds(); 2263 return new Point(r.x, r.y); 2264 } 2265 2266 public void setLocation(Point p) { 2267 // do nothing 2268 } 2269 2270 public Rectangle getBounds() { 2271 return parent.getUI().getTabBounds(parent, 2272 parent.indexOfTab(title)); 2273 } 2274 2275 public void setBounds(Rectangle r) { 2276 // do nothing 2277 } 2278 2279 public Dimension getSize() { 2280 Rectangle r = getBounds(); 2281 return new Dimension(r.width, r.height); 2282 } 2283 2284 public void setSize(Dimension d) { 2285 // do nothing 2286 } 2287 2288 public Accessible getAccessibleAt(Point p) { 2289 if (component instanceof Accessible) { 2290 return (Accessible) component; 2291 } else { 2292 return null; 2293 } 2294 } 2295 2296 public boolean isFocusTraversable() { 2297 return false; 2298 } 2299 2300 public void requestFocus() { 2301 // do nothing 2302 } 2303 2304 public void addFocusListener(FocusListener l) { 2305 // do nothing 2306 } 2307 2308 public void removeFocusListener(FocusListener l) { 2309 // do nothing 2310 } 2311 2312 // TIGER - 4732339 2313 /** 2314 * Returns an AccessibleIcon 2315 * 2316 * @return the enabled icon if one exists and the page 2317 * is enabled. Otherwise, returns the disabled icon if 2318 * one exists and the page is disabled. Otherwise, null 2319 * is returned. 2320 */ 2321 public AccessibleIcon [] getAccessibleIcon() { 2322 AccessibleIcon accessibleIcon = null; 2323 if (enabled && icon instanceof ImageIcon) { 2324 AccessibleContext ac = 2325 ((ImageIcon)icon).getAccessibleContext(); 2326 accessibleIcon = (AccessibleIcon)ac; 2327 } else if (!enabled && disabledIcon instanceof ImageIcon) { 2328 AccessibleContext ac = 2329 ((ImageIcon)disabledIcon).getAccessibleContext(); 2330 accessibleIcon = (AccessibleIcon)ac; 2331 } 2332 if (accessibleIcon != null) { 2333 AccessibleIcon [] returnIcons = new AccessibleIcon[1]; 2334 returnIcons[0] = accessibleIcon; 2335 return returnIcons; 2336 } else { 2337 return null; 2338 } 2339 } 2340 } 2341 2342 /** 2343 * Sets the component that is responsible for rendering the 2344 * title for the specified tab. A null value means 2345 * <code>JTabbedPane</code> will render the title and/or icon for 2346 * the specified tab. A non-null value means the component will 2347 * render the title and <code>JTabbedPane</code> will not render 2348 * the title and/or icon. 2349 * <p> 2350 * Note: The component must not be one that the developer has 2351 * already added to the tabbed pane. 2352 * 2353 * @param index the tab index where the component should be set 2354 * @param component the component to render the title for the 2355 * specified tab 2356 * @exception IndexOutOfBoundsException if index is out of range 2357 * (index < 0 || index >= tab count) 2358 * @exception IllegalArgumentException if component has already been 2359 * added to this <code>JTabbedPane</code> 2360 * 2361 * @see #getTabComponentAt 2362 * @beaninfo 2363 * preferred: true 2364 * attribute: visualUpdate true 2365 * description: The tab component at the specified tab index. 2366 * @since 1.6 2367 */ 2368 public void setTabComponentAt(int index, Component component) { 2369 if (component != null && indexOfComponent(component) != -1) { 2370 throw new IllegalArgumentException("Component is already added to this JTabbedPane"); 2371 } 2372 Component oldValue = getTabComponentAt(index); 2373 if (component != oldValue) { 2374 int tabComponentIndex = indexOfTabComponent(component); 2375 if (tabComponentIndex != -1) { 2376 setTabComponentAt(tabComponentIndex, null); 2377 } 2378 pages.get(index).tabComponent = component; 2379 firePropertyChange("indexForTabComponent", -1, index); 2380 } 2381 } 2382 2383 /** 2384 * Returns the tab component at <code>index</code>. 2385 * 2386 * @param index the index of the item being queried 2387 * @return the tab component at <code>index</code> 2388 * @exception IndexOutOfBoundsException if index is out of range 2389 * (index < 0 || index >= tab count) 2390 * 2391 * @see #setTabComponentAt 2392 * @since 1.6 2393 */ 2394 public Component getTabComponentAt(int index) { 2395 return pages.get(index).tabComponent; 2396 } 2397 2398 /** 2399 * Returns the index of the tab for the specified tab component. 2400 * Returns -1 if there is no tab for this tab component. 2401 * 2402 * @param tabComponent the tab component for the tab 2403 * @return the first tab which matches this tab component, or -1 2404 * if there is no tab for this tab component 2405 * @see #setTabComponentAt 2406 * @see #getTabComponentAt 2407 * @since 1.6 2408 */ 2409 public int indexOfTabComponent(Component tabComponent) { 2410 for(int i = 0; i < getTabCount(); i++) { 2411 Component c = getTabComponentAt(i); 2412 if (c == tabComponent) { 2413 return i; 2414 } 2415 } 2416 return -1; 2417 } 2418 }