Source for org.jfree.layout.RadialLayout

   1: /* ========================================================================
   2:  * JCommon : a free general purpose class library for the Java(tm) platform
   3:  * ========================================================================
   4:  *
   5:  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   6:  * 
   7:  * Project Info:  http://www.jfree.org/jcommon/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  * 
  27:  * -----------------
  28:  * RadialLayout.java
  29:  * -----------------
  30:  * (C) Copyright 2003, 2004, by Bryan Scott (for Australian Antarctic Division).
  31:  *
  32:  * Original Author:  Bryan Scott (for Australian Antarctic Division);
  33:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  34:  *
  35:  *
  36:  * Changes:
  37:  * --------
  38:  * 30-Jun-2003 : Version 1 (BS);
  39:  * 24-Jul-2003 : Completed missing Javadocs (DG);
  40:  *
  41:  */
  42: 
  43: package org.jfree.layout;
  44: 
  45: import java.awt.Checkbox;
  46: import java.awt.Component;
  47: import java.awt.Container;
  48: import java.awt.Dimension;
  49: import java.awt.Frame;
  50: import java.awt.Insets;
  51: import java.awt.LayoutManager;
  52: import java.awt.Panel;
  53: import java.io.Serializable;
  54: 
  55: /**
  56:  * RadialLayout is a component layout manager.  Compents are laid out in a
  57:  * circle. If only one component is contained in the layout it is positioned
  58:  * centrally, otherwise components are evenly spaced around the centre with
  59:  * the first component placed to the North.
  60:  *<P>
  61:  * This code was developed to display CTD rosette firing control
  62:  *
  63:  * WARNING: Not thoughly tested, use at own risk.
  64:  * 
  65:  * @author Bryan Scott (for Australian Antarctic Division)
  66:  */
  67: 
  68: public class RadialLayout implements LayoutManager, Serializable {
  69:     
  70:     /** For serialization. */
  71:     private static final long serialVersionUID = -7582156799248315534L;
  72:     
  73:     /** The minimum width. */
  74:     private int minWidth = 0;
  75:     
  76:     /** The minimum height. */
  77:     private int minHeight = 0;
  78:     
  79:     /** The maximum component width. */
  80:     private int maxCompWidth = 0;
  81:     
  82:     /** The maximum component height. */
  83:     private int maxCompHeight = 0;
  84:     
  85:     /** The preferred width. */
  86:     private int preferredWidth = 0;
  87:     
  88:     /** The preferred height. */
  89:     private int preferredHeight = 0;
  90:     
  91:     /** Size unknown flag. */
  92:     private boolean sizeUnknown = true;
  93: 
  94:     /** 
  95:      * Constructs this layout manager with default properties. 
  96:      */
  97:     public RadialLayout() {
  98:         super();
  99:     }
 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: }