org.wings.STabbedPane.java Source code

Java tutorial

Introduction

Here is the source code for org.wings.STabbedPane.java

Source

/*
 * $Id$
 * (c) Copyright 2000 wingS development team.
 *
 * This file is part of wingS (http://j-wings.org).
 *
 * wingS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 *
 * Please see COPYING for the complete licence.
 */

package org.wings;

import java.awt.Color;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.logging.Level;
import javax.swing.DefaultSingleSelectionModel;
import javax.swing.GrayFilter;
import javax.swing.ImageIcon;
import javax.swing.SingleSelectionModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wings.plaf.ComponentCG;
import org.wings.plaf.TabbedPaneCG;
import org.wings.style.AttributeSet;
import org.wings.style.CSSStyleSheet;
import org.wings.style.SimpleAttributeSet;

// fixme: refactorize.
/**
 * A tabbed pane shows one tab (usually a panel) at a moment.
 * The user can switch between the panels.
 *
 * @author <a href="mailto:haaf@mercatis.de">Armin Haaf</a>,
 *    <a href="mailto:andre.lison@general-bytes.com">Andre Lison</a>
 * @version $Revision$
 */
public class STabbedPane extends SContainer implements SSelectionComponent, LowLevelEventListener, ChangeListener {
    /**
     * @see #getCGClassID
     */
    private static final String cgClassID = "TabbedPaneCG";

    /**
     * Where the tabs are placed.
     * @see #setTabPlacement
     */
    protected int tabPlacement = TOP;

    /** The default selection model */
    protected SingleSelectionModel model;

    ArrayList pages = new ArrayList(2);

    /**
     * layout used to render the tabs. Only one tab is on top at a time.
     */
    final private SCardLayout card = new SCardLayout();

    /**
     * container for all tabs. The card layout shows always one on
     * top.
     */
    final private SContainer contents = new SContainer(card);

    /** the maximum tabs per line */
    protected int maxTabsPerLine = -1;

    /** The style of selected tabs */
    protected String selectionStyle;

    /** The dynamic attributes of selected tabs */
    protected AttributeSet selectionAttributes = new SimpleAttributeSet();

    /** used form tabs or links */
    protected boolean showAsFormComponent = false;

    /** @see LowLevelEventListener#isEpochChecking() */
    protected boolean epochChecking = true;

    private Log fLogger = LogFactory.getLog("org.wings.STabbedPane");

    private DynamicResource fStyleSheet = null;

    /**
     * Number of selected tab.
     */
    protected int selectedIndex = 0;

    /**
     * the newly selected index during a 
     * lowlevelevent
     */
    private int lleChangedIndex = -1;

    /**
     * Creates a new empty Tabbed Pane with the tabs at the top.
     * @see #addTab
     */
    public STabbedPane() {
        this(TOP);
    }

    /**
     * Creates an empty TabbedPane with the specified tab placement
     * of either: TOP, BOTTOM, LEFT, or RIGHT.
     * @param tabPlacement the placement for the tabs relative to the content
     * @see #addTab
     */
    public STabbedPane(int tabPlacement) {
        super();

        setTabPlacement(tabPlacement);
        setBackground(new Color(204, 204, 204));
        setSelectionBackground(new Color(170, 170, 255));
        setFont(new SFont("Verdana,Arial,Helvetica,sans serif", SConstants.PLAIN, 10));

        super.addComponent(contents, null, 0);
        setModel(new DefaultSingleSelectionModel());
    }

    /**
     * @param style the style of selected cells
     */
    public void setSelectionStyle(String selectionStyle) {
        this.selectionStyle = selectionStyle;
    }

    /**
     * @return the style of selected cells.
     */
    public String getSelectionStyle() {
        return selectionStyle;
    }

    /**
     * Set a selectionAttribute.
     * @param name the selectionAttribute name
     * @param value the selectionAttribute value
     */
    public void setSelectionAttribute(String name, String value) {
        boolean changed = selectionAttributes.contains(name);
        selectionAttributes.put(name, value);

        if (changed)
            reload(ReloadManager.RELOAD_STYLE);
    }

    /**
     * return the value of an selectionAttribute.
     * @param name the selectionAttribute name
     */
    public String getSelectionAttribute(String name) {
        return selectionAttributes.get(name);
    }

