home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / JTabbedPane.java < prev    next >
Text File  |  1998-02-26  |  34KB  |  1,106 lines

  1. /*
  2.  * %W% %E%
  3.  *
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  *
  19.  */
  20.  
  21. package com.sun.java.swing;
  22.  
  23. import java.awt.*;
  24. import java.awt.event.*;
  25. import java.beans.*;
  26. import java.util.*;
  27. import com.sun.java.swing.event.*;
  28. import com.sun.java.swing.plaf.*;
  29. import com.sun.java.accessibility.*;
  30. import java.io.Serializable; 
  31.  
  32. /**
  33.  * A component which lets the user switch between a group of components by
  34.  * clicking on a tab with a given title and/or icon.
  35.  * <p>
  36.  * Tabs/components are added to a TabbedPane object by using the addTab and
  37.  * insertTab methods.  A tab is represented by an index corresponding
  38.  * to the position it was added in, where the first tab has an index equal to 0
  39.  * and the last tab has an index equal to the tab count minus 1.
  40.  * <p>
  41.  * The TabbedPane uses a SingleSelectionModel to represent the set
  42.  * of tab indeces and the currently selected index.  If the tab count 
  43.  * is greater than 0, then there will always be a selected index, which
  44.  * by default will be initialized to the first tab.  If the tab count is
  45.  * 0, then the selected index will be -1.
  46.  *
  47.  * @see SingleSelectionModel
  48.  * <p>
  49.  * See <a href="http://java.sun.com/docs/books/tutorial/ui/swing/tabbedpane.html">How to Use Tabbed Panes</a>
  50.  * in <a href="http://java.sun.com/Series/Tutorial/index.html"><em>The Java Tutorial</em></a>
  51.  * for further documentation.
  52.  * <p>
  53.  * Warning: serialized objects of this class will not be compatible with
  54.  * future swing releases.  The current serialization support is appropriate 
  55.  * for short term storage or RMI between Swing1.0 applications.  It will
  56.  * not be possible to load serialized Swing1.0 objects with future releases
  57.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  58.  * baseline for the serialized form of Swing objects.
  59.  *
  60.  * @beaninfo
  61.  *      attribute: isContainer true
  62.  *    description: A component which provides a tab folder metaphor for 
  63.  *                 displaying one component from a set of components.
  64.  *
  65.  * @version %I% %G%
  66.  * @author Dave Moore
  67.  * @author Philip Milne
  68.  * @author Amy Fowler
  69.  */
  70. public class JTabbedPane extends JComponent 
  71.        implements Serializable, Accessible, SwingConstants {
  72.  
  73.     protected int tabPlacement = TOP;
  74.     protected SingleSelectionModel model;
  75.  
  76.     private boolean haveRegistered;
  77.  
  78.     /**
  79.      * The changeListener is the listener we add to the
  80.      * model.  
  81.      */
  82.     protected ChangeListener changeListener = null;
  83.  
  84.     Vector pages;
  85.  
  86.     /**
  87.      * Only one ChangeEvent is needed per TabPane instance since the
  88.      * event's only (read-only) state is the source property.  The source
  89.      * of events generated here is always "this".
  90.      */
  91.     protected transient ChangeEvent changeEvent = null;
  92.  
  93.     /**
  94.      * Creates an empty TabbedPane.
  95.      * @see #addTab
  96.      */
  97.     public JTabbedPane() {
  98.         this(TOP);
  99.     }
  100.  
  101.     /**
  102.      * Creates an empty TabbedPane with the specified tab placement
  103.      * of either: TOP, BOTTOM, LEFT, or RIGHT.
  104.      * @param tabPlacement the placement for the tabs relative to the content
  105.      * @see #addTab
  106.      */
  107.     public JTabbedPane(int tabPlacement) {
  108.         setTabPlacement(tabPlacement);
  109.         pages = new Vector(1);
  110.         setModel(new DefaultSingleSelectionModel());
  111.         updateUI();
  112.     }
  113.  
  114.     /**
  115.      * Returns the UI object which implements the L&F for this component.
  116.      * @see #setUI
  117.      */
  118.     public TabbedPaneUI getUI() {
  119.         return (TabbedPaneUI)ui;
  120.     }
  121.  
  122.     /**
  123.      * Sets the UI object which implements the L&F for this component.
  124.      * @param ui the new UI object
  125.      * @see UIDefaults#getUI
  126.      * @beaninfo
  127.      *       bound: true
  128.      *      hidden: true
  129.      * description: The UI object that implements the tabbedpane's LookAndFeel
  130.      */
  131.     public void setUI(TabbedPaneUI ui) {
  132.         super.setUI(ui);
  133.     }
  134.  
  135.     /**
  136.      * Notification from the UIManager that the L&F has changed. 
  137.      * Called to replace the UI with the latest version from the 
  138.      * default UIFactory.
  139.      *
  140.      * @see JComponent#updateUI
  141.      */
  142.     public void updateUI() {
  143.         int count = getTabCount();
  144.  
  145.         while (count-- > 0) {
  146.             Page page = (Page)pages.elementAt(count);
  147.             Component component = page.component;
  148.  
  149.             if (component.getParent() != this && component instanceof Container) {
  150.                 page.needsUIUpdate = true;
  151.             } else {
  152.                page.needsUIUpdate = false;
  153.            }
  154.         }
  155.         setUI((TabbedPaneUI)UIManager.getUI(this));
  156.     }
  157.  
  158.  
  159.     /**
  160.      * Returns the name of the UI class that implements the
  161.      * L&F for this component.
  162.      *
  163.      * @return "TabbedPaneUI"
  164.      * @see JComponent#getUIClassID
  165.      * @see UIDefaults#getUI
  166.      */
  167.     public String getUIClassID() {
  168.         return "TabbedPaneUI";
  169.     }
  170.  
  171.  
  172.     /**
  173.      * We pass ModelChanged events along to the listeners with
  174.      * the tabbedpane (instead of the model itself) as the event source.
  175.      */
  176.     protected class ModelListener implements ChangeListener, Serializable {
  177.         public void stateChanged(ChangeEvent e) {
  178.  
  179.             fireStateChanged();
  180.  
  181.             int count = getTabCount();
  182.             while (count-- > 0) {
  183.                 Page page = (Page)pages.elementAt(count);
  184.                 Component component = page.component;
  185.  
  186.                 if (page.needsUIUpdate && component.getParent() == JTabbedPane.this && component instanceof Container) {
  187.                     SwingUtilities.updateComponentTreeUI(component);
  188.                     page.needsUIUpdate = false;
  189.                     component.validate();
  190.                 }
  191.             }
  192.         }
  193.     }
  194.  
  195.     /**
  196.      * Subclasses that want to handle ChangeEvents differently
  197.      * can override this to return a subclass of ModelListener or
  198.      * another ChangeListener implementation.
  199.      * 
  200.      * @see #fireStateChanged
  201.      */
  202.     protected ChangeListener createChangeListener() {
  203.         return new ModelListener();
  204.     }
  205.  
  206.     /**
  207.      * Adds a ChangeListener to this tabbedpane.
  208.      *
  209.      * @param l the ChangeListener to add
  210.      * @see #fireStateChanged
  211.      * @see #removeChangeListener
  212.      */
  213.     public void addChangeListener(ChangeListener l) {
  214.         listenerList.add(ChangeListener.class, l);
  215.     }
  216.     
  217.     /**
  218.      * Removes a ChangeListener from this tabbedpane.
  219.      *
  220.      * @param l the ChangeListener to remove
  221.      * @see #fireStateChanged
  222.      * @see #addChangeListener
  223.      */
  224.     public void removeChangeListener(ChangeListener l) {
  225.         listenerList.remove(ChangeListener.class, l);
  226.     }
  227.         
  228.     /**
  229.      * Send a ChangeEvent, whose source is this tabbedpane, to
  230.      * each listener.  This method method is called each time 
  231.      * a ChangeEvent is received from the model.
  232.      * 
  233.      * @see #addChangeListener
  234.      * @see EventListenerList
  235.      */
  236.     protected void fireStateChanged() {
  237.         // Guaranteed to return a non-null array
  238.         Object[] listeners = listenerList.getListenerList();
  239.         // Process the listeners last to first, notifying
  240.         // those that are interested in this event
  241.         for (int i = listeners.length-2; i>=0; i-=2) {
  242.             if (listeners[i]==ChangeListener.class) {
  243.                 // Lazily create the event:
  244.                 if (changeEvent == null)
  245.                     changeEvent = new ChangeEvent(this);
  246.                 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
  247.             }          
  248.         }
  249.     }   
  250.  
  251.     /**
  252.      * Returns the model associated with this tabbedpane.
  253.      *
  254.      * @see #setModel
  255.      */
  256.     public SingleSelectionModel getModel() {
  257.         return model;
  258.     }
  259.  
  260.     /**
  261.      * Sets the model to be used with this tabbedpane.
  262.      * @param model the model to be used
  263.      * 
  264.      * @see #getModel
  265.      * @beaninfo
  266.      *       bound: true
  267.      * description: The tabbedpane's SingleSelectionModel.
  268.      */
  269.     public void setModel(SingleSelectionModel model) {
  270.         SingleSelectionModel oldModel = getModel();
  271.  
  272.         if (oldModel != null) {
  273.             oldModel.removeChangeListener(changeListener);
  274.             changeListener = null;
  275.         }
  276.  
  277.         this.model = model;
  278.  
  279.         if (model != null) {
  280.             changeListener = createChangeListener();
  281.             model.addChangeListener(changeListener);
  282.         }
  283.  
  284.         firePropertyChange("model", oldModel, model);
  285.     }
  286.  
  287.     /**
  288.      * Returns the placement of the tabs for this tabbedpane.
  289.      * @see #setPlacement
  290.      */
  291.     public int getTabPlacement() {
  292.         return tabPlacement;
  293.     }
  294.  
  295.     /**
  296.      * Sets the tab placement for this tabbedpane to be either
  297.      * TOP, LEFT, BOTTOM, or RIGHT.
  298.      * @param tabPlacement the placement for the tabs relative to the content
  299.      *
  300.      * @see #getPlacement
  301.      * @beaninfo
  302.      *   preferred: true
  303.      *       bound: true
  304.      * description: The tabbedpane's tab placement.
  305.      *        enum: TOP JTabbedPane.TOP 
  306.      *              LEFT JTabbedPane.LEFT
  307.      *              BOTTOM JTabbedPane.BOTTOM
  308.      *              RIGHT JTabbedPane.RIGHT
  309.      *
  310.      */
  311.     public void setTabPlacement(int tabPlacement) {
  312.         if (tabPlacement != TOP && tabPlacement != LEFT && 
  313.             tabPlacement != BOTTOM && tabPlacement != RIGHT) {
  314.             throw new IllegalArgumentException("illegal tab placement: must be TOP, BOTTOM, LEFT, or RIGHT");
  315.         }
  316.         if (this.tabPlacement != tabPlacement) {
  317.             int old = this.tabPlacement;
  318.             this.tabPlacement = tabPlacement;
  319.             firePropertyChange("tabPlacement", old, tabPlacement);
  320.             invalidate();
  321.         }
  322.     }
  323.  
  324.     /**
  325.      * Returns the currently selected index for this tabbedpane.
  326.      * Returns -1 if there is no currently selected tab.
  327.      *
  328.      * @return the index of the selected tab
  329.      * @see #setSelectedIndex
  330.      */    
  331.     public int getSelectedIndex() {
  332.         return model.getSelectedIndex();
  333.     }
  334.  
  335.     /**
  336.      * Sets the selected index for this tabbedpane.
  337.      *
  338.      * @see #getSelectedIndex
  339.      * @see SingleSelectionModel#setSelectedIndex
  340.      * @beaninfo
  341.      *   preferred: true
  342.      * description: The tabbedpane's selected tab index.
  343.      */
  344.     public void setSelectedIndex(int index) {
  345.         int oldIndex = model.getSelectedIndex();
  346.  
  347.         model.setSelectedIndex(index);
  348.  
  349.         if ((oldIndex >= 0) && (oldIndex != index)) {
  350.             Page oldPage = (Page) pages.elementAt(oldIndex);
  351.             if (accessibleContext != null) {
  352.                 accessibleContext.firePropertyChange(
  353.                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 
  354.                         AccessibleState.SELECTED, null);
  355.             }
  356.         }
  357.         if ((index >= 0) && (oldIndex != index)) {
  358.             Page newPage = (Page) pages.elementAt(index);
  359.             if (accessibleContext != null) {
  360.                 accessibleContext.firePropertyChange(
  361.                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 
  362.                         null, AccessibleState.SELECTED);
  363.             }
  364.         }
  365.     }
  366.  
  367.     /**
  368.      * Returns the currently selected component for this tabbedpane.
  369.      * Returns null if there is no currently selected tab.
  370.      *
  371.      * @return the component corresponding to the selected tab
  372.      * @see #setSelectedComponent
  373.      */   
  374.     public Component getSelectedComponent() { 
  375.         int index = getSelectedIndex();
  376.         if (index == -1) {
  377.             return null;
  378.         }
  379.         return getComponentAt(index);
  380.     }
  381.  
  382.     /**
  383.      * Sets the selected component for this tabbedpane.  This
  384.      * will automatically set the selectedIndex to the index
  385.      * corresponding to the specified component.
  386.      *
  387.      * @see #getSelectedComponent
  388.      * @beaninfo
  389.      *   preferred: true
  390.      * description: The tabbedpane's selected component.
  391.      */
  392.     public void setSelectedComponent(Component c) {
  393.         int index = indexOfComponent(c);
  394.         if (index != -1) {
  395.             setSelectedIndex(index);
  396.         } else {
  397.             throw new IllegalArgumentException("component not found in tabbed pane");
  398.         }
  399.     }
  400.  
  401.     /**
  402.      * Inserts a <i>component</i>, at <i>index</i>, represented by a
  403.      * <i>title</i> and/or <i>icon</i>, either of which may be null. 
  404.      * Uses java.util.Vector internally, see insertElementAt() 
  405.      * for details of insertion conventions. 
  406.      * @param title the title to be displayed in this tab
  407.      * @param icon the icon to be displayed in this tab
  408.      * @param component The component to be displayed when this tab is clicked. 
  409.      * @param tip the tooltip to be displayed for this tab
  410.      * @param index the position to insert this new tab 
  411.      *
  412.      * @see #addTab
  413.      * @see #removeTabAt  
  414.      */
  415.     public void insertTab(String title, Icon icon, Component component, String tip, int index) {
  416.         Icon disabledIcon = null;
  417.         if (icon != null && icon instanceof ImageIcon) {
  418.             disabledIcon = new ImageIcon(
  419.                     GrayFilter.createDisabledImage(
  420.                         ((ImageIcon)icon).getImage()));
  421.         }
  422.         pages.insertElementAt(new Page(this, title != null? title : "", icon, disabledIcon,
  423.                                        component, tip), index);
  424.         invalidate();
  425.         if (pages.size() == 1) {
  426.             setSelectedIndex(0);
  427.         }
  428.         if (!haveRegistered && tip != null) {
  429.             ToolTipManager.sharedInstance().registerComponent(this);
  430.             haveRegistered = true;
  431.         }
  432.  
  433.         if (accessibleContext != null) {
  434.             accessibleContext.firePropertyChange(
  435.                     AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 
  436.                     null, component);
  437.         }
  438.     }
  439.  
  440.     /**
  441.      * Adds a <i>component</i> and <i>tip</i> represented by a <i>title</i> 
  442.      * and/or <i>icon</i>, either of which can be null. 
  443.      * Cover method for insertTab().
  444.      * @param title the title to be displayed in this tab
  445.      * @param icon the icon to be displayed in this tab
  446.      * @param component The component to be displayed when this tab is clicked. 
  447.      * @param tip the tooltip to be displayed for this tab
  448.      * 
  449.      * @see #insertTab
  450.      * @see #removeTabAt  
  451.      */
  452.     public void addTab(String title, Icon icon, Component component, String tip) {
  453.         insertTab(title, icon, component, tip, pages.size()); 
  454.     }
  455.  
  456.     /**
  457.      * Adds a <i>component</i> represented by a <i>title</b> and/or <i>icon</i>, 
  458.      * either of which can be null. 
  459.      * Cover method for insertTab(). 
  460.      * @param title the title to be displayed in this tab
  461.      * @param icon the icon to be displayed in this tab
  462.      * @param component The component to be displayed when this tab is clicked. 
  463.      *
  464.      * @see #insertTab
  465.      * @see #removeTabAt  
  466.      */
  467.     public void addTab(String title, Icon icon, Component component) {
  468.         insertTab(title, icon, component, null, pages.size()); 
  469.     }
  470.  
  471.     /**
  472.      * Adds a <i>component</i> represented by a <i>title</b> and no icon. 
  473.      * Cover method for insertTab().
  474.      * @param title the title to be displayed in this tab
  475.      * @param component The component to be displayed when this tab is clicked. 
  476.      * 
  477.      * @see #insertTab
  478.      * @see #removeTabAt  
  479.      */
  480.     public void addTab(String title, Component component) {
  481.         insertTab(title, null, component, null, pages.size()); 
  482.     }
  483.  
  484.     /**
  485.      * Removes the tab at <i>index</i>.
  486.      * @param index the index of the tab to be removed
  487.      *
  488.      * @see #addTab
  489.      * @see #removeTabAt  
  490.      */
  491.     public void removeTabAt(int index) {            
  492.         // If we are removing the currently selected tab AND
  493.         // it happens to be the last tab in the bunch, then
  494.         // select the previous tab
  495.         int tabCount = getTabCount();
  496.         int selected = getSelectedIndex();
  497.         if (selected >= (tabCount - 1)) {
  498.             setSelectedIndex(selected - 1);
  499.         }
  500.  
  501.         Component oldvalue = getComponentAt(index);
  502.  
  503.         if (accessibleContext != null) {
  504.             accessibleContext.firePropertyChange(
  505.                     AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 
  506.                     oldvalue, null);
  507.         }
  508.  
  509.         pages.removeElementAt(index);
  510.         invalidate();
  511.     }
  512.  
  513.     /**
  514.      * Returns the number of tabs in this tabbedpane.
  515.      */
  516.     public int getTabCount() {
  517.         return pages.size();
  518.     }
  519.  
  520.     /**
  521.      * Returns the number of tab runs currently used to display
  522.      * the tabs. This returns the number of rows if the tabPlacement
  523.      * is TOP or BOTTOM and the number of columns if tabPlacement
  524.      * is LEFT or RIGHT.
  525.      * If there is no UI set on this tabbedpane, then returns 0.
  526.      */
  527.     public int getTabRunCount() {
  528.         if (ui != null) {
  529.             return ((TabbedPaneUI)ui).getTabRunCount(this);
  530.         }
  531.         return 0;
  532.     }
  533.  
  534. // Getters for the Pages
  535.  
  536.     /**
  537.      * Returns the tab title at <i>index</i>.
  538.      *
  539.      * @see #setTitleAt
  540.      */
  541.     public String getTitleAt(int index) {
  542.         return ((Page)pages.elementAt(index)).title;
  543.     }
  544.  
  545.     /**
  546.      * Returns the tab icon at <i>index</i>.
  547.      *
  548.      * @see #setIconAt
  549.      */
  550.     public Icon getIconAt(int index) {
  551.         return ((Page)pages.elementAt(index)).icon;
  552.     }
  553.  
  554.     /**
  555.      * Returns the tab disabled icon at <i>index</i>.
  556.      *
  557.      * @see #setDisabledIconAt
  558.      */
  559.     public Icon getDisabledIconAt(int index) {
  560.         return ((Page)pages.elementAt(index)).disabledIcon;
  561.     }
  562.  
  563.     /**
  564.      * Returns the tab background color at <i>index</i>.
  565.      *
  566.      * @see #setBackgroundAt
  567.      */
  568.     public Color getBackgroundAt(int index) {
  569.         return ((Page)pages.elementAt(index)).getBackground();
  570.     }
  571.  
  572.     /**
  573.      * Returns the tab foreground color at <i>index</i>.
  574.      *
  575.      * @see #setForegroundAt
  576.      */
  577.     public Color getForegroundAt(int index) {
  578.         return ((Page)pages.elementAt(index)).getForeground();
  579.     }
  580.  
  581.     /**
  582.      * Returns whether or not the tab at <i>index</i> is
  583.      * currently enabled.
  584.      *
  585.      * @see #setEnabledAt
  586.      */
  587.     public boolean isEnabledAt(int index) {
  588.         return ((Page)pages.elementAt(index)).isEnabled();
  589.     }
  590.  
  591.     /**
  592.      * Returns the component at <i>index</i>.
  593.      *
  594.      * @see #setComponentAt
  595.      */
  596.     public Component getComponentAt(int index) {
  597.         return ((Page)pages.elementAt(index)).component;
  598.     }
  599.  
  600.     /**
  601.      * Returns the tab bounds at <i>index</i>.
  602.      * If there is no UI set on this tabbedpane, then returns null.
  603.      */
  604.     public Rectangle getBoundsAt(int index) {
  605.         if (ui != null) {
  606.             return ((TabbedPaneUI)ui).getTabBounds(this, index);
  607.         }
  608.         return null;
  609.     }  
  610.  
  611. // Setters for the Pages
  612.  
  613.     /**
  614.      * Sets the title at <i>index</i> to <i>title</i> which can be null. 
  615.      * An internal exception is raised if there is no tab at that index.
  616.      * @param index the tab index where the title should be set 
  617.      * @param title the title to be displayed in the tab
  618.      *
  619.      * @see #getTitleAt
  620.      */
  621.     public void setTitleAt(int index, String title) {
  622.         String oldTitle =((Page)pages.elementAt(index)).title;
  623.         ((Page)pages.elementAt(index)).title = title;
  624.         invalidate();
  625.  
  626.         if ((oldTitle != title) && (accessibleContext != null)) {
  627.             accessibleContext.firePropertyChange(
  628.                     AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 
  629.                     oldTitle, title);
  630.         }
  631.     }
  632.  
  633.     /**
  634.      * Sets the icon at <i>index</i> to <i>icon</i> which can be null.
  635.      * An internal exception is raised if there is no tab at that index. 
  636.      * @param index the tab index where the icon should be set 
  637.      * @param icon the icon to be displayed in the tab
  638.      *
  639.      * @see #getIconAt
  640.      */
  641.     public void setIconAt(int index, Icon icon) {
  642.         Icon oldIcon = ((Page)pages.elementAt(index)).icon;
  643.         ((Page)pages.elementAt(index)).icon = icon;
  644.         invalidate();
  645.  
  646.         AccessibleContext ac = getAccessibleContext();
  647.         // Fire the accessibility Visible data change
  648.         if ((oldIcon != icon) && (accessibleContext != null)) {
  649.             accessibleContext.firePropertyChange(
  650.                     AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 
  651.                     oldIcon, icon);
  652.         }
  653.     }
  654.  
  655.     /**
  656.      * Sets the disabled icon at <i>index</i> to <i>icon</i> which can be null.
  657.      * An internal exception is raised if there is no tab at that index. 
  658.      * @param index the tab index where the disabled icon should be set 
  659.      * @param icon the icon to be displayed in the tab when disabled
  660.      *
  661.      * @see #getDisabledIconAt
  662.      */
  663.     public void setDisabledIconAt(int index, Icon disabledIcon) {
  664.         ((Page)pages.elementAt(index)).disabledIcon = disabledIcon;
  665.         if (!isEnabledAt(index)) {
  666.             invalidate();
  667.         }
  668.     }
  669.  
  670.     /**
  671.      * Sets the background color at <i>index</i> to <i>background</i> 
  672.      * which can be null, in which case the tab's background color
  673.      * will default to the background color of the tabbedpane.
  674.      * An internal exception is raised if there is no tab at that index.
  675.      * @param index the tab index where the background should be set 
  676.      * @param background the color to be displayed in the tab's background
  677.      *
  678.      * @see #getBackgroundAt
  679.      */
  680.     public void setBackgroundAt(int index, Color background) {
  681.         ((Page)pages.elementAt(index)).setBackground(background);
  682.         repaint();
  683.     }
  684.  
  685.     /**
  686.      * Sets the foreground color at <i>index</i> to <i>foreground</i> 
  687.      * which can be null, in which case the tab's foreground color
  688.      * will default to the foreground color of this tabbedpane.
  689.      * An internal exception is raised if there is no tab at that index. 
  690.      * @param index the tab index where the foreground should be set 
  691.      * @param foreground the color to be displayed as the tab's foreground
  692.      *
  693.      * @see #getForegroundAt
  694.      */
  695.     public void setForegroundAt(int index, Color foreground) {
  696.         ((Page)pages.elementAt(index)).setForeground(foreground);
  697.         repaint();
  698.     }
  699.  
  700.     /**
  701.      * Sets whether or not the tab at <i>index</i> is enabled.
  702.      * An internal exception is raised if there is no tab at that index.
  703.      * @param index the tab index which should be enabled/disabled
  704.      * @param enabled whether or not the tab should be enabled
  705.      *
  706.      * @see #isEnabledAt 
  707.      */
  708.     public void setEnabledAt(int index, boolean enabled) {
  709.         ((Page)pages.elementAt(index)).setEnabled(enabled);
  710.         repaint();
  711.     }
  712.  
  713.     /**
  714.      * Sets the component at <i>index</i> to <i>component</i>.  
  715.      * An internal exception is raised if there is no tab at that index.
  716.      * @param index the tab index where this component is being placed
  717.      * @param component the component for the tab
  718.      *
  719.      * @see #getComponentAt  
  720.      */
  721.     public void setComponentAt(int index, Component component) {
  722.         ((Page)pages.elementAt(index)).component = component;
  723.         invalidate();
  724.     }
  725.  
  726.     /**
  727.      * Returns the first tab index with a given <i>title</i>, 
  728.      * Returns -1 if no tab has this title. 
  729.      * @param title the title for the tab
  730.      */
  731.     public int indexOfTab(String title) {
  732.         for(int i = 0; i < getTabCount(); i++) { 
  733.             if (getTitleAt(i).equals(title == null? "" : title)) { 
  734.                 return i;
  735.             }
  736.         }
  737.         return -1; 
  738.     }
  739.  
  740.     /**
  741.      * Returns the first tab index with a given <i>icon</i>.
  742.      * Returns -1 if no tab has this icon.
  743.      * @param icon the icon for the tab
  744.      */
  745.     public int indexOfTab(Icon icon) {
  746.         for(int i = 0; i < getTabCount(); i++) { 
  747.             if (getIconAt(i).equals(icon)) { 
  748.                 return i;
  749.             }
  750.         }
  751.         return -1; 
  752.     }
  753.  
  754.     /**
  755.      * Returns the index of the tab for the specified component.
  756.      * Returns -1 if there is no tab for this component.
  757.      * @param component the component for the tab
  758.      */
  759.     public int indexOfComponent(Component component) {
  760.         for(int i = 0; i < getTabCount(); i++) { 
  761.             if (getComponentAt(i).equals(component)) { 
  762.                 return i;
  763.             }
  764.         }
  765.         return -1; 
  766.     }
  767.  
  768.     public String getToolTipText(MouseEvent event) {
  769.         if (ui != null) {
  770.             int index = ((TabbedPaneUI)ui).tabForCoordinate(this, event.getX(), event.getY());
  771.  
  772.             if (index != -1) {
  773.                 return ((Page)pages.elementAt(index)).tip;
  774.             }
  775.         }
  776.         return super.getToolTipText(event);
  777.     }
  778.  
  779. /////////////////
  780. // Accessibility support
  781. ////////////////
  782.  
  783.     /**
  784.      * Get the AccessibleContext associated with this JComponent
  785.      *
  786.      * @return the AccessibleContext of this JComponent
  787.      */
  788.     public AccessibleContext getAccessibleContext() {
  789.         if (accessibleContext == null) {
  790.             accessibleContext = new AccessibleJTabbedPane();
  791.         }
  792.         return accessibleContext;
  793.     }
  794.  
  795.     /**
  796.      * The class used to obtain the accessible role for this object.
  797.      * <p>
  798.      * Warning: serialized objects of this class will not be compatible with
  799.      * future swing releases.  The current serialization support is appropriate
  800.      * for short term storage or RMI between Swing1.0 applications.  It will
  801.      * not be possible to load serialized Swing1.0 objects with future releases
  802.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  803.      * baseline for the serialized form of Swing objects.
  804.      */
  805.     protected class AccessibleJTabbedPane extends AccessibleJComponent 
  806.         implements AccessibleSelection, ChangeListener {
  807.  
  808.         public AccessibleJTabbedPane() {
  809.             super();
  810.             JTabbedPane.this.model.addChangeListener(this);
  811.         }
  812.  
  813.         public void stateChanged(ChangeEvent e) {
  814.             Object o = e.getSource();
  815.             firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
  816.                                null, o);
  817.         }
  818.  
  819.         public AccessibleRole getAccessibleRole() {
  820.             return AccessibleRole.PAGE_TAB_LIST;
  821.         }
  822.  
  823.         public int getAccessibleChildrenCount() {
  824.             return getTabCount();
  825.         }
  826.  
  827.         public Accessible getAccessibleChild(int i) {
  828.             if (i < 0 || i >= getTabCount()) {
  829.                 return null;
  830.             }
  831.             return (Accessible) pages.elementAt(i);
  832.         }
  833.  
  834.         public AccessibleSelection getAccessibleSelection() {
  835.            return this;
  836.         }
  837.  
  838.         public Accessible getAccessibleAt(Point p) {
  839.             int tab = ((TabbedPaneUI) ui).tabForCoordinate(JTabbedPane.this, 
  840.                                                            p.x, p.y);
  841.             if (tab == -1) {
  842.                 tab = getSelectedIndex();
  843.             }
  844.             return getAccessibleChild(tab);
  845.         }
  846.  
  847.         public int getAccessibleSelectionCount() {
  848.             return 1;
  849.         }
  850.  
  851.         public Accessible getAccessibleSelection(int i) {
  852.             int index = getSelectedIndex();
  853.             if (index == -1) {
  854.                 return null;
  855.             }
  856.             return (Accessible) pages.elementAt(index);
  857.         }
  858.  
  859.         public boolean isAccessibleChildSelected(int i) {
  860.             return (i == getSelectedIndex());
  861.         }
  862.  
  863.         public void addAccessibleSelection(int i) {
  864.            setSelectedIndex(i);
  865.         }
  866.  
  867.         public void removeAccessibleSelection(int i) {
  868.            // can't do
  869.         }
  870.  
  871.         public void clearAccessibleSelection() {
  872.            // can't do
  873.         }
  874.  
  875.         public void selectAllAccessibleSelection() {
  876.            // can't do
  877.         }
  878.     }
  879.  
  880.     private class Page extends AccessibleContext 
  881.         implements Serializable, Accessible, AccessibleComponent {
  882.         String title;
  883.         Color background;
  884.         Color foreground;
  885.         Icon icon;
  886.         Icon disabledIcon;
  887.         JTabbedPane parent;
  888.         Component component;
  889.         String tip;
  890.         boolean enabled = true;
  891.         boolean needsUIUpdate;
  892.  
  893.         Page(JTabbedPane parent, 
  894.              String title, Icon icon, Icon disabledIcon, Component component, String tip) {
  895.             this.title = title;
  896.             this.icon = icon;
  897.             this.disabledIcon = disabledIcon;
  898.             this.parent = parent;
  899.             this.setAccessibleParent(parent);
  900.             this.component = component;
  901.             this.tip = tip;
  902.             if (component instanceof Accessible) {
  903.                 AccessibleContext ac;
  904.                 ac = ((Accessible) component).getAccessibleContext();
  905.                 if (ac != null) {
  906.                     ac.setAccessibleParent(this);
  907.                 }
  908.             }
  909.         }
  910.  
  911.         /////////////////
  912.         // Accessibility support
  913.         ////////////////
  914.  
  915.         public AccessibleContext getAccessibleContext() {
  916.             return this;
  917.         }
  918.  
  919.  
  920.         // AccessibleContext methods
  921.  
  922.         public String getAccessibleName() {
  923.             if (accessibleName != null) {
  924.                 return accessibleName;
  925.             } else if (title != null) {
  926.                 return title;
  927.             }
  928.             return null;
  929.         }
  930.  
  931.         public String getAccessibleDescription() {
  932.             if (accessibleDescription != null) {
  933.                 return accessibleDescription;
  934.             } else if (tip != null) {
  935.                 return tip;
  936.             }
  937.             return null;
  938.         }
  939.  
  940.         public AccessibleRole getAccessibleRole() {
  941.             return AccessibleRole.PAGE_TAB;
  942.         }
  943.  
  944.         public AccessibleStateSet getAccessibleStateSet() {
  945.             AccessibleStateSet states;
  946.             states = parent.getAccessibleContext().getAccessibleStateSet();
  947.             states.add(AccessibleState.SELECTABLE);
  948.             int i = parent.indexOfTab(title);
  949.             if (i == parent.getSelectedIndex()) {
  950.                 states.add(AccessibleState.SELECTED);
  951.             }       
  952.             return states;
  953.         }
  954.  
  955.         public int getAccessibleIndexInParent() {
  956.             return parent.indexOfTab(title);
  957.         }
  958.  
  959.         public int getAccessibleChildrenCount() {
  960.             if (component instanceof Accessible) {
  961.                 return 1;
  962.             } else {
  963.                 return 0;
  964.             }
  965.         }       
  966.  
  967.         public Accessible getAccessibleChild(int i) {
  968.             if (component instanceof Accessible) {
  969.                 return (Accessible) component;
  970.             } else {
  971.                 return null;
  972.             }
  973.         }
  974.  
  975.         public Locale getLocale() {
  976.             return parent.getLocale();
  977.         }
  978.  
  979.         public AccessibleComponent getAccessibleComponent() {
  980.             return this;
  981.         }
  982.  
  983.  
  984.         // AccessibleComponent methods
  985.  
  986.         public Color getBackground() {
  987.             return background != null? background : parent.getBackground();
  988.         }
  989.  
  990.         public void setBackground(Color c) {
  991.             background = c;
  992.         }
  993.  
  994.         public Color getForeground() {
  995.             return foreground != null? foreground : parent.getForeground();
  996.         }
  997.  
  998.         public void setForeground(Color c) {
  999.             foreground = c;
  1000.         }
  1001.  
  1002.         public Cursor getCursor() {
  1003.             return parent.getCursor();
  1004.         }
  1005.  
  1006.         public void setCursor(Cursor c) {
  1007.             parent.setCursor(c);
  1008.         }
  1009.  
  1010.         public Font getFont() {
  1011.             return parent.getFont();
  1012.         }
  1013.  
  1014.         public void setFont(Font f) {
  1015.             parent.setFont(f);
  1016.         }
  1017.  
  1018.         public FontMetrics getFontMetrics(Font f) {
  1019.             return parent.getFontMetrics(f);
  1020.         }
  1021.  
  1022.         public boolean isEnabled() {
  1023.             return enabled;
  1024.         }
  1025.  
  1026.         public void setEnabled(boolean b) {
  1027.             enabled = b;
  1028.         }
  1029.  
  1030.         public boolean isVisible() {
  1031.             return parent.isVisible();
  1032.         }
  1033.  
  1034.         public void setVisible(boolean b) {
  1035.             parent.setVisible(b);
  1036.         }
  1037.  
  1038.         public boolean isShowing() {
  1039.             return parent.isShowing();
  1040.         }
  1041.  
  1042.         public boolean contains(Point p) {
  1043.             Rectangle r = getBounds();
  1044.             return r.contains(p);
  1045.         }
  1046.  
  1047.         public Point getLocationOnScreen() {
  1048.              Point parentLocation = parent.getLocationOnScreen();
  1049.              Point componentLocation = getLocation();
  1050.              componentLocation.translate(parentLocation.x, parentLocation.y);
  1051.              return componentLocation;
  1052.         }
  1053.     
  1054.         public Point getLocation() {
  1055.              Rectangle r = getBounds();
  1056.              return new Point(r.x, r.y);
  1057.         }
  1058.     
  1059.         public void setLocation(Point p) {
  1060.             // do nothing
  1061.         }
  1062.  
  1063.         public Rectangle getBounds() {
  1064.             return parent.getUI().getTabBounds(parent, 
  1065.                                                parent.indexOfTab(title));
  1066.         }
  1067.  
  1068.         public void setBounds(Rectangle r) {
  1069.             // do nothing
  1070.         }
  1071.  
  1072.         public Dimension getSize() {
  1073.             Rectangle r = getBounds();
  1074.             return new Dimension(r.width, r.height);
  1075.         }
  1076.  
  1077.         public void setSize(Dimension d) {
  1078.             // do nothing
  1079.         }
  1080.  
  1081.         public Accessible getAccessibleAt(Point p) {
  1082.             if (component instanceof Accessible) {
  1083.                 return (Accessible) component;
  1084.             } else {
  1085.                 return null;
  1086.             }
  1087.         }
  1088.  
  1089.         public boolean isFocusTraversable() {
  1090.             return false;
  1091.         }
  1092.  
  1093.         public void requestFocus() {
  1094.             // do nothing
  1095.         }
  1096.  
  1097.         public void addFocusListener(FocusListener l) {
  1098.             // do nothing
  1099.         }
  1100.  
  1101.         public void removeFocusListener(FocusListener l) {
  1102.             // do nothing
  1103.         }
  1104.     }
  1105. }
  1106.