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