All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alee.extended.accordion.AccordionPane Maven / Gradle / Ivy

There is a newer version: 1.2.14
Show newest version
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see .
 */

package com.alee.extended.accordion;

import com.alee.api.Identifiable;
import com.alee.api.annotations.NotNull;
import com.alee.api.annotations.Nullable;
import com.alee.api.data.BoxOrientation;
import com.alee.extended.collapsible.AbstractControlButton;
import com.alee.extended.collapsible.AbstractHeaderPanel;
import com.alee.extended.collapsible.AbstractTitleLabel;
import com.alee.extended.collapsible.HeaderLayout;
import com.alee.laf.panel.WebPanel;
import com.alee.managers.style.StyleId;
import com.alee.painter.decoration.DecorationState;
import com.alee.painter.decoration.Stateful;
import com.alee.utils.SwingUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

/**
 * {@link AccordionPane} for a single collapsible content {@link Component} within {@link WebAccordion}.
 * This is basically a collapsible panel made and optimized specifically for {@link WebAccordion} needs.
 * It doesn't have it's own UI but uses some abstract UI elements from {@link com.alee.extended.collapsible.WebCollapsiblePane}.
 *
 * @author Mikle Garin
 * @see How to use WebAccordion
 * @see WebAccordion
 * @see AccordionModel
 * @see WebAccordionModel
 */
public class AccordionPane extends WebPanel implements Stateful, Identifiable
{
    /**
     * {@link AccordionPane} unique identifier.
     */
    @NotNull
    protected final String id;

    /**
     * Header {@link JComponent}.
     */
    @NotNull
    protected JComponent header;

    /**
     * Content {@link Component}.
     */
    @Nullable
    protected Component content;

    /**
     * Constructs new {@link AccordionPane}.
     *
     * @param accordion {@link WebAccordion} this {@link AccordionPane} is created for
     * @param id        unique {@link AccordionPane} identifier
     * @param title     {@link AccordionPane} title
     */
    public AccordionPane ( @NotNull final WebAccordion accordion, @NotNull final String id, @Nullable final String title )
    {
        this ( StyleId.accordionPane.at ( accordion ), accordion, id, null, title, null );
    }

    /**
     * Constructs new {@link AccordionPane}.
     *
     * @param accordion {@link WebAccordion} this {@link AccordionPane} is created for
     * @param id        unique {@link AccordionPane} identifier
     * @param icon      {@link AccordionPane} title icon
     * @param title     {@link AccordionPane} title
     */
    public AccordionPane ( @NotNull final WebAccordion accordion, @NotNull final String id, @Nullable final Icon icon,
                           @Nullable final String title )
    {
        this ( StyleId.accordionPane.at ( accordion ), accordion, id, icon, title, null );
    }

    /**
     * Constructs new {@link AccordionPane}.
     *
     * @param accordion {@link WebAccordion} this {@link AccordionPane} is created for
     * @param id        unique {@link AccordionPane} identifier
     * @param title     {@link AccordionPane} title
     * @param content   content {@link Component}
     */
    public AccordionPane ( @NotNull final WebAccordion accordion, @NotNull final String id, @Nullable final String title,
                           @Nullable final Component content )
    {
        this ( StyleId.accordionPane.at ( accordion ), accordion, id, null, title, content );
    }

    /**
     * Constructs new {@link AccordionPane}.
     *
     * @param accordion {@link WebAccordion} this {@link AccordionPane} is created for
     * @param id        unique {@link AccordionPane} identifier
     * @param icon      {@link AccordionPane} title icon
     * @param title     {@link AccordionPane} title
     * @param content   content {@link Component}
     */
    public AccordionPane ( @NotNull final WebAccordion accordion, @NotNull final String id, @Nullable final Icon icon,
                           @Nullable final String title, @Nullable final Component content )
    {
        this ( StyleId.accordionPane.at ( accordion ), accordion, id, icon, title, content );
    }

    /**
     * Constructs new {@link AccordionPane}.
     *
     * @param styleId   {@link StyleId}
     * @param accordion {@link WebAccordion} this {@link AccordionPane} is created for
     * @param id        {@link AccordionPane} unique identifier
     * @param title     {@link AccordionPane} title
     */
    public AccordionPane ( @NotNull final StyleId styleId, @NotNull final WebAccordion accordion, @NotNull final String id,
                           @Nullable final String title )
    {
        this ( styleId, accordion, id, null, title, null );
    }

