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