content: /* ========================================================================
   * JCommon : a free general purpose class library for the Java(tm) platform
   * ========================================================================
   *
   * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   * 
   * Project Info:  http://www.jfree.org/jcommon/index.html
   *
   * This library is free software; you can redistribute it and/or modify it 
   * under the terms of the GNU Lesser General Public License as published by 
   * the Free Software Foundation; either version 2.1 of the License, or 
   * (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful, but 
   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
   * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
   * License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
   * USA.  
   *
   * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
   * in the United States and other countries.]
   * 
   * -----------------
   * RadialLayout.java
   * -----------------
   * (C) Copyright 2003, 2004, by Bryan Scott (for Australian Antarctic Division).
   *
   * Original Author:  Bryan Scott (for Australian Antarctic Division);
   * Contributor(s):   David Gilbert (for Object Refinery Limited);
   *
   *
   * Changes:
   * --------
   * 30-Jun-2003 : Version 1 (BS);
   * 24-Jul-2003 : Completed missing Javadocs (DG);
   *
   */
  
  package org.jfree.layout;
  
  import java.awt.Checkbox;
  import java.awt.Component;
  import java.awt.Container;
  import java.awt.Dimension;
  import java.awt.Frame;
  import java.awt.Insets;
  import java.awt.LayoutManager;
  import java.awt.Panel;
  import java.io.Serializable;
  
  /**
   * RadialLayout is a component layout manager.  Compents are laid out in a
   * circle. If only one component is contained in the layout it is positioned
   * centrally, otherwise components are evenly spaced around the centre with
   * the first component placed to the North.
   *<P>
   * This code was developed to display CTD rosette firing control
   *
   * WARNING: Not thoughly tested, use at own risk.
   * 
   * @author Bryan Scott (for Australian Antarctic Division)
   */
  
  public class RadialLayout implements LayoutManager, Serializable {
      
      /** For serialization. */
      private static final long serialVersionUID = -7582156799248315534L;
      
      /** The minimum width. */
      private int minWidth = 0;
      
      /** The minimum height. */
      private int minHeight = 0;
      
      /** The maximum component width. */
      private int maxCompWidth = 0;
      
      /** The maximum component height. */
      private int maxCompHeight = 0;
      
      /** The preferred width. */
      private int preferredWidth = 0;
      
      /** The preferred height. */
      private int preferredHeight = 0;
      
      /** Size unknown flag. */
      private boolean sizeUnknown = true;
  
      /** 
       * Constructs this layout manager with default properties. 
       */
      public RadialLayout() {
          super();
      }
  
      /**
       * Not used.
       *
       * @param comp  the component.
       */
      public void addLayoutComponent(final Component comp) {
          // not used
      }
  
      /**
       * Not used.
       *
       * @param comp  the component.
       */
      public void removeLayoutComponent(final Component comp) {
          // not used
      }
  
      /**
       * Not used.
       *
       * @param name  the component name.
       * @param comp  the component.
       */
      public void addLayoutComponent(final String name, final Component comp) {
          // not used
      }
  
      /**
       * Not used.
       *
       * @param name  the component name.
       * @param comp  the component.
       */
      public void removeLayoutComponent(final String name, final Component comp) {
          // not used
      }
  
      /**
       * Sets the sizes attribute of the RadialLayout object.
       *
       * @param  parent  the parent.
       * 
       * @see LayoutManager
       */
      private void setSizes(final Container parent) {
          final int nComps = parent.getComponentCount();
          //Reset preferred/minimum width and height.
          this.preferredWidth = 0;
          this.preferredHeight = 0;
          this.minWidth = 0;
          this.minHeight = 0;
          for (int i = 0; i < nComps; i++) {
              final Component c = parent.getComponent(i);
              if (c.isVisible()) {
                  final Dimension d = c.getPreferredSize();
                  if (this.maxCompWidth < d.width) {
                      this.maxCompWidth = d.width;
                  }
                  if (this.maxCompHeight < d.height) {
                      this.maxCompHeight = d.height;
                  }
                  this.preferredWidth += d.width;
                  this.preferredHeight += d.height;
              }
          }
          this.preferredWidth  = this.preferredWidth / 2;
          this.preferredHeight = this.preferredHeight / 2;
          this.minWidth = this.preferredWidth;
          this.minHeight = this.preferredHeight;
      }
  
      /**
       * Returns the preferred size.
       *
       * @param parent  the parent.
       *
       * @return The preferred size.
       * @see LayoutManager
       */
      public Dimension preferredLayoutSize(final Container parent) {
          final Dimension dim = new Dimension(0, 0);
          setSizes(parent);
  
          //Always add the container's insets!
          final Insets insets = parent.getInsets();
          dim.width = this.preferredWidth + insets.left + insets.right;
          dim.height = this.preferredHeight + insets.top + insets.bottom;
  
          this.sizeUnknown = false;
          return dim;
      }
  
      /**
       * Returns the minimum size.
       *
       * @param parent  the parent.
       *
       * @return The minimum size.
       * @see LayoutManager
       */
      public Dimension minimumLayoutSize(final Container parent) {
          final Dimension dim = new Dimension(0, 0);
  
          //Always add the container's insets!
          final Insets insets = parent.getInsets();
          dim.width = this.minWidth + insets.left + insets.right;
          dim.height = this.minHeight + insets.top + insets.bottom;
  
          this.sizeUnknown = false;
          return dim;
      }
  
     /**
      * This is called when the panel is first displayed, and every time its size
      * changes.
      * Note: You CAN'T assume preferredLayoutSize or minimumLayoutSize will be
      * called -- in the case of applets, at least, they probably won't be.
      *
      * @param  parent  the parent.
      * @see LayoutManager
      */
      public void layoutContainer(final Container parent) {
          final Insets insets = parent.getInsets();
          final int maxWidth = parent.getSize().width 
              - (insets.left + insets.right);
          final int maxHeight = parent.getSize().height 
              - (insets.top + insets.bottom);
          final int nComps = parent.getComponentCount();
          int x = 0;
          int y = 0;
  
          // Go through the components' sizes, if neither preferredLayoutSize nor
          // minimumLayoutSize has been called.
          if (this.sizeUnknown) {
              setSizes(parent);
          }
  
          if (nComps < 2) {
              final Component c = parent.getComponent(0);
              if (c.isVisible()) {
                  final Dimension d = c.getPreferredSize();
                  c.setBounds(x, y, d.width, d.height);
              }
          } 
          else {
              double radialCurrent = Math.toRadians(90);
              final double radialIncrement = 2 * Math.PI / nComps;
              final int midX = maxWidth / 2;
              final int midY = maxHeight / 2;
              final int a = midX - this.maxCompWidth;
              final int b = midY - this.maxCompHeight;
              for (int i = 0; i < nComps; i++) {
                  final Component c = parent.getComponent(i);
                  if (c.isVisible()) {
                      final Dimension d = c.getPreferredSize();
                      x = (int) (midX
                                 - (a * Math.cos(radialCurrent))
                                 - (d.getWidth() / 2)
                                 + insets.left);
                      y = (int) (midY
                                 - (b * Math.sin(radialCurrent))
                                 - (d.getHeight() / 2)
                                 + insets.top);
  
                      // Set the component's size and position.
                      c.setBounds(x, y, d.width, d.height);
                  }
                  radialCurrent += radialIncrement;
              }
          }
      }
  
      /**
       * Returns the class name.
       * 
       * @return The class name.
       */
      public String toString() {
          return getClass().getName();
      }
  
      /**
       * Run a demonstration.
       *
       * @param args  ignored.
       * 
       * @throws Exception when an error occurs.
       */
      public static void main(final String[] args) throws Exception {
          final Frame frame = new Frame();
          final Panel panel = new Panel();
          panel.setLayout(new RadialLayout());
  
          panel.add(new Checkbox("One"));
          panel.add(new Checkbox("Two"));
          panel.add(new Checkbox("Three"));
          panel.add(new Checkbox("Four"));
          panel.add(new Checkbox("Five"));
          panel.add(new Checkbox("One"));
          panel.add(new Checkbox("Two"));
          panel.add(new Checkbox("Three"));
          panel.add(new Checkbox("Four"));
          panel.add(new Checkbox("Five"));
  
          frame.add(panel);
          frame.setSize(300, 500);
          frame.setVisible(true);
      }
  
  }
  