001    /* ========================================================================
002     * JCommon : a free general purpose class library for the Java(tm) platform
003     * ========================================================================
004     *
005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006     * 
007     * Project Info:  http://www.jfree.org/jcommon/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it 
010     * under the terms of the GNU Lesser General Public License as published by 
011     * the Free Software Foundation; either version 2.1 of the License, or 
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but 
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022     * USA.  
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025     * in the United States and other countries.]
026     * 
027     * -----------------
028     * RadialLayout.java
029     * -----------------
030     * (C) Copyright 2003, 2004, by Bryan Scott (for Australian Antarctic Division).
031     *
032     * Original Author:  Bryan Scott (for Australian Antarctic Division);
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *
035     *
036     * Changes:
037     * --------
038     * 30-Jun-2003 : Version 1 (BS);
039     * 24-Jul-2003 : Completed missing Javadocs (DG);
040     *
041     */
042    
043    package org.jfree.layout;
044    
045    import java.awt.Checkbox;
046    import java.awt.Component;
047    import java.awt.Container;
048    import java.awt.Dimension;
049    import java.awt.Frame;
050    import java.awt.Insets;
051    import java.awt.LayoutManager;
052    import java.awt.Panel;
053    import java.io.Serializable;
054    
055    /**
056     * RadialLayout is a component layout manager.  Compents are laid out in a
057     * circle. If only one component is contained in the layout it is positioned
058     * centrally, otherwise components are evenly spaced around the centre with
059     * the first component placed to the North.
060     *<P>
061     * This code was developed to display CTD rosette firing control
062     *
063     * WARNING: Not thoughly tested, use at own risk.
064     * 
065     * @author Bryan Scott (for Australian Antarctic Division)
066     */
067    
068    public class RadialLayout implements LayoutManager, Serializable {
069        
070        /** For serialization. */
071        private static final long serialVersionUID = -7582156799248315534L;
072        
073        /** The minimum width. */
074        private int minWidth = 0;
075        
076        /** The minimum height. */
077        private int minHeight = 0;
078        
079        /** The maximum component width. */
080        private int maxCompWidth = 0;
081        
082        /** The maximum component height. */
083        private int maxCompHeight = 0;
084        
085        /** The preferred width. */
086        private int preferredWidth = 0;
087        
088        /** The preferred height. */
089        private int preferredHeight = 0;
090        
091        /** Size unknown flag. */
092        private boolean sizeUnknown = true;
093    
094        /** 
095         * Constructs this layout manager with default properties. 
096         */
097        public RadialLayout() {
098            super();
099        }
100    
101        /**
102         * Not used.
103         *
104         * @param comp  the component.
105         */
106        public void addLayoutComponent(final Component comp) {
107            // not used
108        }
109    
110        /**
111         * Not used.
112         *
113         * @param comp  the component.
114         */
115        public void removeLayoutComponent(final Component comp) {
116            // not used
117        }
118    
119        /**
120         * Not used.
121         *
122         * @param name  the component name.
123         * @param comp  the component.
124         */
125        public void addLayoutComponent(final String name, final Component comp) {
126            // not used
127        }
128    
129        /**
130         * Not used.
131         *
132         * @param name  the component name.
133         * @param comp  the component.
134         */
135        public void removeLayoutComponent(final String name, final Component comp) {
136            // not used
137        }
138    
139        /**
140         * Sets the sizes attribute of the RadialLayout object.
141         *
142         * @param  parent  the parent.
143         * 
144         * @see LayoutManager
145         */
146        private void setSizes(final Container parent) {
147            final int nComps = parent.getComponentCount();
148            //Reset preferred/minimum width and height.
149            this.preferredWidth = 0;
150            this.preferredHeight = 0;
151            this.minWidth = 0;
152            this.minHeight = 0;
153            for (int i = 0; i < nComps; i++) {
154                final Component c = parent.getComponent(i);
155                if (c.isVisible()) {
156                    final Dimension d = c.getPreferredSize();
157                    if (this.maxCompWidth < d.width) {
158                        this.maxCompWidth = d.width;
159                    }
160                    if (this.maxCompHeight < d.height) {
161                        this.maxCompHeight = d.height;
162                    }
163                    this.preferredWidth += d.width;
164                    this.preferredHeight += d.height;
165                }
166            }
167            this.preferredWidth  = this.preferredWidth / 2;
168            this.preferredHeight = this.preferredHeight / 2;
169            this.minWidth = this.preferredWidth;
170            this.minHeight = this.preferredHeight;
171        }
172    
173        /**
174         * Returns the preferred size.
175         *
176         * @param parent  the parent.
177         *
178         * @return The preferred size.
179         * @see LayoutManager
180         */
181        public Dimension preferredLayoutSize(final Container parent) {
182            final Dimension dim = new Dimension(0, 0);
183            setSizes(parent);
184    
185            //Always add the container's insets!
186            final Insets insets = parent.getInsets();
187            dim.width = this.preferredWidth + insets.left + insets.right;
188            dim.height = this.preferredHeight + insets.top + insets.bottom;
189    
190            this.sizeUnknown = false;
191            return dim;
192        }
193    
194        /**
195         * Returns the minimum size.
196         *
197         * @param parent  the parent.
198         *
199         * @return The minimum size.
200         * @see LayoutManager
201         */
202        public Dimension minimumLayoutSize(final Container parent) {
203            final Dimension dim = new Dimension(0, 0);
204    
205            //Always add the container's insets!
206            final Insets insets = parent.getInsets();
207            dim.width = this.minWidth + insets.left + insets.right;
208            dim.height = this.minHeight + insets.top + insets.bottom;
209    
210            this.sizeUnknown = false;
211            return dim;
212        }
213    
214       /**
215        * This is called when the panel is first displayed, and every time its size
216        * changes.
217        * Note: You CAN'T assume preferredLayoutSize or minimumLayoutSize will be
218        * called -- in the case of applets, at least, they probably won't be.
219        *
220        * @param  parent  the parent.
221        * @see LayoutManager
222        */
223        public void layoutContainer(final Container parent) {
224            final Insets insets = parent.getInsets();
225            final int maxWidth = parent.getSize().width 
226                - (insets.left + insets.right);
227            final int maxHeight = parent.getSize().height 
228                - (insets.top + insets.bottom);
229            final int nComps = parent.getComponentCount();
230            int x = 0;
231            int y = 0;
232    
233            // Go through the components' sizes, if neither preferredLayoutSize nor
234            // minimumLayoutSize has been called.
235            if (this.sizeUnknown) {
236                setSizes(parent);
237            }
238    
239            if (nComps < 2) {
240                final Component c = parent.getComponent(0);
241                if (c.isVisible()) {
242                    final Dimension d = c.getPreferredSize();
243                    c.setBounds(x, y, d.width, d.height);
244                }
245            } 
246            else {
247                double radialCurrent = Math.toRadians(90);
248                final double radialIncrement = 2 * Math.PI / nComps;
249                final int midX = maxWidth / 2;
250                final int midY = maxHeight / 2;
251                final int a = midX - this.maxCompWidth;
252                final int b = midY - this.maxCompHeight;
253                for (int i = 0; i < nComps; i++) {
254                    final Component c = parent.getComponent(i);
255                    if (c.isVisible()) {
256                        final Dimension d = c.getPreferredSize();
257                        x = (int) (midX
258                                   - (a * Math.cos(radialCurrent))
259                                   - (d.getWidth() / 2)
260                                   + insets.left);
261                        y = (int) (midY
262                                   - (b * Math.sin(radialCurrent))
263                                   - (d.getHeight() / 2)
264                                   + insets.top);
265    
266                        // Set the component's size and position.
267                        c.setBounds(x, y, d.width, d.height);
268                    }
269                    radialCurrent += radialIncrement;
270                }
271            }
272        }
273    
274        /**
275         * Returns the class name.
276         * 
277         * @return The class name.
278         */
279        public String toString() {
280            return getClass().getName();
281        }
282    
283        /**
284         * Run a demonstration.
285         *
286         * @param args  ignored.
287         * 
288         * @throws Exception when an error occurs.
289         */
290        public static void main(final String[] args) throws Exception {
291            final Frame frame = new Frame();
292            final Panel panel = new Panel();
293            panel.setLayout(new RadialLayout());
294    
295            panel.add(new Checkbox("One"));
296            panel.add(new Checkbox("Two"));
297            panel.add(new Checkbox("Three"));
298            panel.add(new Checkbox("Four"));
299            panel.add(new Checkbox("Five"));
300            panel.add(new Checkbox("One"));
301            panel.add(new Checkbox("Two"));
302            panel.add(new Checkbox("Three"));
303            panel.add(new Checkbox("Four"));
304            panel.add(new Checkbox("Five"));
305    
306            frame.add(panel);
307            frame.setSize(300, 500);
308            frame.setVisible(true);
309        }
310    
311    }