001    /*
002     * $Id: StackLayout.java,v 1.2 2006/04/19 23:42:08 gfx Exp $
003     *
004     * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     */
007    
008    package org.jdesktop.swingx;
009    
010    import java.awt.Component;
011    import java.awt.Container;
012    import java.awt.Dimension;
013    import java.awt.Insets;
014    import java.awt.LayoutManager2;
015    import java.awt.Rectangle;
016    import java.util.LinkedList;
017    import java.util.List;
018    
019    /**
020     * <p><code>StackLayout</code> is a Swing layout aimed to act as the layers
021     * stack of most popuplar graphics editing tools like <i>The GIMP</i> or
022     * <i>Photoshop</i>. While similar to <code>CardLayout</code>, this layout
023     * displays all the components of the container. If you are using non-rectangular
024     * components (i.e. transparent) you will see them from top to bottom of the
025     * stack.</p>
026     * <p>When using this layout, each component can be added in the container
027     * either on top of the stack or at the bootom:</p>
028     * <pre>
029     * JPanel panel = new JPanel(new StackLayout());
030     * panel.add(new JLabel("On top"),    StackLayout.TOP);
031     * panel.add(new JLabel("At bottom"), StackLayout.BOTTOM);
032     * </pre>
033     * If you don't specify the constraint, the component will be added at the top
034     * of the components stack.</p>
035     * <p>All the components managed by this layout will be given the same size as
036     * the container itself. The minimum, maximum and preferred size of the 
037     * container are based upon the largest minimum, maximum and preferred size of
038     * the children components.</p>
039     * <p><code>StackLayout</code> works only with JSE 1.5 and Java SE 6 and
040     * greater.</p>
041     * 
042     * @author Romain Guy <romain.guy@mac.com>
043     */
044    
045    public class StackLayout implements LayoutManager2 {
046        /** Use this constraint to add a component at the bottom of the stack. */
047        public static final String BOTTOM = "bottom";
048        /** Use this contrainst to add a component at the top of the stack. */
049        public static final String TOP = "top";
050    
051        // removing components does not happen often compared to adding components
052        // hence we choose a linked list to make insertion at the bottom faster
053        private List<Component> components = new LinkedList<Component>();
054    
055        /**
056         * {@inheritDoc}
057         */
058        public void addLayoutComponent(final Component comp,
059                                       final Object constraints) {
060            synchronized (comp.getTreeLock()) {
061                if (BOTTOM.equals(constraints)) {
062                    components.add(0, comp);
063                } else if (TOP.equals(constraints)) {
064                    components.add(comp);
065                } else {
066                    components.add(comp);
067                }
068            }
069        }
070    
071        /**
072         * {@inheritDoc}
073         */
074        public void addLayoutComponent(final String name, final Component comp) {
075            addLayoutComponent(comp, TOP);
076        }
077    
078        /**
079         * {@inheritDoc}
080         */
081        public void removeLayoutComponent(final Component comp) {
082            synchronized (comp.getTreeLock()) {
083                components.remove(comp);
084            }
085        }
086        
087        /**
088         * {@inheritDoc}
089         */
090        public float getLayoutAlignmentX(final Container target) {
091            return 0.5f;
092        }
093    
094        /**
095         * {@inheritDoc}
096         */
097        public float getLayoutAlignmentY(final Container target) {
098            return 0.5f;
099        }
100    
101        /**
102         * {@inheritDoc}
103         */
104        public void invalidateLayout(final Container target) {
105        }
106    
107        /**
108         * {@inheritDoc}
109         */
110        public Dimension preferredLayoutSize(final Container parent) {
111            synchronized (parent.getTreeLock()) {
112                int width = 0;
113                int height = 0;
114    
115                for (Component comp: components) {
116                    Dimension size = comp.getPreferredSize();
117                    width = Math.max(size.width, width);
118                    height = Math.max(size.height, height);
119                }
120    
121                Insets insets = parent.getInsets();
122                width += insets.left + insets.right;
123                height += insets.top + insets.bottom;
124                
125                return new Dimension(width, height);
126            }
127        }
128    
129        /**
130         * {@inheritDoc}
131         */
132        public Dimension minimumLayoutSize(final Container parent) {
133            synchronized (parent.getTreeLock()) {
134                int width = 0;
135                int height = 0;
136    
137                for (Component comp: components) {
138                    Dimension size = comp.getMinimumSize();
139                    width = Math.max(size.width, width);
140                    height = Math.max(size.height, height);
141                }
142    
143                Insets insets = parent.getInsets();
144                width += insets.left + insets.right;
145                height += insets.top + insets.bottom;
146                
147                return new Dimension(width, height);
148            }
149        }
150    
151        /**
152         * {@inheritDoc}
153         */
154        public Dimension maximumLayoutSize(final Container target) {
155            return new Dimension(Integer.MAX_VALUE,
156                                 Integer.MAX_VALUE);
157        }
158    
159        /**
160         * {@inheritDoc}
161         */
162        public void layoutContainer(final Container parent) {
163            synchronized (parent.getTreeLock()) {
164                int width = parent.getWidth();
165                int height = parent.getHeight();
166                
167                Rectangle bounds = new Rectangle(0, 0, width, height);
168    
169                int componentsCount = components.size();
170                
171                for (int i = 0; i < componentsCount; i++) {
172                    Component comp = components.get(i);
173                    comp.setBounds(bounds);
174                    parent.setComponentZOrder(comp, componentsCount - i - 1);
175                }
176            }
177        }
178    }