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

com.alee.extended.dock.WebDockablePane 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.dock;

import com.alee.api.annotations.NotNull;
import com.alee.api.data.CompassDirection;
import com.alee.api.jdk.Objects;
import com.alee.extended.WebContainer;
import com.alee.extended.dock.data.DockableContainer;
import com.alee.managers.style.StyleId;
import com.alee.managers.style.StyleManager;
import com.alee.utils.CollectionUtils;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Implementation of a pane containing dockable frames.
 * Frames can be added to the pane, repositioned within it, minimized, previewed, made floating or closed.
 * Positioning of the frames is handled by the {@link DockablePaneModel}.
 * Resize of the frames and their drop position while drag is handled by the glass layer.
 *
 * This component should never be used with a non-Web UIs as it might cause an unexpected behavior.
 * You could still use that component even if WebLaF is not your application LaF as this component will use Web-UI in any case.
 *
 * @author Mikle Garin
 * @see How to use WebDockablePane
 * @see DockablePaneDescriptor
 * @see WDockablePaneUI
 * @see WebDockablePaneUI
 * @see IDockablePanePainter
 * @see DockablePanePainter
 * @see WebContainer
 */
public class WebDockablePane extends WebContainer
{
    /**
     * Component properties.
     */
    public static final String SIDEBAR_VISIBILITY_PROPERTY = "sidebarVisibility";
    public static final String SIDEBAR_BUTTON_ACTION_PROPERTY = "sidebarButtonAction";
    public static final String CONTENT_SPACING_PROPERTY = "contentSpacing";
    public static final String RESIZE_GRIPPER_PROPERTY = "resizeGripper";
    public static final String MINIMUM_ELEMENT_SIZE_PROPERTY = "minimumElementSize";
    public static final String OCCUPY_MINIMUM_SIZE_FOR_CHILDREN_PROPERTY = "occupyMinimumSizeForChildren";
    public static final String MODEL_PROPERTY = "model";
    public static final String MODEL_STATE_PROPERTY = "modelState";
    public static final String GLASS_LAYER_PROPERTY = "glassLayer";
    public static final String FRAMES_PROPERTY = "frames";
    public static final String FRAME_PROPERTY = "frame";
    public static final String CONTENT_PROPERTY = "content";

    /**
     * Sidebar buttons visibility type.
     *
     * @see com.alee.extended.dock.SidebarVisibility
     */
    protected SidebarVisibility sidebarVisibility;

    /**
     * Sidebar button action.
     */
    protected SidebarButtonAction sidebarButtonAction;

    /**
     * Spacing between content elements.
     */
    protected int contentSpacing;

    /**
     * Content elements resize gripper width.
     * This setting represents actual draggable area width and not visual representation width.
     * It can be larger than {@link #contentSpacing} and will overlap elements in that case blocking their mouse events.
     */
    protected int resizeGripper;

    /**
     * Minimum size of {@link com.alee.extended.dock.WebDockableFrame} or content component added onto the dockable pane.
     */
    protected Dimension minimumElementSize;

    /**
     * Whether containers minimum size should include children minimum sizes or simply be equal to {@link #minimumElementSize}.
     */
    protected boolean occupyMinimumSizeForChildren;

    /**
     * Model containing information about dockable pane elements.
     * It stores all information on frames and content location specifics.
     */
    protected DockablePaneModel model;

    /**
     * Glass layer component.
     * It usually handles frames resize and displays frame placement locations while any frame is being dragged.
     */
    protected JComponent glassLayer;

    /**
     * List of frames added to this pane.
     * This list only contains frames opened frames.
     * Upon closing frame it gets removed from the pane and this list.
     */
    protected final List frames;

    /**
     * Content component.
     * Content takes all free space left after frames positioning.
     */
    protected JComponent content;

    /**
     * Constructs new dockable pane.
     */
    public WebDockablePane ()
    {
        this ( StyleId.auto );
    }

    /**
     * Constructs new dockable pane.
     *
     * @param id style ID
     */
    public WebDockablePane ( final StyleId id )
    {
        super ();
        this.frames = new ArrayList ( 3 );
        setModel ( createDefaultModel () );
        updateUI ();
        setStyleId ( id );
    }

    @Override
    public StyleId getDefaultStyleId ()
    {
        return StyleId.dockablepane;
    }

    /**
     * Returns sidebar buttons visibility type.
     *
     * @return sidebar buttons visibility type
     */
    public SidebarVisibility getSidebarVisibility ()
    {
        return sidebarVisibility;
    }

