001    /*
002     * $Id: StackLayout.java 3100 2008-10-14 22:33:10Z rah003 $
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     */
021    package org.jdesktop.swingx;
022    
023    import java.awt.Component;
024    import java.awt.Container;
025    import java.awt.Dimension;
026    import java.awt.Insets;
027    import java.awt.LayoutManager2;
028    import java.awt.Rectangle;
029    import java.util.LinkedList;
030    import 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 bootom:</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    
058    public 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        public void addLayoutComponent(final Component comp,
072                                       final Object constraints) {
073            synchronized (comp.getTreeLock()) {
074                if (BOTTOM.equals(constraints)) {
075                    components.add(0, comp);
076                } else if (TOP.equals(constraints)) {
077                    components.add(comp);
078                } else {
079                    components.add(comp);
080                }
081            }
082        }
083    
084        /**
085         * {@inheritDoc}
086         */
087        public void addLayoutComponent(final String name, final Component comp) {
088            addLayoutComponent(comp, TOP);
089        }
090    
091        /**
092         * {@inheritDoc}
093         */
094        public void removeLayoutComponent(final Component comp) {
095            synchronized (comp.getTreeLock()) {
096                components.remove(comp);
097            }
098        }
099        
100        /**
101         * {@inheritDoc}
102         */
103        public float getLayoutAlignmentX(final Container target) {
104            return 0.5f;
105        }
106    
107        /**
108         * {@inheritDoc}
109         */
110        public float getLayoutAlignmentY(final Container target) {
111            return 0.5f;
112        }
113    
114        /**
115         * {@inheritDoc}
116         */
117        public void invalidateLayout(final Container target) {
118        }
119    
120        /**
121         * {@inheritDoc}
122         */
123        public Dimension preferredLayoutSize(final Container parent) {
124            synchronized (parent.getTreeLock()) {
125                int width = 0;
126                int height = 0;
127    
128                for (Component comp: components) {
129                    Dimension size = comp.getPreferredSize();
130                    width = Math.max(size.width, width);
131                    height = Math.max(size.height, height);
132                }
133    
134                Insets insets = parent.getInsets();
135                width += insets.left + insets.right;
136                height += insets.top + insets.bottom;
137                
138                return new Dimension(width, height);
139            }
140        }
141    
142        /**
143         * {@inheritDoc}
144         */
145        public Dimension minimumLayoutSize(final Container parent) {
146            synchronized (parent.getTreeLock()) {
147                int width = 0;
148                int height = 0;
149    
150                for (Component comp: components) {
151                    Dimension size = comp.getMinimumSize();
152                    width = Math.max(size.width, width);
153                    height = Math.max(size.height, height);
154                }
155    
156                Insets insets = parent.getInsets();
157                width += insets.left + insets.right;
158                height += insets.top + insets.bottom;
159                
160                return new Dimension(width, height);
161            }
162        }
163    
164        /**
165         * {@inheritDoc}
166         */
167        public Dimension maximumLayoutSize(final Container target) {
168            return new Dimension(Integer.MAX_VALUE,
169                                 Integer.MAX_VALUE);
170        }
171    
172        /**
173         * {@inheritDoc}
174         */
175        public void layoutContainer(final Container parent) {
176            synchronized (parent.getTreeLock()) {
177                int width = parent.getWidth();
178                int height = parent.getHeight();
179                
180                Rectangle bounds = new Rectangle(0, 0, width, height);
181    
182                int componentsCount = components.size();
183                
184                for (int i = 0; i < componentsCount; i++) {
185                    Component comp = components.get(i);
186                    comp.setBounds(bounds);
187                    parent.setComponentZOrder(comp, componentsCount - i - 1);
188                }
189            }
190        }
191    }