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