/* class JTabbedPane
 *
 * Copyright (C) 2001-2003  R M Pitman
 *
 * This library 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.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * Modified Jul 14, 2003 by Tadpole Computer, Inc.
 * Modifications Copyright 2003 by Tadpole Computer, Inc.
 *
 * Modifications are hereby licensed to all parties at no charge under
 * the same terms as the original.
 *
 * Modified to allow tabs to be focus traversable.  Also added the
 * setEnabledAt and isEnabledAt methods.
 */

package charvax.swing;

import charva.awt.*;
import charva.awt.event.ActionEvent;
import charva.awt.event.ActionListener;

import java.util.Enumeration;
import java.util.Vector;

/**
 * A component that lets the user display one of a set of components
 * (usually Panels) at a time.
 * The management of the tab-selection has to be performed outside of this
 * component because it is possible that the currently selected tab does
 * not contain any focusTraversable components.  In this case, this JTabbedPane
 * will never get the keyboard focus.
 */
public class JTabbedPane
        extends Container {

    /**
     * Construct a JTabbedPane.
     */
    public JTabbedPane() {
        _insets = new Insets(2, 1, 1, 1);
        _layoutMgr = new BorderLayout();
    }

    /**
     * Add the specified component to the tabbed pane. If
     * <code>constraints</code> is a String, it will be used as the tab's
     * title; otherwise the component's name will be used as the title.
     */
    public void add(Component component_, Object constraints_) {

        String label;
        if (constraints_ instanceof String)
            label = (String) constraints_;
        else
            label = component_.getName();

        int labelno = _tabComponents.size() + 1;
        String keylabel = "F" + labelno;
        addTab(label, null, component_, keylabel);
    }

    /**
     * Add a new tab with the specified component, title and function-key.
     *
     * @param title_     The title of this tab.
     * @param icon_      An icon representing the component being added.  This is
     *                   not used and is just for compatibility with Swing.  Pass null for this
     *                   parameter.
     * @param component_ The component to be added in this tab.
     * @param keylabel_  A String representing the key that must be pressed to
     *                   select this tab.
     */
    public void addTab(String title_, Object icon_, Component component_, String keylabel_) {

        TabButton tb = new TabButton(title_, component_, keylabel_);
        _tabComponents.add(component_);
        // arrange for our TabButton to be in the focus list...
        _components.add(_tabs.size(), tb);
        tb.setParent(this);
        _tabs.add(tb);
        _buttongroup.add(tb);

        if (_selectedIndex == -1) {
            setSelectedIndex(0);
            tb.setSelected(true);
        }

        /* If this component is already displayed, generate a PaintEvent
         * and post it onto the queue.
         */
        else if (isDisplayed()) {
            repaint();
        }
    }

    /**
     * 
     */
    public void setSelectedIndex(int index_) {
        if (index_ >= _tabComponents.size())
            throw new IndexOutOfBoundsException();

        if (index_ == _selectedIndex)
            return;

        /* Remove the previously-selected component from the container.
         */
        if (_selectedIndex != -1) {
            super.remove((Component) _tabComponents.elementAt(_selectedIndex));
            //_components.remove(_tabComponents)
            //_currentFocus = null;
        }

        /* Make the TabButton selected as well
         */
        TabButton tb = (TabButton) _tabs.elementAt(index_);
        tb.setSelected(true);

        /* Add the newly-selected component to the container.
         */
        Component selected = (Component) _tabComponents.elementAt(index_);
        super.add(selected);
        super.validate();
        //_layoutMgr.doLayout(this);

        _selectedIndex = index_;

        /* If this component is already displayed, generate a PaintEvent
         * and post it onto the queue.
         */
        repaint();
    }

    public int getSelectedIndex() {
        return _selectedIndex;
    }

    public void setSelectedComponent(Component component_) {

        int index = _tabComponents.indexOf(component_);
        setSelectedIndex(index);
    }

    /**
     * Override the method in Container.
     */
    public Dimension getSize() {
        return minimumSize();
    }

    /**
     * Override the method in Container.
     */
    public Dimension minimumSize() {

        if (super.isValid())
            return _minimumSize;

        /* Scan through the components in each tab and determine
         * the smallest rectangle that will enclose all of them.
         */
        int width = 0;
        int height = 0;
        Enumeration e = _tabComponents.elements();
        while (e.hasMoreElements()) {
            Component c = (Component) e.nextElement();
            Dimension size = c.minimumSize();
            if (size.width > width)
                width = size.width;
            if (size.height > height)
                height = size.height;
        }

        /* Now scan through the titles of the tabs, and determine the width
         * that all of them will fit into.
         */
        int tabwidth = 0;
        e = _tabs.elements();
        while (e.hasMoreElements()) {
            tabwidth += ((TabButton) e.nextElement()).getWidth();
        }
        tabwidth += 2;
        if (tabwidth > width)
            width = tabwidth;

        /* Take into account the border and the height of the tabs.
         */
        _minimumSize = new Dimension(width + _insets.left + _insets.right,
                height + _insets.top + _insets.bottom);

        _isValid = true;
        return _minimumSize;
    }

    public void draw() {
        /* Get the absolute origin of this component.
         */
        Point origin = getLocationOnScreen();
        Point relative = new Point(0, 0);

        Toolkit term = Toolkit.getDefaultToolkit();

        int colorpair = getCursesColor();

        /* Draw the enclosing frame
         */
        Dimension size = getSize();
        term.blankBox(origin, size, colorpair);
        term.drawBox(origin.addOffset(0, 1),
                new Dimension(size.width, size.height - 1),
                colorpair);

        /* Draw each of the tabs
         */
        int hoffset = 1;
        Enumeration e = _tabs.elements();
        for (int i = 0; e.hasMoreElements(); i++) {
            TabButton tb = (TabButton) e.nextElement();

            tb.setLocation(relative.addOffset(hoffset, 0));
            tb.draw();
            hoffset += tb.getWidth();
        }

        /* Now draw the selected component.
         */
        if (_selectedIndex != -1) {
            Component component =
                    (Component) _tabComponents.elementAt(_selectedIndex);

            /* Note that we draw the component even if isVisible() would be
             * false; it doesn't make sense to make a component invisible
             * in a JTabbedPane.
             */
            component.draw();
        }
    }

    /**
     * Removes the tab and component which corresponds to the specified
     * index.
     */
    public void remove(int index) {
        //save this just in case
        Component selected = (Component) _tabComponents.elementAt(0);
        _tabComponents.remove(index);
        super.remove((Component) _tabs.elementAt(index));
        _tabs.remove(index);

        if (getSelectedIndex() == index) {
            if (index - 1 < 0) {
                if (getTabCount() > 0) {
                    setSelectedIndex(0);
                } else { //nothing left, this causes exception
                    _selectedIndex = -1;
                    super.remove(selected);
                    super.validate();
                }
            } else {
                setSelectedIndex(index - 1);
            }
        }

        if (isDisplayed()) {
            repaint();
        }
    }

    /**
     * Returns the first tab index with the specified title, or
     * -1 if no tab has the title.
     */
    public int indexOfTab(String title) {
        for (int i = 0; i < _tabs.size(); i++) {
            if (title.equals(((TabButton) _tabs.elementAt(i)).getText())) {
                return (i);
            }
        }
        return (-1);
    }

    /**
     * Returns the title of the tab with the specified index.
     */
    public String getTitleAt(int index) {
        return ((TabButton) _tabs.elementAt(index)).getText();
    }

    /**
     * Sets the title of the tab with the specified index.
     */
    public void setTitleAt(int index, String title) {
        ((TabButton) _tabs.elementAt(index)).setText(title);
    }


    /**
     * Make the tab at the specified index enabled.
     */
    public void setEnabledAt(int index, boolean enabled) {
        ((TabButton) _tabs.elementAt(index)).setEnabled(enabled);
    }

    /**
     * Returns true if the tab the index is enabled.
     */
    public boolean isEnabledAt(int index) {
        return ((TabButton) _tabs.elementAt(index)).isEnabled();
    }

    /**
     * Returns the number of tabs in this tabbedpane.
     */
    public int getTabCount() {
        return _tabs.size();
    }

    public void debug(int level_) {
        for (int i = 0; i < level_; i++)
            System.err.print("    ");
        System.err.print("JTabbedPane origin=" + _origin +
                " size=" + _size +
                " _selectedIndex=" + _selectedIndex +
                " tabtitles =");
        Enumeration e = _tabs.elements();
        while (e.hasMoreElements()) {
            String title = ((TabButton) e.nextElement()).getText();
            System.err.print(" " + title + " ");
        }
        System.err.println("");
        super.debug(level_ + 1);
    }

    //====================================================================
    // INSTANCE VARIABLES

    private Vector _tabComponents = new Vector();
    private Vector _tabs = new Vector();
    private int _selectedIndex = -1;
    private ButtonGroup _buttongroup = new ButtonGroup();

    private class TabButton extends JButton implements ActionListener {
        public TabButton(String label_, Component c_, String keylabel_) {
            super(label_);
            _keylabel = keylabel_;
            _c = c_;
            addActionListener(this);
        }

        public String toString() {
            return "JTabbedPane.TabButton locaton=" + getLocation() +
                    " label=\"" + getLabel() +
                    "\" actionCommand=\"" + getActionCommand() + "\"";
        }

        public void actionPerformed(ActionEvent ev_) {
            setSelectedComponent(_c);
        }

        public String getKeyLabel() {
            return (_keylabel);
        }

        public void setKeyLabel(String keylabel_) {
            _keylabel = keylabel_;
        }

        public void requestFocus() {
            super.requestFocus();
            Point origin = getLocationOnScreen();
            Insets insets = super.getInsets();
            Toolkit.getDefaultToolkit().
                    setCursor(origin.addOffset(2 + insets.left, 0 + insets.top));
        }

        public void draw() {
            Point origin = getLocationOnScreen();
            Insets insets = super.getInsets();
            origin.translate(insets.left, insets.top);

            Toolkit term = Toolkit.getDefaultToolkit();

            term.setCursor(origin);

            int colorpair = getCursesColor();
            term.addChar(Toolkit.ACS_ULCORNER, 0, colorpair);
            term.addChar(' ', 0, colorpair);
            term.addString(getLabelString(), isEnabled() ? Toolkit.A_BOLD : 0,
                    colorpair);
            term.addChar(' ', 0, colorpair);
            term.addChar(Toolkit.ACS_URCORNER, 0, colorpair);
            if (isEnabled()) {
                if (getMnemonic() > 0) {
                    int mnemonicPos = getText().indexOf((char) getMnemonic());
                    if (mnemonicPos != -1) {
                        term.setCursor(origin.addOffset(2 + mnemonicPos, 0));
                        term.addChar(getMnemonic(), Toolkit.A_UNDERLINE |
                                Toolkit.A_REVERSE, colorpair);
                    }
                }
            }
            term.setCursor(origin.addOffset(0, 1));
            if (isSelected()) {
                term.addChar(Toolkit.ACS_LRCORNER, 0, colorpair);
                for (int j = 0; j < getText().length() + 2; j++) {
                    term.addChar(' ', 0, colorpair);
                }
                term.addChar(Toolkit.ACS_LLCORNER, 0, colorpair);
            } else {
                term.addChar(Toolkit.ACS_BTEE, 0, colorpair);
                term.setCursor(origin.addOffset(getText().length() + 3, 1));
                term.addChar(Toolkit.ACS_BTEE, 0, colorpair);
                if (isEnabled()) {
                    term.setCursor(origin.addOffset(1, 1));
                    term.addString(_keylabel, Toolkit.A_BOLD, colorpair);
                }
            }
        }

        public int getWidth() {
            Insets insets = super.getInsets();
            return super.getText().length() + insets.left + insets.right + 4;
        }

        private String _keylabel;
        private Component _c;
    }
}
