//
// $Id: CollapsiblePanel.java,v 1.8 2003/06/26 05:55:47 ray Exp $

package com.samskivert.swing;

import java.awt.EventQueue;
import java.awt.Rectangle;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

import javax.swing.AbstractButton;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

import com.samskivert.swing.util.SwingUtil;

/**
 * A panel that contains a button which will collapse the rest of the content.
 */
public class CollapsiblePanel extends JPanel
    implements ActionListener
{
    /**
     * Construct a collapsible panel with the specified button as the
     * trigger. The text of the button will be used as the triggertext.
     */
    public CollapsiblePanel (JButton trigger)
    {
        setTrigger(trigger, null, null);
        setTriggerContainer(trigger);
    }

    /**
     * Construct a collapsible panel with the specified button text.
     */
    public CollapsiblePanel (String triggertext)
    {
        this(new JButton(triggertext));
    }

    /**
     * Create a collapsible panel to which the trigger button will be
     * added later.
     */
    public CollapsiblePanel ()
    {
        VGroupLayout gl = new VGroupLayout(VGroupLayout.NONE);
        gl.setOffAxisPolicy(VGroupLayout.STRETCH);
        gl.setGap(0);
        gl.setJustification(VGroupLayout.TOP);
        gl.setOffAxisJustification(VGroupLayout.LEFT);
        setLayout(gl);
    }

    /**
     * Set a component which contains the trigger button. The simple case
     * is to just set the trigger button as this component.
     */
    public void setTriggerContainer (JComponent comp)
    {
        setTriggerContainer(comp, new JPanel());
    }

    /**
     * Set a component which contains the trigger button.
     */
    public void setTriggerContainer (JComponent comp, JPanel content)
    {
        // these are our only two components.
        add(comp);
        add(_content = content);

        // When the content is shown, make sure it's scrolled visible
        _content.addComponentListener(new ComponentAdapter() {
            public void componentShown (ComponentEvent event)
            {
                // we can't do it just yet, the content doesn't know its size
                EventQueue.invokeLater(new Runnable() {
                    public void run () {
                        // The content is offset a bit from the trigger
                        // but we want the trigger to show up, so we add
                        // in point 0,0
                        Rectangle r = _content.getBounds();
                        r.add(0, 0);
                        scrollRectToVisible(r);
                    }
                });
            }
        });

        // and start us out not showing
        setCollapsed(true);
    }

    /**
     * Set the trigger button.
     */
    public void setTrigger (AbstractButton trigger,
                            Icon collapsed, Icon uncollapsed)
    {
        _trigger = trigger;
        _trigger.setHorizontalAlignment(SwingConstants.LEFT);
        _trigger.setHorizontalTextPosition(SwingConstants.RIGHT);
        _downIcon = collapsed;
        _upIcon = uncollapsed;
        _trigger.addActionListener(this);
    }

    /**
     * Set the gap between the trigger button and the rest of the content.
     * Can be negative for an overlapping effect.
     */
    public void setGap (int gap)
    {
        ((VGroupLayout) getLayout()).setGap(gap);
        invalidate();
    }

    /**
     * Get the content panel for filling in with sweet content goodness.
     */
    public JPanel getContent ()
    {
        return _content;
    }

    // documentation from interface ActionListener
    public void actionPerformed (ActionEvent e)
    {
        if (e.getSource() == _trigger) {
            setCollapsed(!isCollapsed());
        }
    }

    /**
     * Is the panel collapsed?
     */
    public boolean isCollapsed ()
    {
        return !_content.isVisible();
    }

    /**
     * Set the collapsion state.
     */
    public void setCollapsed (boolean collapse)
    {
        if (collapse) {
            _content.setVisible(false);
            _trigger.setIcon(_downIcon);

        } else {
            _content.setVisible(true);
            _trigger.setIcon(_upIcon);
        }
        SwingUtil.refresh(this);
    }

    /** The button that triggers collapsion. */
    protected AbstractButton _trigger;

    /** The icons for collapsed and uncollapsed. */
    protected Icon _upIcon, _downIcon;

    /** The who in the what now? */
    protected JPanel _content;
}