    /**
     * Constructs new {@link AccordionPane}.
     *
     * @param styleId   {@link StyleId}
     * @param accordion {@link WebAccordion} this {@link AccordionPane} is created for
     * @param id        {@link AccordionPane} unique identifier
     * @param icon      {@link AccordionPane} title icon
     * @param title     {@link AccordionPane} title
     */
    public AccordionPane ( @NotNull final StyleId styleId, @NotNull final WebAccordion accordion, @NotNull final String id,
                           @Nullable final Icon icon, @Nullable final String title )
    {
        this ( styleId, accordion, id, icon, title, null );
    }

    /**
     * Constructs new {@link AccordionPane}.
     *
     * @param styleId   {@link StyleId}
     * @param accordion {@link WebAccordion} this {@link AccordionPane} is created for
     * @param id        {@link AccordionPane} unique identifier
     * @param title     {@link AccordionPane} title
     * @param content   content {@link Component}
     */
    public AccordionPane ( @NotNull final StyleId styleId, @NotNull final WebAccordion accordion, @NotNull final String id,
                           @Nullable final String title, @Nullable final Component content )
    {
        this ( styleId, accordion, id, null, title, content );
    }

    /**
     * Constructs new {@link AccordionPane}.
     *
     * @param styleId   {@link StyleId}
     * @param accordion {@link WebAccordion} this {@link AccordionPane} is created for
     * @param id        {@link AccordionPane} unique identifier
     * @param icon      {@link AccordionPane} title icon
     * @param title     {@link AccordionPane} title
     * @param content   content {@link Component}
     */
    public AccordionPane ( @NotNull final StyleId styleId, @NotNull final WebAccordion accordion, @NotNull final String id,
                           @Nullable final Icon icon, @Nullable final String title, @Nullable final Component content )
    {
        super ( styleId, ( LayoutManager ) null );
        this.id = id;

        this.header = createHeaderComponent ( icon, title );
        add ( this.header );

        this.content = content;
        if ( content != null )
        {
            add ( this.content );
        }
    }

    @NotNull
    @Override
    public String getId ()
    {
        return id;
    }

    @Nullable
    @Override
    public List getStates ()
    {
        final List states = new ArrayList ( 3 );

        // Header position state
        states.add ( getHeaderPosition ().name () );

        // Expansion state
        states.add ( isExpanded () || isInTransition () ? DecorationState.expanded : DecorationState.collapsed );
        if ( isInTransition () )
        {
            states.add ( isExpanded () ? DecorationState.expanding : DecorationState.collapsing );
        }

        return states;
    }

    /**
     * Returns header {@link JComponent}.
     *
     * @return header {@link JComponent}
     */
    @NotNull
    public JComponent getHeader ()
    {
        return header;
    }

    /**
     * Replaces header {@link JComponent}.
     *
     * @param icon  {@link AccordionPane} title icon
     * @param title {@link AccordionPane} title
     */
    public void setHeader ( @Nullable final Icon icon, @Nullable final String title )
    {
        setHeader ( createHeaderComponent ( icon, title ) );
    }

    /**
     * Replaces header {@link JComponent}.
     *
     * @param header new header {@link JComponent}
     */
    public void setHeader ( @NotNull final JComponent header )
    {
        if ( this.header != header )
        {
            remove ( this.header );
            this.header = header;
            add ( this.header );
            SwingUtils.update ( this );
        }
    }

    /**
     * Returns newly created header {@link JComponent}.
     *
     * @param icon  header {@link Icon}
     * @param title header title
     * @return newly created header {@link JComponent}
     */
    @NotNull
    protected JComponent createHeaderComponent ( @Nullable final Icon icon, @Nullable final String title )
    {
        final AbstractHeaderPanel headerPanel = new AbstractHeaderPanel ( StyleId.accordionPaneHeaderPanel.at ( this ) )
        {
            @Override
            @NotNull
            public BoxOrientation getHeaderPosition ()
            {
                return AccordionPane.this.getHeaderPosition ();
            }

            @Override
            public boolean isExpanded ()
            {
                return AccordionPane.this.isExpanded ();
            }

            @Override
            public boolean isInTransition ()
            {
                return AccordionPane.this.isInTransition ();
            }

            @Override
            protected void onHeaderAction ( @NotNull final InputEvent e )
            {
                expandOrCollapse ( e );
            }
        };
        headerPanel.add ( createTitleComponent ( headerPanel, icon, title ), HeaderLayout.TITLE );
        headerPanel.add ( createControlComponent ( headerPanel ), HeaderLayout.CONTROL );
        return headerPanel;
    }

    /**
     * Returns newly created title {@link JComponent}.
     *
     * @param header header {@link JComponent}
     * @param icon   header {@link Icon}
     * @param title  header title
     * @return newly created title {@link JComponent}
     */
    @NotNull
    protected JComponent createTitleComponent ( @NotNull final JComponent header, @Nullable final Icon icon, @Nullable final String title )
    {
        final AbstractTitleLabel titleLabel = new AbstractTitleLabel ( StyleId.accordionPaneTitleLabel.at ( header ) )
        {
            @Override
            @NotNull
            public BoxOrientation getHeaderPosition ()
            {
                return AccordionPane.this.getHeaderPosition ();
            }

            @Override
            public boolean isExpanded ()
            {
                return AccordionPane.this.isExpanded ();
            }

            @Override
            public boolean isInTransition ()
            {
                return AccordionPane.this.isInTransition ();
            }
        };
        titleLabel.setIcon ( icon );
        titleLabel.setText ( title );
        return titleLabel;
    }

    /**
     * Returns newly created control {@link JComponent}.
     *
     * @param header header {@link JComponent}
     * @return newly created control {@link JComponent}
     */
    @NotNull
    protected JComponent createControlComponent ( @NotNull final JComponent header )
    {
        return new AbstractControlButton ( StyleId.accordionPaneControlButton.at ( header ) )
        {
            @Override
            @NotNull
            public BoxOrientation getHeaderPosition ()
            {
                return AccordionPane.this.getHeaderPosition ();
            }

            @Override
            public boolean isExpanded ()
            {
                return AccordionPane.this.isExpanded ();
            }

            @Override
            public boolean isInTransition ()
            {
                return AccordionPane.this.isInTransition ();
            }

            @Override
            protected void onControlAction ( @NotNull final ActionEvent e )
            {
                expandOrCollapse ( e );
            }
        };
    }

    /**
     * Either expands or collapses this {@link AccordionPane}.
     *
     * @param e {@link AWTEvent}
     */
    protected void expandOrCollapse ( @NotNull final AWTEvent e )
    {
        final Container parent = AccordionPane.this.getParent ();
        if ( parent instanceof WebAccordion )
        {
            final WebAccordion accordion = ( WebAccordion ) parent;
            if ( isExpanded () )
            {
                accordion.collapsePane ( getId () );
            }
            else
            {
                accordion.expandPane ( getId () );
            }
            if ( e instanceof MouseEvent || e instanceof ActionEvent )
            {
                if ( accordion.isShowing () )
                {
                    if ( accordion.isFocusable () )
                    {
                        accordion.requestFocusInWindow ();
                    }
                    else
                    {
                        AccordionPane.this.transferFocus ();
                    }
                }
            }
        }
    }

    /**
     * Returns content {@link Component}.
     *
     * @return content {@link Component}
     */
    @Nullable
    public Component getContent ()
    {
        return content;
    }

    /**
     * Replaces content {@link Component}.
     *
     * @param content new content {@link Component}
     */
    public void setContent ( @Nullable final Component content )
    {
        if ( this.content != content )
        {
            if ( this.content != null )
            {
                remove ( this.content );
            }
            this.content = content;
            if ( this.content != null )
            {
                add ( this.content );
            }
            SwingUtils.update ( this );
        }
    }

    /**
     * Returns header panel position.
     *
     * @return header panel position
     */
    @NotNull
    public BoxOrientation getHeaderPosition ()
    {
        final Container parent = getParent ();
        return parent instanceof WebAccordion ? ( ( WebAccordion ) parent ).getHeaderPosition () : BoxOrientation.top;
    }

    /**
     * Returns whether or not this {@link AccordionPane} is expanded.
     *
     * @return {@code true} if this {@link AccordionPane} is expanded, {@code false} otherwise
     */
    public boolean isExpanded ()
    {
        final Container parent = getParent ();
        return parent instanceof WebAccordion && ( ( WebAccordion ) parent ).isPaneExpanded ( getId () );
    }

    /**
     * Changes state of this {@link AccordionPane} to either expanded or collapsed.
     *
     * @param expanded whether or not this {@link AccordionPane} needs to be expanded or collapsed
     * @return {@code true} if state of this {@link AccordionPane} was changed
     */
    public boolean setExpanded ( final boolean expanded )
    {
        final Container parent = getParent ();
        return parent instanceof WebAccordion && ( ( WebAccordion ) parent ).setPaneExpanded ( getId (), expanded );
    }

    /**
     * Asks this {@link AccordionPane} to expand if possible.
     *
     * @return {@code true} if this {@link AccordionPane} was successfully expanded, {@code false} otherwise
     */
    public boolean expand ()
    {
        final Container parent = getParent ();
        return parent instanceof WebAccordion && ( ( WebAccordion ) parent ).expandPane ( getId () );
    }