    /**
     * Sets sidebar buttons visibility type.
     *
     * @param visibility sidebar buttons visibility type
     * @return this pane
     */
    public WebDockablePane setSidebarVisibility ( final SidebarVisibility visibility )
    {
        if ( this.sidebarVisibility != visibility )
        {
            final SidebarVisibility old = this.sidebarVisibility;
            this.sidebarVisibility = visibility;
            firePropertyChange ( SIDEBAR_VISIBILITY_PROPERTY, old, visibility );
        }
        return this;
    }

    /**
     * Returns sidebar button action.
     *
     * @return sidebar button action
     */
    public SidebarButtonAction getSidebarButtonAction ()
    {
        return sidebarButtonAction;
    }

    /**
     * Sets sidebar button action.
     *
     * @param action sidebar button action
     * @return this pane
     */
    public WebDockablePane setSidebarButtonAction ( final SidebarButtonAction action )
    {
        if ( this.sidebarButtonAction != action )
        {
            final SidebarButtonAction old = this.sidebarButtonAction;
            this.sidebarButtonAction = action;
            firePropertyChange ( SIDEBAR_BUTTON_ACTION_PROPERTY, old, action );
        }
        return this;
    }

    /**
     * Returns spacing between content elements.
     *
     * @return spacing between content elements
     */
    public int getContentSpacing ()
    {
        return contentSpacing;
    }

    /**
     * Sets spacing between content elements.
     *
     * @param spacing spacing between content elements
     * @return this pane
     */
    public WebDockablePane setContentSpacing ( final int spacing )
    {
        if ( this.contentSpacing != spacing )
        {
            final int old = this.contentSpacing;
            this.contentSpacing = spacing;
            firePropertyChange ( CONTENT_SPACING_PROPERTY, old, spacing );
        }
        return this;
    }

    /**
     * Returns content elements resize gripper width.
     *
     * @return content elements resize gripper width
     */
    public int getResizeGripper ()
    {
        return resizeGripper;
    }

    /**
     * Sets content elements resize gripper width.
     *
     * @param resizeGripper content elements resize gripper width
     * @return this pane
     */
    public WebDockablePane setResizeGripper ( final int resizeGripper )
    {
        if ( this.resizeGripper != resizeGripper )
        {
            final int old = this.resizeGripper;
            this.resizeGripper = resizeGripper;
            firePropertyChange ( RESIZE_GRIPPER_PROPERTY, old, resizeGripper );
        }
        return this;
    }

    /**
     * Returns minimum size of {@link com.alee.extended.dock.WebDockableFrame} or content component added onto the dockable pane.
     *
     * @return minimum size of {@link com.alee.extended.dock.WebDockableFrame} or content component added onto the dockable pane
     */
    public Dimension getMinimumElementSize ()
    {
        return minimumElementSize;
    }

    /**
     * Sets minimum size of {@link com.alee.extended.dock.WebDockableFrame} or content component added onto the dockable pane.
     *
     * @param size minimum size of {@link com.alee.extended.dock.WebDockableFrame} or content component added onto the dockable pane
     * @return this pane
     */
    public WebDockablePane setMinimumElementSize ( final Dimension size )
    {
        if ( this.minimumElementSize != size )
        {
            final Dimension old = this.minimumElementSize;
            this.minimumElementSize = size;
            firePropertyChange ( MINIMUM_ELEMENT_SIZE_PROPERTY, old, size );
        }
        return this;
    }

    /**
     * Returns whether containers minimum size should include children sizes or simply be equal to {@link #minimumElementSize}.
     *
     * @return true if containers minimum size should include children sizes, false if it should be equal to {@link #minimumElementSize}
     */
    public boolean isOccupyMinimumSizeForChildren ()
    {
        return occupyMinimumSizeForChildren;
    }

    /**
     * Sets whether containers minimum size should include children sizes or simply be equal to {@link #minimumElementSize}.
     *
     * @param occupy whether containers minimum size should include children sizes or simply be equal to {@link #minimumElementSize}
     * @return this pane
     */
    public WebDockablePane setOccupyMinimumSizeForChildren ( final boolean occupy )
    {
        if ( this.occupyMinimumSizeForChildren != occupy )
        {
            final boolean old = this.occupyMinimumSizeForChildren;
            this.occupyMinimumSizeForChildren = occupy;
            firePropertyChange ( OCCUPY_MINIMUM_SIZE_FOR_CHILDREN_PROPERTY, old, occupy );
        }
        return this;
    }

    /**
     * Returns model containing information about dockable pane elements.
     *
     * @return model containing information about dockable pane elements
     */
    public DockablePaneModel getModel ()
    {
        return model;
    }

    /**
     * Sets model containing information about dockable pane elements.
     *
     * @param model model containing information about dockable pane elements
     * @return this pane
     */
    public WebDockablePane setModel ( final DockablePaneModel model )
    {
        if ( this.model != model )
        {
            final DockablePaneModel old = this.model;
            this.model = model;
            setLayout ( model );
            firePropertyChange ( MODEL_PROPERTY, old, model );
        }
        return this;
    }

    /**
     * Returns default {@link DockablePaneModel} implementation to be used.
     *
     * @return default {@link DockablePaneModel} implementation to be used
     */
    protected DockablePaneModel createDefaultModel ()
    {
        return new WebDockablePaneModel ();
    }

    /**
     * Returns dockable pane element states data.
     * It contains data which can be used to restore dockable element states.
     *
     * @return dockable pane element states data
     * @see #setState(com.alee.extended.dock.data.DockableContainer)
     */
    public DockableContainer getState ()
    {
        return getModel ().getRoot ();
    }

    /**
     * Sets dockable pane element states data.
     * This data can be retrieved from dockable pane at any time in runtime.
     *
     * @param state dockable pane element states data
     * @return this pane
     * @see #getState()
     */
    public WebDockablePane setState ( final DockableContainer state )
    {
        // Changing root element
        if ( getModel ().getRoot () != state )
        {
            final DockableContainer old = getModel ().getRoot ();
            getModel ().setRoot ( state );
            firePropertyChange ( MODEL_STATE_PROPERTY, old, model );
        }
        return this;
    }

    /**
     * Returns glass layer component.
     *
     * @return glass layer component
     */
    public JComponent getGlassLayer ()
    {
        return glassLayer;
    }

    /**
     * Sets glass layer component.
     *
     * @param glassLayer glass layer component
     * @return this pane
     */
    public WebDockablePane setGlassLayer ( final JComponent glassLayer )
    {
        if ( this.glassLayer != glassLayer )
        {
            final JComponent old = this.glassLayer;
            this.glassLayer = glassLayer;
            firePropertyChange ( GLASS_LAYER_PROPERTY, old, glassLayer );
        }
        return this;
    }

    /**
     * Returns list of dockable frames added to this pane.
     *
     * @return list of dockable frames added to this pane
     */
    public List getFrames ()
    {
        return CollectionUtils.copy ( frames );
    }

    /**
     * Returns list of dockable frames added to this pane and positioned on the specified side relative to the content area.
     *
     * @param position frames position relative to content area
     * @return list of dockable frames added to this pane and positioned on the specified side relative to the content area
     */
    public List getFrames ( final CompassDirection position )
    {
        final List positioned = new ArrayList ( frames.size () );
        for ( final WebDockableFrame frame : frames )
        {
            if ( frame.getPosition () == position )
            {
                positioned.add ( frame );
            }
        }
        return positioned;
    }

    /**
     * Returns opened frame with the specified ID or {@code null} if no such frame exists.
     *
     * @param id frame ID
     * @return opened frame with the specified ID or {@code null} if no such frame exists
     */
    public WebDockableFrame getFrame ( final String id )
    {
        for ( final WebDockableFrame frame : frames )
        {
            if ( Objects.equals ( id, frame.getId () ) )
            {
                return frame;
            }
        }
        return null;
    }

    /**
     * Adds specified dockable frame into this pane.
     * Position of the frame will get restored by its ID if it was saved at least once before.
     * Note that this frame might be in closed state, in that case it will still not be visible.
     *
     * @param frame dockable frame to add
     * @return added frame
     */
    public WebDockableFrame addFrame ( final WebDockableFrame frame )
    {
        if ( !frames.contains ( frame ) )
        {
            final List old = CollectionUtils.copy ( frames );
            frames.add ( frame );
            firePropertyChange ( FRAMES_PROPERTY, old, frames );
            firePropertyChange ( FRAME_PROPERTY, null, frame );
        }
        return frame;
    }

    /**
     * Removes specified dockable frame from this pane.
     * This will completely remove frame and its data from pane model.
     *
     * @param frame dockable frame to remove
     * @return removed frame
     */
    public WebDockableFrame removeFrame ( final WebDockableFrame frame )
    {
        if ( frames.contains ( frame ) )
        {
            final List old = CollectionUtils.copy ( frames );
            frames.remove ( frame );
            firePropertyChange ( FRAMES_PROPERTY, old, frames );
            firePropertyChange ( FRAME_PROPERTY, frame, null );
        }
        return frame;
    }

    /**
     * Returns current content component.
     *
     * @return current content component
     */
    public JComponent getContent ()
    {
        return content;
    }

    /**
     * Sets content component.
     *
     * @param content content component
     * @return previous content component
     */
    public JComponent setContent ( final JComponent content )
    {
        final JComponent old = this.content;
        if ( this.content != content )
        {
            this.content = content;
            firePropertyChange ( CONTENT_PROPERTY, old, content );
        }
        return old;
    }

    /**
     * Adds new {@link com.alee.extended.dock.DockableFrameListener}.
     *
     * @param listener {@link com.alee.extended.dock.DockableFrameListener} to add
     */
    public void addFrameListener ( final DockableFrameListener listener )
    {
        listenerList.add ( DockableFrameListener.class, listener );
    }

    /**
     * Removes specified {@link com.alee.extended.dock.DockableFrameListener}.
     *
     * @param listener {@link com.alee.extended.dock.DockableFrameListener} to remove
     */
    public void removeFrameListener ( final DockableFrameListener listener )
    {
        listenerList.remove ( DockableFrameListener.class, listener );
    }

    /**
     * Informs listeners about frame being added.
     *
     * @param frame        {@link com.alee.extended.dock.WebDockableFrame} which was added
     * @param dockablePane {@link com.alee.extended.dock.WebDockablePane} where frame was added
     */
    public void fireFrameAdded ( final WebDockableFrame frame, final WebDockablePane dockablePane )
    {
        for ( final DockableFrameListener listener : listenerList.getListeners ( DockableFrameListener.class ) )
        {
            listener.frameAdded ( frame, dockablePane );
        }
    }

    /**
     * Informs listeners about frame state change.
     *
     * @param frame    {@link com.alee.extended.dock.WebDockableFrame}
     * @param oldState previous frame state
     * @param newState current frame state
     */
    public void fireFrameStateChanged ( final WebDockableFrame frame, final DockableFrameState oldState, final DockableFrameState newState )
    {
        for ( final DockableFrameListener listener : listenerList.getListeners ( DockableFrameListener.class ) )
        {
            listener.frameStateChanged ( frame, oldState, newState );
        }
    }

    /**
     * Informs listeners about frame being moved.
     *
     * @param frame    {@link com.alee.extended.dock.WebDockableFrame}
     * @param position current frame position relative to content
     */
    public void fireFrameMoved ( final WebDockableFrame frame, final CompassDirection position )
    {
        for ( final DockableFrameListener listener : listenerList.getListeners ( DockableFrameListener.class ) )
        {
            listener.frameMoved ( frame, position );
        }
    }

    /**
     * Informs listeners about frame being removed.
     *
     * @param frame        {@link com.alee.extended.dock.WebDockableFrame} which was removed
     * @param dockablePane {@link com.alee.extended.dock.WebDockablePane} where frame was removed from
     */
    public void fireFrameRemoved ( final WebDockableFrame frame, final WebDockablePane dockablePane )
    {
        for ( final DockableFrameListener listener : listenerList.getListeners ( DockableFrameListener.class ) )
        {
            listener.frameRemoved ( frame, dockablePane );
        }
    }

    @Override
    public void applyComponentOrientation ( final ComponentOrientation orientation )
    {
        if ( orientation == null )
        {
            throw new NullPointerException ( "ComponentOrientation must not be null" );
        }
        setComponentOrientation ( orientation );
        synchronized ( getTreeLock () )
        {
            for ( final WebDockableFrame frame : frames )
            {
                frame.applyComponentOrientation ( orientation );
            }
            final JComponent content = getContent ();
            if ( content != null )
            {
                content.applyComponentOrientation ( orientation );
            }
        }
    }

    /**
     * Returns the look and feel (LaF) object that renders this component.
     *
     * @return the {@link WDockablePaneUI} object that renders this component
     */
    public WDockablePaneUI getUI ()
    {
        return ( WDockablePaneUI ) ui;
    }

    /**
     * Sets the LaF object that renders this component.
     *
     * @param ui {@link WDockablePaneUI}
     */
    public void setUI ( final WDockablePaneUI ui )
    {
        super.setUI ( ui );
    }

    @Override
    public void updateUI ()
    {
        StyleManager.getDescriptor ( this ).updateUI ( this );
    }

    @NotNull
    @Override
    public String getUIClassID ()
    {
        return StyleManager.getDescriptor ( this ).getUIClassId ();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy