001/*
002 * $Id: StackLayout.java 3793 2010-09-28 05:15:02Z kschaefe $
003 *
004 * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020 */
021package org.jdesktop.swingx;
022
023import java.awt.Component;
024import java.awt.Container;
025import java.awt.Dimension;
026import java.awt.Insets;
027import java.awt.LayoutManager2;
028import java.awt.Rectangle;
029import java.util.LinkedList;
030import java.util.List;
031
032/**
033 * <p><code>StackLayout</code> is a Swing layout aimed to act as the layers
034 * stack of most popuplar graphics editing tools like <i>The GIMP</i> or
035 * <i>Photoshop</i>. While similar to <code>CardLayout</code>, this layout
036 * displays all the components of the container. If you are using non-rectangular
037 * components (i.e. transparent) you will see them from top to bottom of the
038 * stack.</p>
039 * <p>When using this layout, each component can be added in the container
040 * either on top of the stack or at the bottom:</p>
041 * <pre>
042 * JPanel panel = new JPanel(new StackLayout());
043 * panel.add(new JLabel("On top"),    StackLayout.TOP);
044 * panel.add(new JLabel("At bottom"), StackLayout.BOTTOM);
045 * </pre>
046 * If you don't specify the constraint, the component will be added at the top
047 * of the components stack.</p>
048 * <p>All the components managed by this layout will be given the same size as
049 * the container itself. The minimum, maximum and preferred size of the 
050 * container are based upon the largest minimum, maximum and preferred size of
051 * the children components.</p>
052 * <p><code>StackLayout</code> works only with JSE 1.5 and Java SE 6 and
053 * greater.</p>
054 * 
055 * @author Romain Guy <romain.guy@mac.com>
056 */
057
058public class StackLayout implements LayoutManager2 {
059    /** Use this constraint to add a component at the bottom of the stack. */
060    public static final String BOTTOM = "bottom";
061    /** Use this contrainst to add a component at the top of the stack. */
062    public static final String TOP = "top";
063
064    // removing components does not happen often compared to adding components
065    // hence we choose a linked list to make insertion at the bottom faster
066    private List<Component> components = new LinkedList<Component>();
067
068    /**
069     * {@inheritDoc}
070     */
071    @Override
072    public void addLayoutComponent(final Component comp,
073                                   final Object constraints) {
074        synchronized (comp.getTreeLock()) {
075            if (BOTTOM.equals(constraints)) {
076                components.add(0, comp);
077            } else if (TOP.equals(constraints)) {
078                components.add(comp);
079            } else {
080                components.add(comp);
081            }
082        }
083    }
084
085    /**
086     * {@inheritDoc}
087     */
088    @Override
089    public void addLayoutComponent(final String name, final Component comp) {
090        addLayoutComponent(comp, TOP);
091    }
092
093    /**
094     * {@inheritDoc}
095     */
096    @Override
097    public void removeLayoutComponent(final Component comp) {
098        synchronized (comp.getTreeLock()) {
099            components.remove(comp);
100        }
101    }
102    
103    /**
104     * {@inheritDoc}
105     */
106    @Override
107    public float getLayoutAlignmentX(final Container target) {
108        return 0.5f;
109    }
110
111    /**
112     * {@inheritDoc}
113     */
114    @Override
115    public float getLayoutAlignmentY(final Container target) {
116        return 0.5f;
117    }
118
119    /**
120     * {@inheritDoc}
121     */
122    @Override
123    public void invalidateLayout(final Container target) {
124    }
125
126    /**
127     * {@inheritDoc}
128     */
129    @Override
130    public Dimension preferredLayoutSize(final Container parent) {
131        synchronized (parent.getTreeLock()) {
132            int width = 0;
133            int height = 0;
134
135            for (Component comp: components) {
136                Dimension size = comp.getPreferredSize();
137                width = Math.max(size.width, width);
138                height = Math.max(size.height, height);
139            }
140
141            Insets insets = parent.getInsets();
142            width += insets.left + insets.right;
143            height += insets.top + insets.bottom;
144            
145            return new Dimension(width, height);
146        }
147    }
148
149    /**
150     * {@inheritDoc}
151     */
152    @Override
153    public Dimension minimumLayoutSize(final Container parent) {
154        synchronized (parent.getTreeLock()) {
155            int width = 0;
156            int height = 0;
157
158            for (Component comp: components) {
159                Dimension size = comp.getMinimumSize();
160                width = Math.max(size.width, width);
161                height = Math.max(size.height, height);
162            }
163
164            Insets insets = parent.getInsets();
165            width += insets.left + insets.right;
166            height += insets.top + insets.bottom;
167            
168            return new Dimension(width, height);
169        }
170    }
171
172    /**
173     * {@inheritDoc}
174     */
175    @Override
176    public Dimension maximumLayoutSize(final Container target) {
177        return new Dimension(Integer.MAX_VALUE,
178                             Integer.MAX_VALUE);
179    }
180
181    /**
182     * {@inheritDoc}
183     */
184    @Override
185    public void layoutContainer(final Container parent) {
186        synchronized (parent.getTreeLock()) {
187            int width = parent.getWidth();
188            int height = parent.getHeight();
189            
190            Rectangle bounds = new Rectangle(0, 0, width, height);
191
192            int componentsCount = components.size();
193            
194            for (int i = 0; i < componentsCount; i++) {
195                Component comp = components.get(i);
196                comp.setBounds(bounds);
197                parent.setComponentZOrder(comp, componentsCount - i - 1);
198            }
199        }
200    }
201}