    /**
     * remove an selectionAttribute
     * @param name the selectionAttribute name
     */
    public String removeSelectionAttribute(String name) {
        if (selectionAttributes.contains(name)) {
            String value = selectionAttributes.remove(name);

            reload(ReloadManager.RELOAD_STYLE);

            return value;
        }

        return null;
    }

    /**
     * Set the selectionAttributes.
     * @param selectionAttributes the selectionAttributes
     */
    public void setSelectionAttributes(AttributeSet selectionAttributes) {
        if (selectionAttributes == null)
            throw new IllegalArgumentException("null not allowed");

        if (!this.selectionAttributes.equals(selectionAttributes)) {
            this.selectionAttributes = selectionAttributes;
            reload(ReloadManager.RELOAD_STYLE);
        }
    }

    /**
     * @return the current selectionAttributes
     */
    public AttributeSet getSelectionAttributes() {
        return selectionAttributes;
    }

    /**
     * Set the background color of the selected
     * tab. This is ignored, if <i>showAsFormComponent</i> is on.
     * @param c the new background color
     */
    public void setSelectionBackground(Color color) {
        setSelectionAttribute("background-color", CSSStyleSheet.getAttribute(color));
    }

    /**
     * Return the background color.
     * @return the background color
     */
    public Color getSelectionBackground() {
        return CSSStyleSheet.getBackground(selectionAttributes);
    }

    /**
     * Set the foreground color.
     * @param c the new foreground color
     */
    public void setSelectionForeground(Color color) {
        setSelectionAttribute("color", CSSStyleSheet.getAttribute(color));
    }

    /**
     * Return the foreground color.
     * @return the foreground color
     */
    public Color getSelectionForeground() {
        return CSSStyleSheet.getForeground(selectionAttributes);
    }

    /**
     * Add a listener to the list of change listeners.
     * ChangeListeners are notified, when the tab selection changes.
     *
     * @param cl add to listener list
     */
    public void addChangeListener(ChangeListener cl) {
        addEventListener(ChangeListener.class, cl);
    }

    /**
     * Remove listener from the list of change listeners.
     * ChangeListeners are notified, when the tab selection changes.
     *
     * @param cl remove from listener list
     */
    public void removeChangeListener(ChangeListener cl) {
        removeEventListener(ChangeListener.class, cl);
    }

    /**
     * Fire ChangeEvents at all registered change listeners.
     */
    protected void fireStateChanged() {
        ChangeEvent event = null;

        // maybe the better way to do this is to user the getListenerList
        // and iterate through all listeners, this saves the creation of
        // an array but it must cast to the apropriate listener
        Object[] listeners = getListenerList();
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == ChangeListener.class) {
                // Lazily create the event:
                if (event == null)
                    event = new ChangeEvent(this);
                ((ChangeListener) listeners[i + 1]).stateChanged(event);
            }
        }
    }

    /**
     * Returns the placement of the tabs for this tabbedpane.
     * @see #setTabPlacement
     */
    public int getTabPlacement() {
        return tabPlacement;
    }

    /**
     * Sets the tab placement for this tabbedpane.
     * Possible values are:<ul>
     * <li>SConstants.TOP
     * <li>SConstants.BOTTOM
     * <li>SConstants.LEFT
     * <li>SConstants.RIGHT
     * </ul>
     * The default value is TOP.
     *
     * @param tabPlacement the placement for the tabs relative to the content
     *
     */
    public void setTabPlacement(int tabPlacement) {
        if (tabPlacement != TOP && tabPlacement != LEFT && tabPlacement != BOTTOM && tabPlacement != RIGHT) {
            throw new IllegalArgumentException("illegal tab placement: must be TOP, BOTTOM, LEFT, or RIGHT");
        }

        this.tabPlacement = tabPlacement;
        if (fStyleSheet != null)
            fStyleSheet.invalidate();
    }

    /**
     * Returns the model associated with this tabbedpane.
     *
     * @see #setModel
     */
    public SingleSelectionModel getModel() {
        return model;
    }

    /**
     * Sets the model to be used with this tabbedpane.
     * @param model the model to be used
     *
     * @see #getModel
     */
    public void setModel(SingleSelectionModel model) {
        this.model = model;
        model.addChangeListener(this);
    }

    /**
     * Returns the currently selected index for this tabbedpane.
     * Returns -1 if there is no currently selected tab.
     *
     * @return the index of the selected tab
     * @see #setSelectedIndex
     */
    public int getSelectedIndex() {
        return model.getSelectedIndex();
    }

    /**
     * Sets the selected index for this tabbedpane.
     *
     * @see #getSelectedIndex
     * @see SingleSelectionModel#setSelectedIndex
     * @beaninfo
     *   preferred: true
     * description: The tabbedpane's selected tab index.
     */
    public void setSelectedIndex(int index) {
        model.setSelectedIndex(index);
    }

    /**
     * Returns the currently selected component for this tabbedpane.
     * Returns null if there is no currently selected tab.
     *
     * @return the component corresponding to the selected tab
     * @see #setSelectedComponent
     */
    public SComponent getSelectedComponent() {
        int index = getSelectedIndex();
        if (index == -1) {
            return null;
        }
        return getTabAt(index);
    }

    /**
     * Sets the selected component for this tabbedpane.  This
     * will automatically set the selectedIndex to the index
     * corresponding to the specified component.
     *
     * @see #getSelectedComponent
     * @beaninfo
     *   preferred: true
     * description: The tabbedpane's selected component.
     */
    public void setSelectedComponent(SComponent c) {
        int index = indexOfComponent(c);
        if (index != -1) {
            setSelectedIndex(index);
        } else {
            throw new IllegalArgumentException("component not found in tabbed pane");
        }
    }

    /**
     * Returns the index of the tab for the specified component.
     * Returns -1 if there is no tab for this component.
     * @param component the component for the tab
     */
    public int indexOfComponent(SComponent component) {
        for (int i = 0; i < getTabCount(); ++i) {
            if (((Page) pages.get(i)).component.equals(component)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Returns the number of tabs in this tabbedpane.
     *
     * @return an int specifying the number of tabbed pages
     */
    public int getTabCount() {
        return pages.size();
    }

    /**
     * Inserts a <i>component</i>, at <i>index</i>, represented by a
     * <i>title</i> and/or <i>icon</i>, either of which may be null.
     * Uses java.util.ArrayList internally, see insertElementAt()
     * for details of insertion conventions.
     * @param title the title to be displayed in this tab
     * @param icon the icon to be displayed in this tab
     * @param component The component to be displayed when this tab is clicked.
     * @param tip the tooltip to be displayed for this tab
     * @param index the position to insert this new tab
     *
     * @see #addTab
     * @see #removeTabAt
     */
    public void insertTab(String title, SIcon icon, SComponent component, String tip, int index) {

        SIcon disabledIcon = null;

        if (icon != null && icon instanceof SImageIcon) {
            disabledIcon = new SImageIcon(
                    new ImageIcon(GrayFilter.createDisabledImage(((SImageIcon) icon).getImage())));
        }

        String t = (title != null) ? title : "";

        Page p = new Page(t, icon, disabledIcon, component, tip);
        pages.add(index, p);

        contents.addComponent(p.component, p.component.getComponentId());

        if (pages.size() == 1) {
            setSelectedIndex(0);
        }
    }

    /**
     * Adds a <i>component</i> and <i>tip</i> represented by a <i>title</i>
     * and/or <i>icon</i>, either of which can be null.
     * Cover method for insertTab().
     * @param title the title to be displayed in this tab
     * @param icon the icon to be displayed in this tab
     * @param component The component to be displayed when this tab is clicked.
     * @param tip the tooltip to be displayed for this tab
     *
     * @see #insertTab
     * @see #removeTabAt
     */
    public void addTab(String title, SIcon icon, SComponent component, String tip) {
        insertTab(title, icon, component, tip, pages.size());
    }

    /**
     * Adds a <i>component</i> represented by a <i>title</i> and/or <i>icon</i>,
     * either of which can be null.
     * Cover method for insertTab().
     * @param title the title to be displayed in this tab
     * @param icon the icon to be displayed in this tab
     * @param component The component to be displayed when this tab is clicked.
     *
     * @see #insertTab
     * @see #removeTabAt
     */
    public void addTab(String title, SIcon icon, SComponent component) {
        insertTab(title, icon, component, null, pages.size());
    }

    /**
     * Adds a <i>component</i> represented by a <i>title</i> and no icon.
     * Cover method for insertTab().
     * @param title the title to be displayed in this tab
     * @param component The component to be displayed when this tab is clicked.
     *
     * @see #insertTab
     * @see #removeTabAt
     */
    public void addTab(String title, SComponent component) {
        insertTab(title, null, component, null, pages.size());
    }

    /**
     * Adds a <i>component</i> with the specified tab title.
     * Cover method for insertTab().
     * @param title the title to be displayed in this tab
     * @param component The component to be displayed when this tab is clicked.
     *
     * @see #insertTab
     * @see #removeTabAt
     */
    public SComponent add(String title, SComponent component) {
        addTab(title, component);
        return component;
    }

    /**
     * Adds a <i>component</i> at the specified tab index.  If constraints
     * is a String or an Icon, it will be used for the tab title,
     * otherwise the component's name will be used as the tab title.
     * Cover method for insertTab().
     * @param component The component to be displayed when this tab is clicked.
     * @constraint the object to be displayed in the tab
     * @param index the position to insert this new tab
     *
     * @see #insertTab
     * @see #removeTabAt
     */
    public SComponent addComponent(SComponent component, Object constraints) {
        return addComponent(component, constraints, pages.size());
    }

    /**
     * Adds a <i>component</i> at the specified tab index.  If constraints
     * is a String or an Icon, it will be used for the tab title,
     * otherwise the component's name will be used as the tab title.
     * Cover method for insertTab().
     * @param component The component to be displayed when this tab is clicked.
     * @constraint the object to be displayed in the tab
     * @param index the position to insert this new tab
     *
     * @see #insertTab
     * @see #removeTabAt
     */
    public SComponent addComponent(SComponent component, Object constraints, int index) {
        SIcon icon = constraints instanceof SIcon ? (SIcon) constraints : null;
        String title = constraints instanceof String ? (String) constraints : null;
        insertTab(title, icon, component, null, Math.min(index, pages.size()));

        return component;
    }

    //    /**
    //     * Removes the tab at <i>index</i>.
    //     * After the component associated with <i>index</i> is removed,
    //     * its visibility is reset to true to ensure it will be visible
    //     * if added to other containers.
    //     * @param index the index of the tab to be removed
    //     *
    //     * @see #addTab
    //     * @see #insertTab
    //     */
    //    public void removeTabAt(int index) {
    //        // If we are removing the currently selected tab AND
    //        // it happens to be the last tab in the bunch, then
    //        // select the previous tab
    //        int tabCount = getTabCount();
    //        int selected = getSelectedIndex();
    //        if ( selected >= (tabCount - 1) ) {
    //            setSelectedIndex(selected - 1);
    //        }
    //
    //        removePageAt(index);
    //    }

    /**
     * Removes the tab at <i>index</i>.
     * After the component associated with <i>index</i> is removed,
     * its visibility is reset to true to ensure it will be visible
     * if added to other containers.
     *
     * @param index the index of the tab to be removed
     * @see #addTab
     * @see #insertTab
     */
    public void removeTabAt(int index) {
        // If we are removing the currently selected tab AND
        // it happens to be the last tab in the bunch, then
        // select the previous tab, except it is the last one.
        // TODO: how is a tabbedPane with no tabs rendered?
        int newTabCount = getTabCount() - 1;
        int selected = getSelectedIndex();
        removePageAt(index);
        if (newTabCount > 0 && selected != -1) {
            if (selected >= (newTabCount)) {
                /* last tab was selected and maybe removed, so try to find a 
                 * tab to select before
                 */
                int decrement = 1;

                while (newTabCount > decrement && !isEnabledAt(newTabCount - decrement)) {
                    decrement++;
                }
                if (isEnabledAt(newTabCount - decrement)) {
                    setSelectedIndex(newTabCount - decrement);
                } else {
                    // only disabled tabs left
                    setSelectedIndex(-1);
                }
            } else {
                int newTab = selected;
                /* some tab was selected and maybe removed, so try to find a 
                 * tab to select behind or before the removed one
                 */
                while ((newTabCount - 1 > newTab) && !isEnabledAt(newTab)) {
                    newTab++;
                }
                if (isEnabledAt(newTab)) {
                    setSelectedIndex(newTab);
                    getSelectedComponent().setVisible(true);
                } else {
                    // see if there is an enabled tab before
                    newTab = selected - 1;
                    if (newTab == -1) {
                        setSelectedIndex(-1);
                        return;
                    }
                    while (newTab > 0 && !isEnabledAt(newTab)) {
                        newTab--;
                    }
                    if (isEnabledAt(newTab)) {
                        setSelectedIndex(newTab);
                        getSelectedComponent().setVisible(true);
                    } else {
                        // only disabled tabs left
                        setSelectedIndex(-1);
                    }
                }

            }
        } else {
            // no tab left
            setSelectedIndex(-1);
        }
    }

    public void removeAllTabs() {
        while (getTabCount() != 0) {
            removeTabAt(0);
        }
    }

    /**
     * Removes the tab which corresponds to the specified component.
     *
     * @param component the component to remove from the tabbedpane
     * @see #addTab
     * @see #removeTabAt
     */
    public void remove(SComponent component) {
        int index = indexOfComponent(component);
        if (index != -1) {
            removeTabAt(index);
        }
    }

    /**
     * Sets the maximum tabs per line. tabs <= 0: No maximum.
     */
    public void setMaxTabsPerLine(int tabs) {
        maxTabsPerLine = tabs;
    }

    /**
     * Returns the maximum tabs per line.
     */
    public int getMaxTabsPerLine() {
        return maxTabsPerLine;
    }

    /**
     * Returns the tab title at <i>index</i>.
     *
     * @see #setTitleAt
     */
    public String getTitleAt(int index) {
        return ((Page) pages.get(index)).title;
    }

    /**
     * Returns the tab icon at <i>index</i>.
     *
     * @see #setIconAt
     */
    public SIcon getIconAt(int index) {
        return ((Page) pages.get(index)).icon;
    }

    /**
     * Returns the tab disabled icon at <i>index</i>.
     *
     * @see #setDisabledIconAt
     */
    public SIcon getDisabledIconAt(int index) {
        return ((Page) pages.get(index)).disabledIcon;
    }

    /**
     * Returns the tab background color at <i>index</i>.
     *
     * @see #setBackgroundAt
     */
    public Color getBackgroundAt(int index) {
        return ((Page) pages.get(index)).background;
    }

    /**
     * Returns the tab foreground color at <i>index</i>.
     *
     * @see #setForegroundAt
     */
    public Color getForegroundAt(int index) {
        return ((Page) pages.get(index)).foreground;
    }

    /**
     * Returns the tab style at <i>index</i>.
     *
     * @see #setStyleAt
     */
    public String getStyleAt(int index) {
        return ((Page) pages.get(index)).style;
    }

    /**
     * Returns whether or not the tab at <i>index</i> is
     * currently enabled.
     *
     * @see #setEnabledAt
     */
    public boolean isEnabledAt(int index) {
        return ((Page) pages.get(index)).enabled;
    }

    /**
     * Returns the component at <i>index</i>.
     *
     * @see #setComponentAt
     * @deprecated use {@link #getComponentAt} instead (swing conformity)
     */
    public SComponent getTabAt(int index) {
        return ((Page) pages.get(index)).component;
    }

    /**
     * Returns the component at <i>index</i>.
     *
     * @see #setComponentAt
     * @deprecated use {@link #getComponent(int)} instead for swing conformity
     */
    public SComponent getComponentAt(int index) {
        return ((Page) pages.get(index)).component;
    }

    /**
     * Sets the title at <i>index</i> to <i>title</i> which can be null.
     * An internal exception is raised if there is no tab at that index.
     * @param index the tab index where the title should be set
     * @param title the title to be displayed in the tab
     *
     * @see #getTitleAt
     */
    public void setTitleAt(int index, String title) {
        ((Page) pages.get(index)).title = title;
    }

    /**
     * Sets the icon at <i>index</i> to <i>icon</i> which can be null.
     * An internal exception is raised if there is no tab at that index.
     * @param index the tab index where the icon should be set
     * @param icon the icon to be displayed in the tab
     *
     * @see #getIconAt
     */
    public void setIconAt(int index, SIcon icon) {
        ((Page) pages.get(index)).icon = icon;
    }

    /**
     * Sets the disabled icon at <i>index</i> to <i>icon</i> which can be null.
     * An internal exception is raised if there is no tab at that index.
     * @param index the tab index where the disabled icon should be set
     * @param icon the icon to be displayed in the tab when disabled
     *
     * @see #getDisabledIconAt
     */
    public void setDisabledIconAt(int index, SIcon disabledIcon) {
        ((Page) pages.get(index)).disabledIcon = disabledIcon;
    }

    /**
     * Sets the background color at <i>index</i> to <i>background</i>
     * which can be null, in which case the tab's background color
     * will default to the background color of the tabbedpane.
     * An internal exception is raised if there is no tab at that index.
     * @param index the tab index where the background should be set
     * @param background the color to be displayed in the tab's background
     *
     * @see #getBackgroundAt
     */
    public void setBackgroundAt(int index, Color background) {
        ((Page) pages.get(index)).background = background;
    }

    /**
     * Sets the foreground color.
     * @param foreground the color to be displayed as the tab's foreground
     * @see #getForeground
     */
    public void setForeground(Color foreground) {
        super.setForeground(foreground);
        if (fStyleSheet != null)
            fStyleSheet.invalidate();
    }

    /**
     * Sets the background color.
     * @param background the color to use as background.
     * @see #getForeground
     */
    public void setBackground(Color background) {
        super.setBackground(background);
        if (fStyleSheet != null)
            fStyleSheet.invalidate();
    }

    /**
     * Sets the foreground color at <i>index</i> to <i>foreground</i>
     * which can be null, in which case the tab's foreground color
     * will default to the foreground color of this tabbedpane.
     * An internal exception is raised if there is no tab at that index.
     * @param index the tab index where the foreground should be set
     * @param foreground the color to be displayed as the tab's foreground
     *
     * @see #getForegroundAt
     */
    public void setForegroundAt(int index, Color foreground) {
        ((Page) pages.get(index)).foreground = foreground;
    }

    /**
     * Sets the style at <i>index</i> to <i>style</i>
     * which can be null, in which case the tab's style
     * will default to the style of this tabbedpane.
     * An internal exception is raised if there is no tab at that index.
     * @param index the tab index where the style should be set
     * @param foreground the style to be used as the tab's style
     *
     * @see #getStyleAt
     */
    public void setStyleAt(int index, String style) {
        ((Page) pages.get(index)).style = style;
    }

    /**
     * Sets whether or not the tab at <i>index</i> is enabled.
     * An internal exception is raised if there is no tab at that index.
     * @param index the tab index which should be enabled/disabled
     * @param enabled whether or not the tab should be enabled
     *
     * @see #isEnabledAt
     */
    public void setEnabledAt(int index, boolean enabled) {
        ((Page) pages.get(index)).enabled = enabled;
    }

    /**
     * Set the tooltip text for tab at <i>index</i>
     * @param index set the tooltip for this tab
     */
    public void setToolTipTextAt(int index, String toolTip) {
        ((Page) pages.get(index)).toolTip = toolTip;
    }

    /**
     * Get the tooltip text from tab at <i>index</i>
     * @return the text or <i>null</i> if not set.
     */
    public String getToolTipTextAt(int index) {
        return ((Page) pages.get(index)).toolTip;
    }

    /**
     * Sets the component at <i>index</i> to <i>component</i>.
     * An internal exception is raised if there is no tab at that index.
     * @param index the tab index where this component is being placed
     * @param component the component for the tab
     *
     * @see #getComponentAt
     */
    public void setComponentAt(int index, SComponent component) {
        Page page = (Page) pages.get(index);
        if (component != page.component) {
            if (page.component != null) {
                contents.remove(page.component);
            }
            page.component = component;
            contents.addComponent(page.component, page.component.getComponentId());
            if (getSelectedIndex() == index)
                card.show(component);
        }
    }

    /**
     * Returns the first tab index with a given <i>title</i>,
     * Returns -1 if no tab has this title.
     * @param title the title for the tab
     */
    public int indexOfTab(String title) {
        for (int i = 0; i < getTabCount(); i++) {
            if (getTitleAt(i).equals((title == null) ? "" : title)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Returns the first tab index with a given <i>icon</i>.
     * Returns -1 if no tab has this icon.
     * @param icon the icon for the tab
     */
    public int indexOfTab(SIcon icon) {
        for (int i = 0; i < getTabCount(); i++) {
            if (getIconAt(i).equals(icon)) {
                return i;
            }
        }
        return -1;
    }

    //    private void removePageAt(int i) {
    //        pages.remove(i);
    //        contents.remove(((Page)pages.get(i)).component);
    //    }

    private void removePageAt(int i) {
        contents.remove(((Page) pages.get(i)).component);
        pages.remove(i);
    }

    /**
     * Lightweight container for tab properties.
     */
    private static class Page implements Serializable {
        public String title;
        public String toolTip;
        public Color foreground;
        public Color background;
        public SIcon icon;
        public SIcon disabledIcon;
        public boolean enabled = true;
        public String style;
        public SComponent component;

        public Page(String title, SIcon icon, SIcon disabledIcon, SComponent component, String tip) {
            this.title = title;
            this.toolTip = tip;
            this.icon = icon;
            this.disabledIcon = disabledIcon;
            this.component = component;
        }
    }

    /**
     * Set display mode (href or form-component).
     * An AbstractButton can appear as HTML-Form-Button or as 
     * HTML-HREF. If button is inside a {@link org.wings.SForm} the default
     * is displaying it as html form button.
     * Setting <i>showAsFormComponent</i> to <i>false</i> will
     * force displaying as href even if button is inside 
     * a form.
     * @param showAsFormComponent if true, display as link, if false as html form component.
     */
    public void setShowAsFormComponent(boolean showAsFormComponent) {
        if (this.showAsFormComponent == showAsFormComponent)
            return;
        this.showAsFormComponent = showAsFormComponent;
        if (fStyleSheet != null)
            fStyleSheet.invalidate();
    }

    /**
       * Test, what display method is set.
       * @see #setShowAsFormComponent(boolean)
       * @return true, if displayed as link, false when displayed as html form component.
       */
    public boolean getShowAsFormComponent() {
        return showAsFormComponent && getResidesInForm();
    }

    /**
     * Set the parent frame of this tabbed pane
     * @param f the parent frame.
     */
    public void setParentFrame(SFrame f) {
        super.setParentFrame(f);
        contents.setParentFrame(f);
        ComponentCG cg = this.getCG();
        if (f != null && cg instanceof org.wings.plaf.TabbedPaneCG) {
            fLogger.trace("STabbedPane.setParentFrame, Installing stylesheet ...");
            fStyleSheet = ((org.wings.plaf.TabbedPaneCG) cg).installStyleSheet(this);
        }
    }

    public String getCGClassID() {
        return cgClassID;
    }

    public void setCG(TabbedPaneCG cg) {
        super.setCG(cg);
    }

    /**
     * Tab was clicked.
     * @see LowLevelEventListener#processLowLevelEvent(String, String[])
     */
    public void processLowLevelEvent(String name, String[] values) {
        /*
        if ( !name.startsWith(getLowLevelEventId()) ) {
        return;
        }
        */
        for (int i = 0; i < values.length; ++i) {
            try {
                int index = new Integer(values[i]).intValue();
                if (index < 0 || index >= pages.size())
                    continue;

                /* prevent clever users from showing
                 * disabled tabs
                 */
                if (((Page) pages.get(index)).enabled) {
                    lleChangedIndex = index;
                    SForm.addArmedComponent(this);
                    return;
                }
            } catch (NumberFormatException nfe) {
                continue;
            }
        }
    }

    /**
     * Does nothin'.
     */
    public void fireIntermediateEvents() {
    }

    /**
     * Sets selection and fire changeevents, if user changed 
     * tab selection.
     */
    public void fireFinalEvents() {
        if (lleChangedIndex > -1)
            setSelectedIndex(lleChangedIndex);
        lleChangedIndex = -1;
    }

    /** @see LowLevelEventListener#isEpochChecking() */
    public boolean isEpochChecking() {
        return epochChecking;
    }

    /** @see LowLevelEventListener#isEpochChecking() */
    public void setEpochChecking(boolean epochChecking) {
        this.epochChecking = epochChecking;
    }

    /**
     * When tab selection changed.
     * @see ChangeListener#stateChanged(ChangeEvent)
     */
    public void stateChanged(ChangeEvent ce) {
        final int index = model.getSelectedIndex();
        if (index >= pages.size() || index == -1)
            return;
        card.show(((Page) pages.get(index)).component);

        reload(ReloadManager.RELOAD_CODE);
        fireStateChanged();
    }

}

/*
 * Local variables:
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * compile-command: "ant -emacs -find build.xml"
 * End:
 */