    /**
     * Returns whether or not this {@link AccordionPane} is collapsed.
     *
     * @return {@code true} if this {@link AccordionPane} is collapsed, {@code false} otherwise
     */
    public boolean isCollapsed ()
    {
        final Container parent = getParent ();
        return parent instanceof WebAccordion && ( ( WebAccordion ) parent ).isPaneCollapsed ( getId () );
    }

    /**
     * Changes state of this {@link AccordionPane} to either collapsed or expanded.
     *
     * @param collapsed whether or not this {@link AccordionPane} needs to be collapsed or expanded
     * @return {@code true} if state of this {@link AccordionPane} was changed
     */
    public boolean setCollapsed ( final boolean collapsed )
    {
        final Container parent = getParent ();
        return parent instanceof WebAccordion && ( ( WebAccordion ) parent ).setPaneCollapsed ( getId (), collapsed );
    }

    /**
     * Asks this {@link AccordionPane} to collapse if possible.
     *
     * @return {@code true} if this {@link AccordionPane} was successfully collapsed, {@code false} otherwise
     */
    public boolean collapse ()
    {
        final Container parent = getParent ();
        return parent instanceof WebAccordion && ( ( WebAccordion ) parent ).collapsePane ( getId () );
    }

    /**
     * Returns whether or not {@link AccordionPane} is in transition to either of two expansion states.
     *
     * @return {@code true} if {@link AccordionPane} is in transition, {@code false} otherwise
     */
    public boolean isInTransition ()
    {
        final Container parent = getParent ();
        return parent instanceof WebAccordion && ( ( WebAccordion ) parent ).isPaneInTransition ( getId () );
    }

    /**
     * Returns minimum size in pixels of a single {@link AccordionPane}'s content.
     * Can be any value from {@code 0} to any reasonable amount of pixels you want for each {@link AccordionPane}'s content.
     * It is used instead of the content's preferred size to ensure that {@link WebAccordion} preserves it's preferred size at all times.
     * Otherwise whenever you expand/collapse something {@link WebAccordion} preferred size would vary wildly and negatively affect layout.
     *
     * @return minimum size in pixels of a single {@link AccordionPane}'s content
     */
    public int getMinimumPaneContentSize ()
    {
        final Container parent = getParent ();
        return parent instanceof WebAccordion ? ( ( WebAccordion ) parent ).getMinimumPaneContentSize () : 0;
    }

    /**
     * Adds {@link AccordionPaneListener}.
     *
     * @param listener {@link AccordionPaneListener} to add
     */
    public void addAccordionPaneListener ( @NotNull final AccordionPaneListener listener )
    {
        listenerList.add ( AccordionPaneListener.class, listener );
    }

    /**
     * Removes {@link AccordionPaneListener}.
     *
     * @param listener {@link AccordionPaneListener} to remove
     */
    public void removeAccordionPaneListener ( @NotNull final AccordionPaneListener listener )
    {
        listenerList.remove ( AccordionPaneListener.class, listener );
    }

    /**
     * Notifies that specified {@link AccordionPane} started expanding.
     *
     * @param accordion {@link WebAccordion}
     */
    public void fireExpanding ( @NotNull final WebAccordion accordion )
    {
        for ( final AccordionPaneListener listener : listenerList.getListeners ( AccordionPaneListener.class ) )
        {
            listener.expanding ( accordion, this );
        }
    }

    /**
     * Notifies when {@link WebAccordion} finished expanding.
     *
     * @param accordion {@link WebAccordion}
     */
    public void fireExpanded ( @NotNull final WebAccordion accordion )
    {
        for ( final AccordionPaneListener listener : listenerList.getListeners ( AccordionPaneListener.class ) )
        {
            listener.expanded ( accordion, this );
        }
    }

    /**
     * Notifies when {@link WebAccordion} starts to collapse.
     *
     * @param accordion {@link WebAccordion}
     */
    public void fireCollapsing ( @NotNull final WebAccordion accordion )
    {
        for ( final AccordionPaneListener listener : listenerList.getListeners ( AccordionPaneListener.class ) )
        {
            listener.collapsing ( accordion, this );
        }
    }

    /**
     * Notifies when {@link WebAccordion} finished collapsing.
     *
     * @param accordion {@link WebAccordion}
     */
    public void fireCollapsed ( @NotNull final WebAccordion accordion )
    {
        for ( final AccordionPaneListener listener : listenerList.getListeners ( AccordionPaneListener.class ) )
        {
            listener.collapsed ( accordion, this );
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy