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