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