001 /* JTabbedPane.java --
002 Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package javax.swing;
040
041 import java.awt.Color;
042 import java.awt.Component;
043 import java.awt.Point;
044 import java.awt.Rectangle;
045 import java.awt.event.MouseEvent;
046 import java.io.Serializable;
047 import java.util.Locale;
048 import java.util.Vector;
049
050 import javax.accessibility.Accessible;
051 import javax.accessibility.AccessibleContext;
052 import javax.accessibility.AccessibleRole;
053 import javax.accessibility.AccessibleSelection;
054 import javax.accessibility.AccessibleState;
055 import javax.accessibility.AccessibleStateSet;
056 import javax.swing.event.ChangeEvent;
057 import javax.swing.event.ChangeListener;
058 import javax.swing.plaf.TabbedPaneUI;
059 import javax.swing.plaf.UIResource;
060
061 /**
062 * This is a container for components where only one component is displayed at
063 * a given time and the displayed component can be switched by clicking on
064 * tabs.
065 *
066 * <p>
067 * Tabs can be oriented in several ways. They can be above, below, left and
068 * right of the component. Tabs can either wrap around (by creating multiple
069 * rows of tabs) or they can be scrolled (where only a subset of the tabs
070 * can be seen at once). More tabs can be added by calling the
071 * add/addTab/insertTab methods.
072 * </p>
073 */
074 public class JTabbedPane extends JComponent implements Serializable,
075 Accessible,
076 SwingConstants
077 {
078 /**
079 * Accessibility support for <code>JTabbedPane</code>.
080 */
081 protected class AccessibleJTabbedPane extends JComponent.AccessibleJComponent
082 implements AccessibleSelection, ChangeListener
083 {
084 /**
085 * The serialization UID.
086 */
087 private static final long serialVersionUID = 7610530885966830483L;
088
089 /**
090 * Creates a new AccessibleJTabbedPane object.
091 */
092 public AccessibleJTabbedPane()
093 {
094 super();
095 }
096
097 /**
098 * Receives notification when the selection state of the
099 * <code>JTabbedPane</code> changes and fires appropriate property change
100 * events to interested listeners.
101 *
102 * @param e the change event describing the change
103 */
104 public void stateChanged(ChangeEvent e)
105 {
106 // I couldn't figure out what else should be done here.
107 Object source = e.getSource();
108 firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
109 null, source);
110 }
111
112 /**
113 * Returns the accessible role of the <code>JTabbedPane</code>, which is
114 * {@link AccessibleRole#PAGE_TAB_LIST}.
115 *
116 * @return the accessible role of the <code>JTabbedPane</code>
117 */
118 public AccessibleRole getAccessibleRole()
119 {
120 return AccessibleRole.PAGE_TAB_LIST;
121 }
122
123 /**
124 * Returns the number of accessible child components of the
125 * <code>JTabbedPane</code>.
126 *
127 * @return the number of accessible child components of the
128 * <code>JTabbedPane</code>
129 */
130 public int getAccessibleChildrenCount()
131 {
132 return getTabCount();
133 }
134
135 /**
136 * Returns the accessible child component at the specified index.
137 *
138 * @param i the index of the child component to fetch
139 *
140 * @return the accessible child component at the specified index
141 */
142 public Accessible getAccessibleChild(int i)
143 {
144 // Testing shows that the reference implementation returns instances
145 // of page here.
146 Accessible child = null;
147 if (i >= 0 && i < tabs.size())
148 child = (Page) tabs.get(i);
149 return child;
150 }
151
152 /**
153 * Returns the current selection state of the <code>JTabbedPane</code>
154 * as AccessibleSelection object.
155 *
156 * @return the current selection state of the <code>JTabbedPane</code>
157 */
158 public AccessibleSelection getAccessibleSelection()
159 {
160 return this;
161 }
162
163 /**
164 * Returns the accessible child component at the specified coordinates.
165 * If there is no child component at this location, then return the
166 * currently selected tab.
167 *
168 * @param p the coordinates at which to look up the child component
169 *
170 * @return the accessible child component at the specified coordinates or
171 * the currently selected tab if there is no child component at
172 * this location
173 */
174 public Accessible getAccessibleAt(Point p)
175 {
176 int tabIndex = indexAtLocation(p.x, p.y);
177 if (tabIndex >= 0)
178 return getAccessibleChild(tabIndex);
179 else
180 return getAccessibleSelection(0);
181 }
182
183 /**
184 * Returns the number of selected child components of the
185 * <code>JTabbedPane</code>. The reference implementation appears
186 * to return <code>1</code> always and we do the same.
187 *
188 * @return <code>1</code>
189 */
190 public int getAccessibleSelectionCount()
191 {
192 return 1;
193 }
194
195 /**
196 * Returns the selected tab, or <code>null</code> if there is no
197 * selection.
198 *
199 * @param i the selection index (ignored here).
200 *
201 * @return The selected tab, or <code>null</code>.
202 */
203 public Accessible getAccessibleSelection(int i)
204 {
205 Accessible result = null;
206 int selected = getSelectedIndex();
207 if (selected >= 0)
208 result = (Page) tabs.get(selected);
209 return result;
210 }
211
212 /**
213 * Returns <code>true</code> if the specified child is selected,
214 * and <code>false</code> otherwise.
215 *
216 * @param i the child index.
217 *
218 * @return A boolean.
219 */
220 public boolean isAccessibleChildSelected(int i)
221 {
222 return i == getSelectedIndex();
223 }
224
225 /**
226 * Selects the specified tab.
227 *
228 * @param i the index of the item to select.
229 */
230 public void addAccessibleSelection(int i)
231 {
232 setSelectedIndex(i);
233 }
234
235 /**
236 * Does nothing - it makes no sense to remove a selection for a
237 * tabbed pane, since one tab must always be selected.
238 *
239 * @param i the item index.
240 *
241 * @see #addAccessibleSelection(int)
242 */
243 public void removeAccessibleSelection(int i)
244 {
245 // do nothing
246 }
247
248 /**
249 * Does nothing - it makes no sense to clear the selection for
250 * a tabbed pane, since one tab must always be selected.
251 *
252 * @see #addAccessibleSelection(int)
253 */
254 public void clearAccessibleSelection()
255 {
256 // do nothing
257 }
258
259 /**
260 * Does nothing - it makes no sense to select all for a tabbed
261 * pane, since only one tab can be selected at a time.
262 *
263 * @see #addAccessibleSelection(int)
264 */
265 public void selectAllAccessibleSelection()
266 {
267 // do nothing
268 }
269 }
270
271 /**
272 * A helper class that listens for changes to the model.
273 */
274 protected class ModelListener implements ChangeListener, Serializable
275 {
276 private static final long serialVersionUID = 497359819958114132L;
277
278 /**
279 * Creates a new ModelListener object.
280 */
281 protected ModelListener()
282 {
283 // Nothing to do here.
284 }
285
286 /**
287 * This method is called whenever the model is changed.
288 *
289 * @param e The ChangeEvent that is passed from the model.
290 */
291 public void stateChanged(ChangeEvent e)
292 {
293 // Propagate to our listeners.
294 fireStateChanged();
295 }
296 }
297
298 /**
299 * A private class that holds all the information for each tab.
300 */
301 private class Page
302 extends AccessibleContext
303 implements Accessible
304 {
305 /** The tooltip string. */
306 private String tip;
307
308 /** The component associated with the tab. */
309 private Component component;
310
311 /** The active icon associated with the tab. */
312 private transient Icon icon;
313
314 /** The disabled icon associated with the tab. */
315 private transient Icon disabledIcon;
316
317 /** The tab's enabled status. */
318 private transient boolean enabled = true;
319
320 /** The string painted on the tab. */
321 private transient String title;
322
323 /** The background color of the tab. */
324 private transient Color bg;
325
326 /** The foreground color of the tab. */
327 private transient Color fg;
328
329 /** The mnemonic associated with the tab. */
330 private transient int mnemonicKey;
331
332 /** The index of the underlined character in the string. */
333 private transient int underlinedChar = -1;
334
335 /**
336 * Creates a new data storage for the tab.
337 *
338 * @param title The string displayed on the tab.
339 * @param icon The active icon displayed on the tab.
340 * @param component The component associated with the tab.
341 * @param tip The tooltip associated with the tab.
342 */
343 protected Page(String title, Icon icon, Component component, String tip)
344 {
345 this.title = title;
346 this.icon = icon;
347 this.component = component;
348 this.tip = tip;
349 }
350
351 /**
352 * This method returns the component associated with the tab.
353 *
354 * @return The component associated with the tab.
355 */
356 public Component getComponent()
357 {
358 return component;
359 }
360
361 /**
362 * This method sets the component associated with the tab.
363 *
364 * @param c The component associated with the tab.
365 */
366 public void setComponent(Component c)
367 {
368 int i = indexOfComponent(component);
369 insertTab(title, icon, c, tip, i);
370 component = c;
371 removeTabAt(i);
372 }
373
374 /**
375 * This method returns the tooltip string.
376 *
377 * @return The tooltip string.
378 */
379 public String getTip()
380 {
381 return tip;
382 }
383
384 /**
385 * This method sets the tooltip string.
386 *
387 * @param tip The tooltip string.
388 */
389 public void setTip(String tip)
390 {
391 this.tip = tip;
392 }
393
394 /**
395 * This method returns the background color.
396 *
397 * @return The background color.
398 */
399 public Color getBackground()
400 {
401 Color background;
402 if (bg == null)
403 background = JTabbedPane.this.getBackground();
404 else
405 background = bg;
406 return background;
407 }
408
409 /**
410 * This method sets the background color.
411 *
412 * @param background The background color.
413 */
414 public void setBackground(Color background)
415 {
416 bg = background;
417 }
418
419 /**
420 * This method returns the foreground color.
421 *
422 * @return The foreground color.
423 */
424 public Color getForeground()
425 {
426 Color foreground;
427 if (fg == null)
428 foreground = JTabbedPane.this.getForeground();
429 else
430 foreground = fg;
431 return foreground;
432 }
433
434 /**
435 * This method sets the foreground color.
436 *
437 * @param foreground The foreground color.
438 */
439 public void setForeground(Color foreground)
440 {
441 fg = foreground;
442 }
443
444 /**
445 * This method returns the title associated with the tab.
446 *
447 * @return The title of the tab.
448 */
449 public String getTitle()
450 {
451 return title;
452 }
453
454 private static final long serialVersionUID = 1614381073220130939L;
455
456 /**
457 * This method sets the title of the tab.
458 *
459 * @param text The title of the tab.
460 */
461 public void setTitle(String text)
462 {
463 title = text;
464 if (title != null && title.length() <= underlinedChar)
465 setDisplayedMnemonicIndex(title.length() - 1);
466 }
467
468 /**
469 * This method returns the active icon.
470 *
471 * @return The active icon.
472 */
473 public Icon getIcon()
474 {
475 return icon;
476 }
477
478 /**
479 * This method sets the active icon.
480 *
481 * @param icon The active icon.
482 */
483 public void setIcon(Icon icon)
484 {
485 this.icon = icon;
486 }
487
488 /**
489 * This method returns the disabled icon.
490 *
491 * @return The disabled icon.
492 */
493 public Icon getDisabledIcon()
494 {
495 if (disabledIcon == null && icon instanceof ImageIcon)
496 setDisabledIcon(icon);
497 return disabledIcon;
498 }
499
500 /**
501 * This method sets the disabled icon.
502 *
503 * @param disabledIcon The disabled icon.
504 */
505 public void setDisabledIcon(Icon disabledIcon)
506 {
507 this.disabledIcon = disabledIcon;
508 }
509
510 /**
511 * This method returns whether the tab is enabled.
512 *
513 * @return Whether the tab is enabled.
514 */
515 public boolean isEnabled()
516 {
517 return enabled;
518 }
519
520 /**
521 * This method sets whether the tab is enabled.
522 *
523 * @param enabled Whether this tab is enabled.
524 */
525 public void setEnabled(boolean enabled)
526 {
527 this.enabled = enabled;
528 }
529
530 /**
531 * This method returns the mnemonic.
532 *
533 * @return The mnemonic.
534 */
535 public int getMnemonic()
536 {
537 return mnemonicKey;
538 }
539
540 /**
541 * This method sets the mnemonic. If the title is set, it will update the
542 * mnemonicIndex.
543 *
544 * @param key The mnemonic.
545 */
546 public void setMnemonic(int key)
547 {
548 setMnemonic((char) key);
549 }
550
551 /**
552 * This method sets the mnemonic. If the title is set, it will update the
553 * mnemonicIndex.
554 *
555 * @param aChar The mnemonic.
556 */
557 public void setMnemonic(char aChar)
558 {
559 mnemonicKey = aChar;
560 if (title != null)
561 setDisplayedMnemonicIndex(title.indexOf(mnemonicKey));
562 }
563
564 /**
565 * This method returns the mnemonicIndex.
566 *
567 * @return The mnemonicIndex.
568 */
569 public int getDisplayedMnemonicIndex()
570 {
571 return underlinedChar;
572 }
573
574 /**
575 * This method sets the mnemonicIndex.
576 *
577 * @param index The mnemonicIndex.
578 *
579 * @throws IllegalArgumentException If index less than -1 || index greater
580 * or equal to title.length.
581 */
582 public void setDisplayedMnemonicIndex(int index)
583 throws IllegalArgumentException
584 {
585 if (index < -1 || title != null && index >= title.length())
586 throw new IllegalArgumentException();
587
588 if (title == null || mnemonicKey == 0 || (index > -1 && title.charAt(index) != mnemonicKey))
589 index = -1;
590
591 underlinedChar = index;
592 }
593
594 /**
595 * Returns the accessible context, which is this object itself.
596 *
597 * @return the accessible context, which is this object itself
598 */
599 public AccessibleContext getAccessibleContext()
600 {
601 return this;
602 }
603
604 /**
605 * Returns the accessible name for this tab.
606 *
607 * @return The accessible name.
608 */
609 public String getAccessibleName()
610 {
611 if (accessibleName != null)
612 return accessibleName;
613 else
614 return title;
615 }
616
617 /**
618 * Returns the accessible role of this tab, which is always
619 * {@link AccessibleRole#PAGE_TAB}.
620 *
621 * @return the accessible role of this tab
622 */
623 public AccessibleRole getAccessibleRole()
624 {
625 return AccessibleRole.PAGE_TAB;
626 }
627
628 /**
629 * Returns the accessible state set of this object.
630 *
631 * @return the accessible state set of this object
632 */
633 public AccessibleStateSet getAccessibleStateSet()
634 {
635 AccessibleContext parentCtx = JTabbedPane.this.getAccessibleContext();
636 AccessibleStateSet state = parentCtx.getAccessibleStateSet();
637 state.add(AccessibleState.SELECTABLE);
638 if (component == getSelectedComponent())
639 state.add(AccessibleState.SELECTED);
640 return state;
641 }
642
643 /**
644 * Returns the index of this tab inside its parent.
645 *
646 * @return the index of this tab inside its parent
647 */
648 public int getAccessibleIndexInParent()
649 {
650 // TODO: Not sure if the title is unambiguous, but I can't figure
651 // another way of doing this.
652 return indexOfTab(title);
653 }
654
655 /**
656 * Returns the number of accessible children, which is always one (the
657 * component of this tab).
658 *
659 * @return the number of accessible children
660 */
661 public int getAccessibleChildrenCount()
662 {
663 return 1;
664 }
665
666 /**
667 * Returns the accessible child of this tab, which is the component
668 * displayed by the tab.
669 *
670 * @return the accessible child of this tab
671 */
672 public Accessible getAccessibleChild(int i)
673 {
674 // A quick test shows that this method always returns the component
675 // displayed by the tab, regardless of the index.
676 return (Accessible) component;
677 }
678
679 /**
680 * Returns the locale of this accessible object.
681 *
682 * @return the locale of this accessible object
683 */
684 public Locale getLocale()
685 {
686 // TODO: Is this ok?
687 return Locale.getDefault();
688 }
689 }
690
691 private static final long serialVersionUID = 1614381073220130939L;
692
693 /** The changeEvent used to fire changes to listeners. */
694 protected ChangeEvent changeEvent;
695
696 /** The listener that listens to the model. */
697 protected ChangeListener changeListener;
698
699 /** The model that describes this JTabbedPane. */
700 protected SingleSelectionModel model;
701
702 /** Indicates that the TabbedPane is in scrolling mode. */
703 public static final int SCROLL_TAB_LAYOUT = 1;
704
705 /** Indicates that the TabbedPane is in wrap mode. */
706 public static final int WRAP_TAB_LAYOUT = 0;
707
708 /** The current tabPlacement of the TabbedPane. */
709 protected int tabPlacement = SwingConstants.TOP;
710
711 /** The current tabLayoutPolicy of the TabbedPane. */
712 private transient int layoutPolicy;
713
714 /** The list of tabs associated with the TabbedPane. */
715 transient Vector tabs = new Vector();
716
717 /**
718 * Creates a new JTabbedPane object with tabs on top and using wrap tab
719 * layout.
720 */
721 public JTabbedPane()
722 {
723 this(SwingConstants.TOP, WRAP_TAB_LAYOUT);
724 }
725
726 /**
727 * Creates a new JTabbedPane object using wrap tab layout and the given
728 * <code>tabPlacement</code>, where <code>tabPlacement</code> can be one
729 * of the following values: {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} or
730 * {@link #RIGHT}.
731 *
732 * @param tabPlacement where the tabs will be placed
733 */
734 public JTabbedPane(int tabPlacement)
735 {
736 this(tabPlacement, WRAP_TAB_LAYOUT);
737 }
738
739 /**
740 * Creates a new JTabbedPane object with the given <code>tabPlacement</code>
741 * and <code>tabLayoutPolicy</code>. The <code>tabPlacement</code> can be one
742 * of the following values: {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} or
743 * {@link #RIGHT}. The <code>tabLayoutPolicy</code> can be either
744 * {@link #SCROLL_TAB_LAYOUT} or {@link #WRAP_TAB_LAYOUT}.
745 *
746 * @param tabPlacement where the tabs will be placed
747 * @param tabLayoutPolicy the way tabs will be placed
748 *
749 * @throws IllegalArgumentException If tabLayoutPolicy or tabPlacement are
750 * not valid.
751 */
752 public JTabbedPane(int tabPlacement, int tabLayoutPolicy)
753 {
754 if (tabPlacement != TOP && tabPlacement != BOTTOM && tabPlacement != RIGHT
755 && tabPlacement != LEFT)
756 throw new IllegalArgumentException("tabPlacement is not valid.");
757 if (tabLayoutPolicy != SCROLL_TAB_LAYOUT
758 && tabLayoutPolicy != WRAP_TAB_LAYOUT)
759 throw new IllegalArgumentException("tabLayoutPolicy is not valid.");
760 this.tabPlacement = tabPlacement;
761 layoutPolicy = tabLayoutPolicy;
762
763 setModel(new DefaultSingleSelectionModel());
764
765 updateUI();
766 }
767
768 /**
769 * This method returns the UI used to display the JTabbedPane.
770 *
771 * @return The UI used to display the JTabbedPane.
772 */
773 public TabbedPaneUI getUI()
774 {
775 return (TabbedPaneUI) ui;
776 }
777
778 /**
779 * This method sets the UI used to display the JTabbedPane.
780 *
781 * @param ui The UI used to display the JTabbedPane.
782 */
783 public void setUI(TabbedPaneUI ui)
784 {
785 super.setUI(ui);
786 }
787
788 /**
789 * This method restores the UI to the defaults given by the UIManager.
790 */
791 public void updateUI()
792 {
793 setUI((TabbedPaneUI) UIManager.getUI(this));
794 }
795
796 /**
797 * This method returns a string identifier that is used to determine which
798 * UI will be used with the JTabbedPane.
799 *
800 * @return A string identifier for the UI.
801 */
802 public String getUIClassID()
803 {
804 return "TabbedPaneUI";
805 }
806
807 /**
808 * This method creates a ChangeListener that is used to listen to the model
809 * for events.
810 *
811 * @return A ChangeListener to listen to the model.
812 */
813 protected ChangeListener createChangeListener()
814 {
815 return new ModelListener();
816 }
817
818 /**
819 * This method adds a ChangeListener to the JTabbedPane.
820 *
821 * @param l The ChangeListener to add.
822 */
823 public void addChangeListener(ChangeListener l)
824 {
825 listenerList.add(ChangeListener.class, l);
826 }
827
828 /**
829 * This method removes a ChangeListener to the JTabbedPane.
830 *
831 * @param l The ChangeListener to remove.
832 */
833 public void removeChangeListener(ChangeListener l)
834 {
835 listenerList.remove(ChangeListener.class, l);
836 }
837
838 /**
839 * This method fires a ChangeEvent to all the JTabbedPane's ChangeListeners.
840 */
841 protected void fireStateChanged()
842 {
843 Object[] changeListeners = listenerList.getListenerList();
844 if (changeEvent == null)
845 changeEvent = new ChangeEvent(this);
846 for (int i = changeListeners.length - 2; i >= 0; i -= 2)
847 {
848 if (changeListeners[i] == ChangeListener.class)
849 ((ChangeListener) changeListeners[i + 1]).stateChanged(changeEvent);
850 }
851 }
852
853 /**
854 * This method returns all ChangeListeners registered with the JTabbedPane.
855 *
856 * @return The ChangeListeners registered with the JTabbedPane.
857 */
858 public ChangeListener[] getChangeListeners()
859 {
860 return (ChangeListener[]) super.getListeners(ChangeListener.class);
861 }
862
863 /**
864 * This method returns the model used with the JTabbedPane.
865 *
866 * @return The JTabbedPane's model.
867 */
868 public SingleSelectionModel getModel()
869 {
870 return model;
871 }
872
873 /**
874 * This method changes the model property of the JTabbedPane.
875 *
876 * @param m The new model to use with the JTabbedPane.
877 */
878 public void setModel(SingleSelectionModel m)
879 {
880 if (m != model)
881 {
882 SingleSelectionModel oldModel = this.model;
883 if (oldModel != null && changeListener != null)
884 oldModel.removeChangeListener(changeListener);
885
886 model = m;
887
888 if (model != null)
889 {
890 if (changeListener == null)
891 changeListener = createChangeListener();
892 model.addChangeListener(changeListener);
893 }
894 firePropertyChange("model", oldModel, this.model);
895 }
896 }
897
898 /**
899 * This method returns the tabPlacement.
900 *
901 * @return The tabPlacement used with the JTabbedPane.
902 */
903 public int getTabPlacement()
904 {
905 return tabPlacement;
906 }
907
908 /**
909 * This method changes the tabPlacement property of the JTabbedPane.
910 *
911 * @param tabPlacement The tabPlacement to use.
912 *
913 * @throws IllegalArgumentException If tabPlacement is not one of TOP,
914 * BOTTOM, LEFT, or RIGHT.
915 */
916 public void setTabPlacement(int tabPlacement)
917 {
918 if (tabPlacement != TOP && tabPlacement != BOTTOM && tabPlacement != RIGHT
919 && tabPlacement != LEFT)
920 throw new IllegalArgumentException("tabPlacement is not valid.");
921 if (tabPlacement != this.tabPlacement)
922 {
923 int oldPlacement = this.tabPlacement;
924 this.tabPlacement = tabPlacement;
925 firePropertyChange("tabPlacement", oldPlacement, this.tabPlacement);
926 }
927 }
928
929 /**
930 * This method returns the tabLayoutPolicy.
931 *
932 * @return The tabLayoutPolicy.
933 */
934 public int getTabLayoutPolicy()
935 {
936 return layoutPolicy;
937 }
938
939 /**
940 * This method changes the tabLayoutPolicy property of the JTabbedPane.
941 *
942 * @param tabLayoutPolicy The tabLayoutPolicy to use.
943 *
944 * @throws IllegalArgumentException If tabLayoutPolicy is not one of
945 * SCROLL_TAB_LAYOUT or WRAP_TAB_LAYOUT.
946 */
947 public void setTabLayoutPolicy(int tabLayoutPolicy)
948 {
949 if (tabLayoutPolicy != SCROLL_TAB_LAYOUT
950 && tabLayoutPolicy != WRAP_TAB_LAYOUT)
951 throw new IllegalArgumentException("tabLayoutPolicy is not valid.");
952 if (tabLayoutPolicy != layoutPolicy)
953 {
954 int oldPolicy = layoutPolicy;
955 layoutPolicy = tabLayoutPolicy;
956 firePropertyChange("tabLayoutPolicy", oldPolicy, layoutPolicy);
957 }
958 }
959
960 /**
961 * This method returns the index of the tab that is currently selected.
962 *
963 * @return The index of the selected tab.
964 */
965 public int getSelectedIndex()
966 {
967 return model.getSelectedIndex();
968 }
969
970 /**
971 * This method checks the index.
972 *
973 * @param index The index to check.
974 * @param start DOCUMENT ME!
975 * @param end DOCUMENT ME!
976 *
977 * @throws IndexOutOfBoundsException DOCUMENT ME!
978 */
979 private void checkIndex(int index, int start, int end)
980 {
981 if (index < start || index >= end)
982 throw new IndexOutOfBoundsException("Index < " + start + " || Index >= "
983 + end);
984 }
985
986 /**
987 * This method sets the selected index. This method will hide the old
988 * component and show the new component.
989 *
990 * @param index The index to set it at.
991 */
992 public void setSelectedIndex(int index)
993 {
994 checkIndex(index, -1, tabs.size());
995 if (index != getSelectedIndex())
996 {
997 // Hiding and showing the involved components
998 // is done by the JTabbedPane's UI.
999 model.setSelectedIndex(index);
1000 }
1001 }
1002
1003 /**
1004 * This method returns the component at the selected index.
1005 *
1006 * @return The component at the selected index.
1007 */
1008 public Component getSelectedComponent()
1009 {
1010 int selectedIndex = getSelectedIndex();
1011 Component selected = null;
1012 if (selectedIndex >= 0)
1013 selected = getComponentAt(selectedIndex);
1014 return selected;
1015 }
1016
1017 /**
1018 * This method sets the component at the selected index.
1019 *
1020 * @param c The component associated with the selected index.
1021 */
1022 public void setSelectedComponent(Component c)
1023 {
1024 if (c.getParent() == this)
1025 setSelectedIndex(indexOfComponent(c));
1026 else
1027 setComponentAt(getSelectedIndex(), c);
1028 }
1029
1030 /**
1031 * This method inserts tabs into JTabbedPane. This includes adding the
1032 * component to the JTabbedPane and hiding it.
1033 *
1034 * @param title the title of the tab; may be <code>null</code>
1035 * @param icon the tab's icon; may be <code>null</code>
1036 * @param component the component associated with the tab
1037 * @param tip the tooltip for the tab
1038 * @param index the index to insert the tab at
1039 */
1040 public void insertTab(String title, Icon icon, Component component,
1041 String tip, int index)
1042 {
1043 if (title == null)
1044 title = "";
1045 Page p = new Page(title, icon, component, tip);
1046 tabs.insertElementAt(p, index);
1047
1048 // Hide the component so we don't see it. Do it before we parent it
1049 // so we don't trigger a repaint.
1050 if (component != null)
1051 {
1052 component.hide();
1053 super.add(component);
1054 }
1055
1056 if (getSelectedIndex() == -1)
1057 {
1058 setSelectedIndex(0);
1059 fireStateChanged();
1060 }
1061
1062 revalidate();
1063 repaint();
1064 }
1065
1066 /**
1067 * This method adds a tab to the JTabbedPane.
1068 *
1069 * @param title the title of the tab; may be <code>null</code>
1070 * @param icon the icon for the tab; may be <code>null</code>
1071 * @param component the associated component
1072 * @param tip the associated tooltip
1073 */
1074 public void addTab(String title, Icon icon, Component component, String tip)
1075 {
1076 insertTab(title, icon, component, tip, tabs.size());
1077 }
1078
1079 /**
1080 * This method adds a tab to the JTabbedPane.
1081 *
1082 * @param title the title of the tab; may be <code>null</code>
1083 * @param icon the icon for the tab; may be <code>null</code>
1084 * @param component the associated component
1085 */
1086 public void addTab(String title, Icon icon, Component component)
1087 {
1088 insertTab(title, icon, component, null, tabs.size());
1089 }
1090
1091 /**
1092 * This method adds a tab to the JTabbedPane.
1093 *
1094 * @param title the title of the tab; may be <code>null</code>
1095 * @param component the associated component
1096 */
1097 public void addTab(String title, Component component)
1098 {
1099 insertTab(title, null, component, null, tabs.size());
1100 }
1101
1102 /**
1103 * This method adds a tab to the JTabbedPane. The title of the tab is the
1104 * Component's name. If the Component is an instance of UIResource, it
1105 * doesn't add the tab and instead add the component directly to the
1106 * JTabbedPane.
1107 *
1108 * @param component The associated component.
1109 *
1110 * @return The Component that was added.
1111 */
1112 public Component add(Component component)
1113 {
1114 if (component instanceof UIResource)
1115 super.add(component);
1116 else
1117 insertTab(component.getName(), null, component, null, tabs.size());
1118
1119 return component;
1120 }
1121
1122 /**
1123 * This method adds a tab to the JTabbedPane. If the Component is an
1124 * instance of UIResource, it doesn't add the tab and instead add the
1125 * component directly to the JTabbedPane.
1126 *
1127 * @param title the title of the tab; may be <code>null</code>
1128 * @param component the associated component
1129 *
1130 * @return The Component that was added.
1131 */
1132 public Component add(String title, Component component)
1133 {
1134 if (component instanceof UIResource)
1135 super.add(component);
1136 else
1137 insertTab(title, null, component, null, tabs.size());
1138 return component;
1139 }
1140
1141 /**
1142 * This method adds a tab to the JTabbedPane. If the Component is an
1143 * instance of UIResource, it doesn't add the tab and instead add the
1144 * component directly to the JTabbedPane.
1145 *
1146 * @param component The associated component.
1147 * @param index The index to insert the tab at.
1148 *
1149 * @return The Component that was added.
1150 */
1151 public Component add(Component component, int index)
1152 {
1153 if (component instanceof UIResource)
1154 super.add(component);
1155 else
1156 insertTab(component.getName(), null, component, null, index);
1157 return component;
1158 }
1159
1160 /**
1161 * This method adds a tab to the JTabbedPane. If the Component is an
1162 * instance of UIResource, it doesn't add the tab and instead add the
1163 * component directly to the JTabbedPane. If the constraints object is an
1164 * icon, it will be used as the tab's icon. If the constraints object is a
1165 * string, we will use it as the title.
1166 *
1167 * @param component The associated component.
1168 * @param constraints The constraints object.
1169 */
1170 public void add(Component component, Object constraints)
1171 {
1172 add(component, constraints, tabs.size());
1173 }
1174
1175 /**
1176 * This method adds a tab to the JTabbedPane. If the Component is an
1177 * instance of UIResource, it doesn't add the tab and instead add the
1178 * component directly to the JTabbedPane. If the constraints object is an
1179 * icon, it will be used as the tab's icon. If the constraints object is a
1180 * string, we will use it as the title.
1181 *
1182 * @param component The associated component.
1183 * @param constraints The constraints object.
1184 * @param index The index to insert the tab at.
1185 */
1186 public void add(Component component, Object constraints, int index)
1187 {
1188 if (component instanceof UIResource)
1189 super.add(component);
1190 else
1191 {
1192 if (constraints instanceof String)
1193 insertTab((String) constraints, null, component, null, index);
1194 else
1195 insertTab(component.getName(),
1196 (constraints instanceof Icon) ? (Icon) constraints : null,
1197 component, null, index);
1198 }
1199 }
1200
1201 /**
1202 * Removes the tab at index. After the component associated with
1203 * index is removed, its visibility is reset to true to ensure it
1204 * will be visible if added to other containers.
1205 *
1206 * @param index The index of the tab to remove.
1207 */
1208 public void removeTabAt(int index)
1209 {
1210 checkIndex(index, 0, tabs.size());
1211
1212 // We need to adjust the selection if we remove a tab that comes
1213 // before the selected tab or if the selected tab is removed.
1214 // This decrements the selected index by 1 if any of this is the case.
1215 // Note that this covers all cases:
1216 // - When the selected tab comes after the removed tab, this simply
1217 // adjusts the selection so that after the removal the selected tab
1218 // is still the same.
1219 // - When we remove the currently selected tab, then the tab before the
1220 // selected tab gets selected.
1221 // - When the last tab is removed, then we have an index==0, which gets
1222 // decremented to -1, which means no selection, which is 100% perfect.
1223 int selectedIndex = getSelectedIndex();
1224 if (selectedIndex >= index)
1225 setSelectedIndex(selectedIndex - 1);
1226
1227 Component comp = getComponentAt(index);
1228
1229 // Remove the tab object.
1230 tabs.remove(index);
1231
1232 // Remove the component. I think we cannot assume that the tab order
1233 // is equal to the component order, so we iterate over the children
1234 // here to find the and remove the correct component.
1235 if (comp != null)
1236 {
1237 Component[] children = getComponents();
1238 for (int i = children.length - 1; i >= 0; --i)
1239 {
1240 if (children[i] == comp)
1241 {
1242 super.remove(i);
1243 comp.setVisible(true);
1244 break;
1245 }
1246 }
1247 }
1248 revalidate();
1249 repaint();
1250 }
1251
1252 /**
1253 * Removes the specified Component from the JTabbedPane.
1254 *
1255 * @param component The Component to remove.
1256 */
1257 public void remove(Component component)
1258 {
1259 // Since components implementing UIResource
1260 // are not added as regular tabs by the add()
1261 // methods we have to take special care when
1262 // removing these object. Especially
1263 // Container.remove(Component) cannot be used
1264 // because it will call JTabbedPane.remove(int)
1265 // later which is overridden and can only
1266 // handle tab components.
1267 // This implementation can even cope with a
1268 // situation that someone called insertTab()
1269 // with a component that implements UIResource.
1270 int index = indexOfComponent(component);
1271
1272 // If the component is not a tab component
1273 // find out its Container-given index
1274 // and call that class' implementation
1275 // directly.
1276 if (index == -1)
1277 {
1278 Component[] cs = getComponents();
1279 for (int i = 0; i< cs.length; i++)
1280 if (cs[i] == component)
1281 super.remove(i);
1282 }
1283 else
1284 removeTabAt(index);
1285 }
1286
1287 /**
1288 * Removes the tab and component which corresponds to the specified index.
1289 *
1290 * @param index The index of the tab to remove.
1291 */
1292 public void remove(int index)
1293 {
1294 removeTabAt(index);
1295 }
1296
1297 /**
1298 * This method removes all tabs and associated components from the
1299 * JTabbedPane.
1300 */
1301 public void removeAll()
1302 {
1303 setSelectedIndex(-1);
1304 for (int i = getTabCount() - 1; i >= 0; i--)
1305 removeTabAt(i);
1306 }
1307
1308 /**
1309 * This method returns how many tabs are in the JTabbedPane.
1310 *
1311 * @return The number of tabs in the JTabbedPane.
1312 */
1313 public int getTabCount()
1314 {
1315 return tabs.size();
1316 }
1317
1318 /**
1319 * This method returns the number of runs used to paint the JTabbedPane.
1320 *
1321 * @return The number of runs.
1322 */
1323 public int getTabRunCount()
1324 {
1325 return ((TabbedPaneUI) ui).getTabRunCount(this);
1326 }
1327
1328 /**
1329 * This method returns the tab title given the index.
1330 *
1331 * @param index The index of the tab.
1332 *
1333 * @return The title for the tab.
1334 */
1335 public String getTitleAt(int index)
1336 {
1337 checkIndex(index, 0, tabs.size());
1338 return ((Page) tabs.elementAt(index)).getTitle();
1339 }
1340
1341 /**
1342 * This method returns the active icon given the index.
1343 *
1344 * @param index The index of the tab.
1345 *
1346 * @return The active icon for the tab.
1347 */
1348 public Icon getIconAt(int index)
1349 {
1350 checkIndex(index, 0, tabs.size());
1351 return ((Page) tabs.elementAt(index)).getIcon();
1352 }
1353
1354 /**
1355 * This method returns the disabled icon given the index.
1356 *
1357 * @param index The index of the tab.
1358 *
1359 * @return The disabled icon for the tab.
1360 */
1361 public Icon getDisabledIconAt(int index)
1362 {
1363 checkIndex(index, 0, tabs.size());
1364 return ((Page) tabs.elementAt(index)).getDisabledIcon();
1365 }
1366
1367 /**
1368 * This method returns the tooltip string for the tab.
1369 *
1370 * @param index The index of the tab.
1371 *
1372 * @return The tooltip string for the tab.
1373 */
1374 public String getToolTipTextAt(int index)
1375 {
1376 checkIndex(index, 0, tabs.size());
1377 return ((Page) tabs.elementAt(index)).getTip();
1378 }
1379
1380 /**
1381 * This method returns the foreground color for the tab.
1382 *
1383 * @param index The index of the tab.
1384 *
1385 * @return The foreground color for the tab.
1386 */
1387 public Color getForegroundAt(int index)
1388 {
1389 checkIndex(index, 0, tabs.size());
1390 return ((Page) tabs.elementAt(index)).getForeground();
1391 }
1392
1393 /**
1394 * This method returns the background color for the tab.
1395 *
1396 * @param index The index of the tab.
1397 *
1398 * @return The background color for the tab.
1399 */
1400 public Color getBackgroundAt(int index)
1401 {
1402 checkIndex(index, 0, tabs.size());
1403 return ((Page) tabs.elementAt(index)).getBackground();
1404 }
1405
1406 /**
1407 * This method returns the component associated with the tab.
1408 *
1409 * @param index The index of the tab.
1410 *
1411 * @return The component associated with the tab.
1412 */
1413 public Component getComponentAt(int index)
1414 {
1415 checkIndex(index, 0, tabs.size());
1416 return ((Page) tabs.elementAt(index)).getComponent();
1417 }
1418
1419 /**
1420 * This method returns whether this tab is enabled. Disabled tabs cannot be
1421 * selected.
1422 *
1423 * @param index The index of the tab.
1424 *
1425 * @return Whether the tab is enabled.
1426 */
1427 public boolean isEnabledAt(int index)
1428 {
1429 checkIndex(index, 0, tabs.size());
1430 return ((Page) tabs.elementAt(index)).isEnabled();
1431 }
1432
1433 /**
1434 * This method returns the mnemonic for the tab.
1435 *
1436 * @param tabIndex The index of the tab.
1437 *
1438 * @return The mnemonic for the tab.
1439 */
1440 public int getMnemonicAt(int tabIndex)
1441 {
1442 checkIndex(tabIndex, 0, tabs.size());
1443 return ((Page) tabs.elementAt(tabIndex)).getMnemonic();
1444 }
1445
1446 /**
1447 * This method returns the mnemonic index for the tab.
1448 *
1449 * @param tabIndex The index of the tab.
1450 *
1451 * @return The mnemonic index for the tab.
1452 */
1453 public int getDisplayedMnemonicIndexAt(int tabIndex)
1454 {
1455 checkIndex(tabIndex, 0, tabs.size());
1456 return ((Page) tabs.elementAt(tabIndex)).getDisplayedMnemonicIndex();
1457 }
1458
1459 /**
1460 * This method returns the bounds of the tab given the index.
1461 *
1462 * @param index The index of the tab.
1463 *
1464 * @return A rectangle describing the bounds of the tab.
1465 */
1466 public Rectangle getBoundsAt(int index)
1467 {
1468 checkIndex(index, 0, tabs.size());
1469 return ((TabbedPaneUI) ui).getTabBounds(this, index);
1470 }
1471
1472 /**
1473 * This method sets the title of the tab.
1474 *
1475 * @param index The index of the tab.
1476 * @param title The new title.
1477 */
1478 public void setTitleAt(int index, String title)
1479 {
1480 checkIndex(index, 0, tabs.size());
1481 ((Page) tabs.elementAt(index)).setTitle(title);
1482 }
1483
1484 /**
1485 * This method sets the icon of the tab.
1486 *
1487 * @param index The index of the tab.
1488 * @param icon The new icon.
1489 */
1490 public void setIconAt(int index, Icon icon)
1491 {
1492 checkIndex(index, 0, tabs.size());
1493 ((Page) tabs.elementAt(index)).setIcon(icon);
1494 }
1495
1496 /**
1497 * This method sets the disabled icon of the tab.
1498 *
1499 * @param index The index of the tab.
1500 * @param disabledIcon The new disabled icon.
1501 */
1502 public void setDisabledIconAt(int index, Icon disabledIcon)
1503 {
1504 checkIndex(index, 0, tabs.size());
1505 ((Page) tabs.elementAt(index)).setDisabledIcon(disabledIcon);
1506 }
1507
1508 /**
1509 * This method sets the tooltip text of the tab.
1510 *
1511 * @param index The index of the tab.
1512 * @param toolTipText The tooltip text.
1513 */
1514 public void setToolTipTextAt(int index, String toolTipText)
1515 {
1516 checkIndex(index, 0, tabs.size());
1517 ((Page) tabs.elementAt(index)).setTip(toolTipText);
1518 }
1519
1520 /**
1521 * This method sets the background color of the tab.
1522 *
1523 * @param index The index of the tab.
1524 * @param background The background color of the tab.
1525 */
1526 public void setBackgroundAt(int index, Color background)
1527 {
1528 checkIndex(index, 0, tabs.size());
1529 ((Page) tabs.elementAt(index)).setBackground(background);
1530 }
1531
1532 /**
1533 * This method sets the foreground color of the tab.
1534 *
1535 * @param index The index of the tab.
1536 * @param foreground The foreground color of the tab.
1537 */
1538 public void setForegroundAt(int index, Color foreground)
1539 {
1540 checkIndex(index, 0, tabs.size());
1541 ((Page) tabs.elementAt(index)).setForeground(foreground);
1542 }
1543
1544 /**
1545 * This method sets whether the tab is enabled.
1546 *
1547 * @param index The index of the tab.
1548 * @param enabled Whether the tab is enabled.
1549 */
1550 public void setEnabledAt(int index, boolean enabled)
1551 {
1552 checkIndex(index, 0, tabs.size());
1553 ((Page) tabs.elementAt(index)).setEnabled(enabled);
1554 }
1555
1556 /**
1557 * This method sets the component associated with the tab.
1558 *
1559 * @param index The index of the tab.
1560 * @param component The component associated with the tab.
1561 */
1562 public void setComponentAt(int index, Component component)
1563 {
1564 checkIndex(index, 0, tabs.size());
1565 ((Page) tabs.elementAt(index)).setComponent(component);
1566 }
1567
1568 /**
1569 * This method sets the displayed mnemonic index of the tab.
1570 *
1571 * @param tabIndex The index of the tab.
1572 * @param mnemonicIndex The mnemonic index.
1573 */
1574 public void setDisplayedMnemonicIndexAt(int tabIndex, int mnemonicIndex)
1575 {
1576 checkIndex(tabIndex, 0, tabs.size());
1577 ((Page) tabs.elementAt(tabIndex)).setDisplayedMnemonicIndex(mnemonicIndex);
1578 }
1579
1580 /**
1581 * This method sets the mnemonic for the tab.
1582 *
1583 * @param tabIndex The index of the tab.
1584 * @param mnemonic The mnemonic.
1585 */
1586 public void setMnemonicAt(int tabIndex, int mnemonic)
1587 {
1588 checkIndex(tabIndex, 0, tabs.size());
1589 ((Page) tabs.elementAt(tabIndex)).setMnemonic(mnemonic);
1590 }
1591
1592 /**
1593 * This method finds the index of a tab given the title.
1594 *
1595 * @param title The title that belongs to a tab.
1596 *
1597 * @return The index of the tab that has the title or -1 if not found.
1598 */
1599 public int indexOfTab(String title)
1600 {
1601 int index = -1;
1602 for (int i = 0; i < tabs.size(); i++)
1603 {
1604 if (((Page) tabs.elementAt(i)).getTitle().equals(title))
1605 {
1606 index = i;
1607 break;
1608 }
1609 }
1610 return index;
1611 }
1612
1613 /**
1614 * This method finds the index of a tab given the icon.
1615 *
1616 * @param icon The icon that belongs to a tab.
1617 *
1618 * @return The index of the tab that has the icon or -1 if not found.
1619 */
1620 public int indexOfTab(Icon icon)
1621 {
1622 int index = -1;
1623 for (int i = 0; i < tabs.size(); i++)
1624 {
1625 if (((Page) tabs.elementAt(i)).getIcon() == icon)
1626 {
1627 index = i;
1628 break;
1629 }
1630 }
1631 return index;
1632 }
1633
1634 /**
1635 * This method finds the index of a tab given the component.
1636 *
1637 * @param component A component associated with a tab.
1638 *
1639 * @return The index of the tab that has this component or -1 if not found.
1640 */
1641 public int indexOfComponent(Component component)
1642 {
1643 int index = -1;
1644 for (int i = 0; i < tabs.size(); i++)
1645 {
1646 if (((Page) tabs.elementAt(i)).getComponent() == component)
1647 {
1648 index = i;
1649 break;
1650 }
1651 }
1652 return index;
1653 }
1654
1655 /**
1656 * This method returns a tab index given an (x,y) location. The origin of
1657 * the (x,y) pair will be the JTabbedPane's top left position. The tab
1658 * returned will be the one that contains the point. This method is
1659 * delegated to the UI.
1660 *
1661 * @param x The x coordinate of the point.
1662 * @param y The y coordinate of the point.
1663 *
1664 * @return The index of the tab that contains the point.
1665 */
1666 public int indexAtLocation(int x, int y)
1667 {
1668 return ((TabbedPaneUI) ui).tabForCoordinate(this, x, y);
1669 }
1670
1671 /**
1672 * This method returns the tooltip text given a mouse event.
1673 *
1674 * @param event The mouse event.
1675 *
1676 * @return The tool tip text that is associated with this mouse event.
1677 */
1678 public String getToolTipText(MouseEvent event)
1679 {
1680 int index = indexAtLocation(event.getX(), event.getY());
1681 return ((Page) tabs.elementAt(index)).getTip();
1682 }
1683
1684 /**
1685 * Returns a string describing the attributes for the
1686 * <code>JTabbedPane</code> component, for use in debugging. The return
1687 * value is guaranteed to be non-<code>null</code>, but the format of the
1688 * string may vary between implementations.
1689 *
1690 * @return A string describing the attributes of the
1691 * <code>JTabbedPane</code>.
1692 */
1693 protected String paramString()
1694 {
1695 StringBuffer sb = new StringBuffer(super.paramString());
1696 sb.append(",tabPlacement=");
1697 if (tabPlacement == TOP)
1698 sb.append("TOP");
1699 if (tabPlacement == BOTTOM)
1700 sb.append("BOTTOM");
1701 if (tabPlacement == LEFT)
1702 sb.append("LEFT");
1703 if (tabPlacement == RIGHT)
1704 sb.append("RIGHT");
1705 return sb.toString();
1706 }
1707
1708 /**
1709 * Returns the object that provides accessibility features for this
1710 * <code>JTabbedPane</code> component.
1711 *
1712 * @return The accessible context (an instance of
1713 * {@link AccessibleJTabbedPane}).
1714 */
1715 public AccessibleContext getAccessibleContext()
1716 {
1717 if (accessibleContext == null)
1718 {
1719 AccessibleJTabbedPane ctx = new AccessibleJTabbedPane();
1720 addChangeListener(ctx);
1721 accessibleContext = ctx;
1722 }
1723
1724 return accessibleContext;
1725 }
1726 }