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