//============================================================================
//ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C#
//Copyright © 2004  John Champion
//
//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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//=============================================================================
#region Using directives
using System;
using System.Text;
using System.Drawing;
using System.Collections;
using System.Runtime.Serialization;
using System.Security.Permissions;
#endregion
namespace DrawGraph
{
	/// 
	/// A collection class containing a list of  objects
	/// organized together in some form.
	/// 
	/// 
	/// John Champion
	///  $Revision: 3.24 $ $Date: 2007/06/02 06:56:03 $ 
	[Serializable]
	public class MasterPane : PaneBase, ICloneable, ISerializable, IDeserializationCallback
	{
	#region Fields
		/// 
		/// Private field that holds a collection of  objects for inclusion
		/// in this .  Use the public property 
		/// to access this collection.
		/// 
		internal PaneList _paneList;
		/// 
		/// Private field that sets the amount of space between the GraphPanes.  Use the public property
		///  to access this value;
		/// 
		internal float _innerPaneGap;
		/// 
		///Private field that stores a boolean value which signifies whether all 
		///s in the chart use the same entries in their 
		///  If set to true, only one set of entries will be displayed in 
		///this  instance.  If set to false, this instance will display all 
		///entries from all s.
		/// 
		private bool _isUniformLegendEntries;
		/// 
		/// private field that determines if the
		/// 
		/// function will automatically set
		/// the  of each  in the
		///  such that the scale factors have the same value.
		/// 
		private bool _isCommonScaleFactor;
		/// 
		/// private field that saves the paneLayout format specified when
		///  was called. This value will
		/// default to  if
		///  (or an overload) was never called.
		/// 
		internal PaneLayout _paneLayout;
		/// 
		/// Private field that stores the boolean value that determines whether
		///  is specifying rows or columns.
		/// 
		internal bool _isColumnSpecified;
		/// 
		/// private field that stores the row/column item count that was specified to the
		///  method.  This values will be
		/// null if  was never called.
		/// 
		internal int[] _countList;
		/// 
		/// private field that stores the row/column size proportional values as specified
		/// to the  method.  This
		/// value will be null if 
		/// was never called.  
		/// 
		internal float[] _prop;
/*		/// 
		/// private field to store the  instance, which
		/// manages the persistence and handling of pane layout information.
		/// 
		private PaneLayoutMgr _paneLayoutMgr;
*/
	#endregion
	#region Defaults
		/// 
		/// A simple struct that defines the
		/// default property values for the  class.
		/// 
		public new struct Default
		{
			/// 
			/// The default pane layout for
			/// 
			/// method calls.
			/// 
			/// 
			/// 
			/// 
			/// 
			/// 
			public static PaneLayout PaneLayout = PaneLayout.SquareColPreferred;
			/// 
			/// The default value for the  property.
			/// This is the size of the margin between adjacent 
			/// objects, in units of points (1/72 inch).
			/// 
			/// 
			public static float InnerPaneGap = 10;
			
			/// 
			/// The default value for the  property for
			/// the  class.
			/// 
			public static bool IsShowLegend = false;
			/// 
			/// The default value for the  property.
			/// 
			public static bool IsUniformLegendEntries = false;
			/// 
			/// The default value for the  property.
			/// 
			public static bool IsCommonScaleFactor = false;
		}
	#endregion
	#region Properties
		/// 
		/// Gets or sets the  collection instance that holds the list of
		///  objects that are included in this .
		/// 
		/// 
		/// 
		public PaneList PaneList
		{
			get { return _paneList; }
			set { _paneList = value; }
		}
/*
		/// 
		/// Gets the  instance, which manages the pane layout
		/// settings, and handles the layout functions.
		/// 
		/// 
		/// 
		/// 
		/// 
		/// 
		public PaneLayoutMgr PaneLayoutMgr
		{
			get { return _paneLayoutMgr; }
		}
*/
		/// 
		/// Gets or sets the size of the margin between adjacent 
		/// objects.
		/// 
		/// This property is scaled according to ,
		/// based on .  The default value comes from
		/// .
		/// 
		/// The value is in points (1/72nd inch).
		public float InnerPaneGap
		{
			get { return _innerPaneGap; }
			set { _innerPaneGap = value; }
		}
		/// 
		/// Gets or set the value of the	 
		/// 
		public bool IsUniformLegendEntries
		{
			get { return (_isUniformLegendEntries); }
			set { _isUniformLegendEntries = value; } 
		}
		/// 
		/// Gets or sets a value that determines if the
		///  method will automatically set the
		/// 
		/// of each  in the  such that the
		/// scale factors have the same value.
		/// 
		/// 
		/// The scale factors, calculated by , determine
		/// scaled font sizes, tic lengths, etc.  This function will insure that for
		/// multiple graphpanes, a certain specified font size will be the same for
		/// all the panes.
		/// 
		/// 
		/// 
		/// 
		/// 
		/// 
		public bool IsCommonScaleFactor
		{
			get { return _isCommonScaleFactor; }
			set { _isCommonScaleFactor = value; }
		}
	#endregion
	
	#region Constructors
		/// 
		/// Default constructor for the class.  Sets the  to (0, 0, 500, 375).
		/// 
		public MasterPane() : this( "", new RectangleF( 0, 0, 500, 375 ) )
		{
		}
		
		/// 
		/// Default constructor for the class.  Specifies the  of
		/// the , and the size of the .
		/// 
		public MasterPane( string title, RectangleF paneRect ) : base( title, paneRect )
		{
			_innerPaneGap = Default.InnerPaneGap;
			//_paneLayoutMgr = new PaneLayoutMgr();
			_isUniformLegendEntries = Default.IsUniformLegendEntries ;
			_isCommonScaleFactor = Default.IsCommonScaleFactor;
			_paneList = new PaneList();
			_legend.IsVisible = Default.IsShowLegend;
			InitLayout();
		}
		private void InitLayout()
		{
			_paneLayout = Default.PaneLayout;
			_countList = null;
			_isColumnSpecified = false;
			_prop = null;
		}
		/// 
		/// The Copy Constructor - Make a deep-copy clone of this class instance.
		/// 
		/// The  object from which to copy
		public MasterPane( MasterPane rhs ) : base( rhs )
		{
			// copy all the value types
			//_paneLayoutMgr = rhs._paneLayoutMgr.Clone();
			_innerPaneGap = rhs._innerPaneGap;
			_isUniformLegendEntries = rhs._isUniformLegendEntries;
			_isCommonScaleFactor = rhs._isCommonScaleFactor;
			// Then, fill in all the reference types with deep copies
			_paneList = rhs._paneList.Clone();
			_paneLayout = rhs._paneLayout;
			_countList = rhs._countList;
			_isColumnSpecified = rhs._isColumnSpecified;
			_prop = rhs._prop;
		}
		/// 
		/// Implement the  interface in a typesafe manner by just
		/// calling the typed version of  to make a deep copy.
		/// 
		/// A deep copy of this object
		object ICloneable.Clone()
		{
			return this.Clone();
		}
		/// 
		/// Typesafe, deep-copy clone method.
		/// 
		/// A new, independent copy of this class
		public MasterPane Clone()
		{
			return new MasterPane( this );
		}
	#endregion
	#region Serialization
		/// 
		/// Current schema value that defines the version of the serialized file
		/// 
		// schema changed to 2 with addition of 'prop'
		public const int schema2 = 10;
		/// 
		/// Constructor for deserializing objects
		/// 
		/// A  instance that defines the serialized data
		/// 
		/// A  instance that contains the serialized data
		/// 
		protected MasterPane( SerializationInfo info, StreamingContext context ) : base( info, context )
		{
			// The schema value is just a file version parameter.  You can use it to make future versions
			// backwards compatible as new member variables are added to classes
			int sch = info.GetInt32( "schema2" );
			_paneList = (PaneList) info.GetValue( "paneList", typeof(PaneList) );
			//_paneLayoutMgr = (PaneLayoutMgr) info.GetValue( "paneLayoutMgr", typeof(PaneLayoutMgr) );
			_innerPaneGap = info.GetSingle( "innerPaneGap" );
			_isUniformLegendEntries = info.GetBoolean( "isUniformLegendEntries" );
			_isCommonScaleFactor = info.GetBoolean( "isCommonScaleFactor" );
			_paneLayout = (PaneLayout)info.GetValue( "paneLayout", typeof( PaneLayout ) );
			_countList = (int[])info.GetValue( "countList", typeof( int[] ) );
			_isColumnSpecified = info.GetBoolean( "isColumnSpecified" );
			_prop = (float[])info.GetValue( "prop", typeof( float[] ) );
		}
		/// 
		/// Populates a  instance with the data needed to serialize the target object
		/// 
		/// A  instance that defines the serialized data
		/// A  instance that contains the serialized data
		[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
		public override void GetObjectData( SerializationInfo info, StreamingContext context )
		{
			base.GetObjectData( info, context );
			info.AddValue( "schema2", schema2 );
			info.AddValue( "paneList", _paneList );
			//info.AddValue( "paneLayoutMgr", _paneLayoutMgr );
			info.AddValue( "innerPaneGap", _innerPaneGap );
			info.AddValue( "isUniformLegendEntries", _isUniformLegendEntries );
			info.AddValue( "isCommonScaleFactor", _isCommonScaleFactor );
			info.AddValue( "paneLayout", _paneLayout );
			info.AddValue( "countList", _countList );
			info.AddValue( "isColumnSpecified", _isColumnSpecified );
			info.AddValue( "prop", _prop );
		}
		/// 
		/// Respond to the callback when the MasterPane objects are fully initialized.
		/// 
		/// 
		public void OnDeserialization(object sender)
		{
			Bitmap bitmap = new Bitmap( 10, 10 );
			Graphics g = Graphics.FromImage( bitmap );
			ReSize( g, _rect );
		}
	#endregion
	#region List Methods
		/// 
		/// Indexer to access the specified  object from 
		/// by its ordinal position in the list.
		/// 
		/// The ordinal position (zero-based) of the
		///  object to be accessed.
		/// A  object reference.
		public GraphPane this[ int index ]  
		{
			get { return( (GraphPane) _paneList[index] ); }
			set { _paneList[index] = value; }
		}
		/// 
		/// Indexer to access the specified  object from 
		/// by its  string.
		/// 
		/// The string title of the
		///  object to be accessed.
		/// A  object reference.
		public GraphPane this[ string title ]  
		{
			get { return _paneList[title]; }
		}
		/// 
		/// Add a  object to the  collection at the end of the list.
		/// 
		/// A reference to the  object to
		/// be added
		/// 
		public void Add( GraphPane pane )
		{
			_paneList.Add( pane );
		}
		/// 
		/// Call  for all  objects in the
		///  list.
		/// 
		/// 
		/// This overload of AxisChange just uses the default Graphics instance for the screen.
		/// If you have a Graphics instance available from your Windows Form, you should use
		/// the  overload instead.
		/// 
		public void AxisChange()
		{
			using ( Graphics g = Graphics.FromHwnd( IntPtr.Zero ) )
				AxisChange( g );
		}
		/// 
		/// Call  for all  objects in the
		///  list.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		public void AxisChange( Graphics g )
		{
			foreach ( GraphPane pane in _paneList )
				pane.AxisChange( g );
		}
		/// 
		/// Redo the layout using the current size of the ,
		/// and also handle resizing the
		/// contents by calling .
		/// 
		/// This method will use the pane layout that was specified by a call to
		/// .  If
		///  has not previously been called,
		/// it will default to .
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// 
		/// 
		/// 
		public void ReSize( Graphics g )
		{
			ReSize( g, _rect );
		}
		/// 
		/// Change the size of the , and also handle resizing the
		/// contents by calling .
		/// 
		/// This method will use the pane layout that was specified by a call to
		/// .  If
		///  has not previously been called,
		/// it will default to .
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// 
		/// 
		/// 
		/// 
		public override void ReSize( Graphics g, RectangleF rect )
		{
			_rect = rect;
			DoLayout( g );
			CommonScaleFactor();
		}
		/// 
		/// Method that forces the scale factor calculations
		/// (via ),
		/// to give a common scale factor for all  objects in the
		/// .
		/// 
		/// 
		/// This will make it such that a given font size will result in the same output font
		/// size for all 's.  Note that this does not make the scale
		/// factor for the 's the same as that of the
		/// .
		/// 
		/// 
		public void CommonScaleFactor()
		{
			if ( _isCommonScaleFactor )
			{
				// Find the maximum scaleFactor of all the GraphPanes
				float maxFactor = 0;
				foreach ( GraphPane pane in PaneList )
				{
					pane.BaseDimension = PaneBase.Default.BaseDimension;
					float scaleFactor = pane.CalcScaleFactor();
					maxFactor = scaleFactor > maxFactor ? scaleFactor : maxFactor;
				}
				// Now, calculate the base dimension
				foreach ( GraphPane pane in PaneList )
				{
					float scaleFactor = pane.CalcScaleFactor();
					pane.BaseDimension *= scaleFactor / maxFactor;
				}
			}
		}
		/// 
		/// Render all the  objects in the  to the
		/// specified graphics device.
		/// 
		/// This method should be part of the Paint() update process.  Calling this routine
		/// will redraw all
		/// features of all the  items.  No preparation is required other than
		/// instantiated  objects that have been added to the list with the
		///  method.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		public override void Draw( Graphics g )
		{			
			// Draw the pane border & background fill, the title, and the GraphObj objects that lie at
			// ZOrder.GBehindAll
			base.Draw( g );
			if ( _rect.Width <= 1 || _rect.Height <= 1 )
				return;
			float scaleFactor = CalcScaleFactor();
			// Clip everything to the rect
			g.SetClip( _rect );
			// For the MasterPane, All GraphItems go behind the GraphPanes, except those that
			// are explicity declared as ZOrder.AInFront
			_graphObjList.Draw( g, this, scaleFactor, ZOrder.G_BehindChartFill );
			_graphObjList.Draw( g, this, scaleFactor, ZOrder.E_BehindCurves );
			_graphObjList.Draw( g, this, scaleFactor, ZOrder.D_BehindAxis );
			_graphObjList.Draw( g, this, scaleFactor, ZOrder.C_BehindChartBorder );
			// Reset the clipping
			g.ResetClip();
			foreach ( GraphPane pane in _paneList )
				pane.Draw( g );
			// Clip everything to the rect
			g.SetClip( _rect );
			_graphObjList.Draw( g, this, scaleFactor, ZOrder.B_BehindLegend );
			
			// Recalculate the legend rect, just in case it has not yet been done
			// innerRect is the area for the GraphPane's
			RectangleF innerRect = CalcClientRect( g, scaleFactor ); 
			_legend.CalcRect( g, this, scaleFactor, ref innerRect ); 
			
			_legend.Draw( g, this, scaleFactor );
			_graphObjList.Draw( g, this, scaleFactor, ZOrder.A_InFront );
			
			// Reset the clipping
			g.ResetClip();
		}
		/// 
		/// Find the pane and the object within that pane that lies closest to the specified
		/// mouse (screen) point.
		/// 
		/// 
		/// This method first finds the  within the list that contains
		/// the specified mouse point.  It then calls the 
		/// method to determine which object, if any, was clicked.  With the exception of the
		/// , all the parameters in this method are identical to those
		/// in the  method.
		/// If the mouse point lies within the  of any 
		///  item, then that pane will be returned (otherwise it will be
		/// null).  Further, within the selected pane, if the mouse point is within the
		/// bounding box of any of the items (or in the case
		/// of  and , within
		///  pixels), then the object will be returned.
		/// You must check the type of the object to determine what object was
		/// selected (for example, "if ( object is Legend ) ...").  The
		///  parameter returns the index number of the item
		/// within the selected object (such as the point number within a
		///  object.
		/// 
		/// The screen point, in pixel coordinates.
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// A reference to the  object that was clicked.
		/// A reference to the nearest object to the
		/// specified screen point.  This can be any of ,
		/// , ,
		/// , , or .
		/// Note: If the pane title is selected, then the  object
		/// will be returned.
		/// 
		/// The index number of the item within the selected object
		/// (where applicable).  For example, for a  object,
		///  will be the index number of the nearest data point,
		/// accessible via CurveItem.Points[index].
		/// index will be -1 if no data points are available.
		/// true if a  was found, false otherwise.
		/// 
		public bool FindNearestPaneObject( PointF mousePt, Graphics g, out GraphPane pane,
			out object nearestObj, out int index )
		{
			pane = null;
			nearestObj = null;
			index = -1;
			GraphObj	saveGraphItem = null;
			int			saveIndex = -1;
			float		scaleFactor = CalcScaleFactor();
			// See if the point is in a GraphObj
			// If so, just save the object and index so we can see if other overlying objects were
			// intersected as well.
			if ( this.GraphObjList.FindPoint( mousePt, this, g, scaleFactor, out index ) )
			{
				saveGraphItem = this.GraphObjList[index];
				saveIndex = index;
				// If it's an "In-Front" item, then just return it
				if ( saveGraphItem.ZOrder == ZOrder.A_InFront )
				{
					nearestObj = saveGraphItem;
					index = saveIndex;
					return true;
				}
			}
			foreach ( GraphPane tPane in _paneList )
			{
				if ( tPane.Rect.Contains( mousePt ) )
				{
					pane = tPane;
					return tPane.FindNearestObject( mousePt, g, out nearestObj, out index );
				}
			}
			// If no items were found in the GraphPanes, then return the item found on the MasterPane (if any)
			if ( saveGraphItem != null )
			{
				nearestObj = saveGraphItem;
				index = saveIndex;
				return true;
			}
			return false;
		}
		/// 
		/// Find the  within the  that contains the
		///  within its .
		/// 
		/// The mouse point location where you want to search
		/// A  object that contains the mouse point, or
		/// null if no  was found.
		public GraphPane FindPane( PointF mousePt )
		{
			foreach ( GraphPane pane in _paneList )
			{
				if ( pane.Rect.Contains( mousePt ) )
					return pane;
			}
			
			return null;
		}
		/// 
		/// Find the  within the  that contains the
		///  within its .
		/// 
		/// The mouse point location where you want to search
		/// A  object that contains the mouse point, or
		/// null if no  was found.
		public GraphPane FindChartRect( PointF mousePt )
		{
			foreach ( GraphPane pane in _paneList )
			{
				if ( pane.Chart._rect.Contains( mousePt ) )
					return pane;
			}
			
			return null;
		}
	#endregion
	#region Layout Methods
		/// The SetLayout() methods setup the desired layout of the
		///  objects within a .  These functions
		/// do not make any changes, they merely set the parameters so that future calls
		/// to  or 
		/// will use the desired layout.
		/// The layout options include a set of "canned" layouts provided by the
		///  enumeration, options to just set a specific
		/// number of rows and columns of panes (and all pane sizes are the same), and more
		/// customized options of specifying the number or rows in each column or the number of
		/// columns in each row, along with proportional values that determine the size of each
		/// individual column or row.
		/// 
		/// 
		/// Automatically set all of the  's in
		/// the list to a pre-defined layout configuration from a 
		/// enumeration.
		/// 
		/// This method uses a  enumeration to describe the type of layout
		/// to be used.  Overloads are available that provide other layout options
		/// A  enumeration that describes how
		/// the panes should be laid out within the .
		/// 
		/// A graphic device object to be drawn into.  This is normally created with a call to
		/// the CreateGraphics() method of the Control or Form.
		/// 
		/// 
		/// 
		/// 
		public void SetLayout( Graphics g, PaneLayout paneLayout )
		{
			InitLayout();
			_paneLayout = paneLayout;
			DoLayout( g );
		}
		/// 
		/// Automatically set all of the  's in
		/// the list to a reasonable configuration.
		/// 
		/// This method explicitly specifies the number of rows and columns to use
		/// in the layout, and all  objects will have the same size.
		/// Overloads are available that provide other layout options
		/// 
		/// A graphic device object to be drawn into.  This is normally created with a call to
		/// the CreateGraphics() method of the Control or Form.
		/// 
		/// The number of rows of  objects
		/// to include in the layout
		/// The number of columns of  objects
		/// to include in the layout
		/// 
		/// 
		/// 
		public void SetLayout( Graphics g, int rows, int columns )
		{
			InitLayout();
			if ( rows < 1 )
				rows = 1;
			if ( columns < 1 )
				columns = 1;
			int[] countList = new int[rows];
			for ( int i = 0; i < rows; i++ )
				countList[i] = columns;
			SetLayout( g, true, countList, null );
		}
		/// 
		/// Automatically set all of the  's in
		/// the list to the specified configuration.
		/// 
		/// This method specifies the number of rows in each column, or the number of
		/// columns in each row, allowing for irregular layouts.  Overloads are available that
		/// provide other layout options.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally created with a call to
		/// the CreateGraphics() method of the Control or Form.
		/// 
		/// Specifies whether the number of columns in each row, or
		/// the number of rows in each column will be specified.  A value of true indicates the
		/// number of columns in each row are specified in .
		/// An integer array specifying either the number of columns in
		/// each row or the number of rows in each column, depending on the value of
		/// .
		/// 
		/// 
		/// 
		public void SetLayout( Graphics g, bool isColumnSpecified, int[] countList )
		{
			SetLayout( g, isColumnSpecified, countList, null );
		}
		/// 
		/// Automatically set all of the  's in
		/// the list to the specified configuration.
		/// 
		/// This method specifies the number of panes in each row or column, allowing for
		/// irregular layouts.
		/// This method specifies the number of rows in each column, or the number of
		/// columns in each row, allowing for irregular layouts.  Additionally, a
		///  parameter is provided that allows varying column or
		/// row sizes.  Overloads for SetLayout() are available that provide other layout options.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally created with a call to
		/// the CreateGraphics() method of the Control or Form.
		/// 
		/// Specifies whether the number of columns in each row, or
		/// the number of rows in each column will be specified.  A value of true indicates the
		/// number of columns in each row are specified in .
		/// An integer array specifying either the number of columns in
		/// each row or the number of rows in each column, depending on the value of
		/// .
		/// An array of float values specifying proportional sizes for each
		/// row or column.  Note that these proportions apply to the non-specified dimension -- that is,
		/// if  is true, then these proportions apply to the row
		/// heights, and if  is false, then these proportions apply
		/// to the column widths.  The values in this array are arbitrary floats -- the dimension of
		/// any given row or column is that particular proportional value divided by the sum of all
		/// the values.  For example, let  be true, and
		///  is an array with values of { 1.0, 2.0, 3.0 }.  The sum of
		/// those values is 6.0.  Therefore, the first row is 1/6th of the available height, the
		/// second row is 2/6th's of the available height, and the third row is 3/6th's of the
		/// available height.
		/// 
		/// 
		/// 
		/// 
		public void SetLayout( Graphics g, bool isColumnSpecified, int[] countList, float[] proportion )
		{
			InitLayout();
			// use defaults if the parameters are invalid
			if ( countList != null && countList.Length > 0 )
			{
				_prop = new float[countList.Length];
				// Sum up the total proportional factors
				float sumProp = 0.0f;
				for ( int i = 0; i < countList.Length; i++ )
				{
					_prop[i] = ( proportion == null || proportion.Length <= i || proportion[i] < 1e-10 ) ?
												1.0f : proportion[i];
					sumProp += _prop[i];
				}
				// Make prop sum to 1.0
				for ( int i = 0; i < countList.Length; i++ )
					_prop[i] /= sumProp;
				_isColumnSpecified = isColumnSpecified;
				_countList = countList;
				DoLayout( g );
			}
		}
		/// 
		/// Modify the   sizes of each
		///  such that they fit within the 
		/// in a pre-configured layout.
		/// 
		/// The  method (and overloads) is
		/// used for setting the layout configuration.
		/// 
		/// 
		/// 
		/// 
		public void DoLayout( Graphics g )
		{
			if ( _countList != null )
				DoLayout( g, _isColumnSpecified, _countList, _prop );
			else
			{
				int count = _paneList.Count;
				if ( count == 0 )
					return;
				int rows,
						cols,
						root = (int)( Math.Sqrt( (double)count ) + 0.9999999 );
				//float[] widthList = new float[5];
				switch ( _paneLayout )
				{
					case PaneLayout.ForceSquare:
						rows = root;
						cols = root;
						DoLayout( g, rows, cols );
						break;
					case PaneLayout.SingleColumn:
						rows = count;
						cols = 1;
						DoLayout( g, rows, cols );
						break;
					case PaneLayout.SingleRow:
						rows = 1;
						cols = count;
						DoLayout( g, rows, cols );
						break;
					default:
					case PaneLayout.SquareColPreferred:
						rows = root;
						cols = root;
						if ( count <= root * ( root - 1 ) )
							rows--;
						DoLayout( g, rows, cols );
						break;
					case PaneLayout.SquareRowPreferred:
						rows = root;
						cols = root;
						if ( count <= root * ( root - 1 ) )
							cols--;
						DoLayout( g, rows, cols );
						break;
					case PaneLayout.ExplicitCol12:
						DoLayout( g, true, new int[2] { 1, 2 }, null );
						break;
					case PaneLayout.ExplicitCol21:
						DoLayout( g, true, new int[2] { 2, 1 }, null );
						break;
					case PaneLayout.ExplicitCol23:
						DoLayout( g, true, new int[2] { 2, 3 }, null );
						break;
					case PaneLayout.ExplicitCol32:
						DoLayout( g, true, new int[2] { 3, 2 }, null );
						break;
					case PaneLayout.ExplicitRow12:
						DoLayout( g, false, new int[2] { 1, 2 }, null );
						break;
					case PaneLayout.ExplicitRow21:
						DoLayout( g, false, new int[2] { 2, 1 }, null );
						break;
					case PaneLayout.ExplicitRow23:
						DoLayout( g, false, new int[2] { 2, 3 }, null );
						break;
					case PaneLayout.ExplicitRow32:
						DoLayout( g, false, new int[2] { 3, 2 }, null );
						break;
				}
			}
		}
		/// 
		/// Internal method that applies a previously set layout with a specific
		/// row and column count.  This method is only called by
		/// .
		/// 
		internal void DoLayout( Graphics g, int rows, int columns )
		{
			if ( rows < 1 )
				rows = 1;
			if ( columns < 1 )
				columns = 1;
			int[] countList = new int[rows];
			for ( int i = 0; i < rows; i++ )
				countList[i] = columns;
			DoLayout( g, true, countList, null );
		}
		/// 
		/// Internal method that applies a previously set layout with a rows per column or
		/// columns per row configuration.  This method is only called by
		/// .
		/// 
		internal void DoLayout( Graphics g, bool isColumnSpecified, int[] countList,
					float[] proportion )
		{
			// calculate scaleFactor on "normal" pane size (BaseDimension)
			float scaleFactor = CalcScaleFactor();
			// innerRect is the area for the GraphPane's
			RectangleF innerRect = CalcClientRect( g, scaleFactor );
			_legend.CalcRect( g, this, scaleFactor, ref innerRect );
			// scaled InnerGap is the area between the GraphPane.Rect's
			float scaledInnerGap = (float)( _innerPaneGap * scaleFactor );
			int iPane = 0;
			if ( isColumnSpecified )
			{
				int rows = countList.Length;
				float y = 0.0f;
				for ( int rowNum = 0; rowNum < rows; rowNum++ )
				{
					float propFactor = _prop == null ? 1.0f / rows : _prop[rowNum];
					float height = ( innerRect.Height - (float)( rows - 1 ) * scaledInnerGap ) *
									propFactor;
					int columns = countList[rowNum];
					if ( columns <= 0 )
						columns = 1;
					float width = ( innerRect.Width - (float)( columns - 1 ) * scaledInnerGap ) /
									(float)columns;
					for ( int colNum = 0; colNum < columns; colNum++ )
					{
						if ( iPane >= _paneList.Count )
							return;
						this[iPane].Rect = new RectangleF(
											innerRect.X + colNum * ( width + scaledInnerGap ),
											innerRect.Y + y,
											width,
											height );
						iPane++;
					}
					y += height + scaledInnerGap;
				}
			}
			else
			{
				int columns = countList.Length;
				float x = 0.0f;
				for ( int colNum = 0; colNum < columns; colNum++ )
				{
					float propFactor = _prop == null ? 1.0f / columns : _prop[colNum];
					float width = ( innerRect.Width - (float)( columns - 1 ) * scaledInnerGap ) *
									propFactor;
					int rows = countList[colNum];
					if ( rows <= 0 )
						rows = 1;
					float height = ( innerRect.Height - (float)( rows - 1 ) * scaledInnerGap ) / (float)rows;
					for ( int rowNum = 0; rowNum < rows; rowNum++ )
					{
						if ( iPane >= _paneList.Count )
							return;
						this[iPane].Rect = new RectangleF(
											innerRect.X + x,
											innerRect.Y + rowNum * ( height + scaledInnerGap ),
											width,
											height );
						iPane++;
					}
					x += width + scaledInnerGap;
				}
			}
		}
		/*
		/// 
		/// Automatically set all of the  's in
		/// the list to a reasonable configuration.
		/// 
		/// This method explicitly specifies the number of rows and columns to use in the layout.
		/// A more automatic overload, using a  enumeration, is available.
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// The number of rows of  objects
		/// to include in the layout
		/// The number of columns of  objects
		/// to include in the layout
		public void DoPaneLayout( Graphics g, int rows, int columns )
		{
			// save the layout settings for future reference
			_countList = null;
			_rows = rows;
			_columns = columns;
			// calculate scaleFactor on "normal" pane size (BaseDimension)
			float scaleFactor = this.CalcScaleFactor();
			// innerRect is the area for the GraphPane's
			RectangleF innerRect = CalcClientRect( g, scaleFactor );
			_legend.CalcRect( g, this, scaleFactor, ref innerRect );
			// scaled InnerGap is the area between the GraphPane.Rect's
			float scaledInnerGap = (float)( _innerPaneGap * scaleFactor );
			float width = ( innerRect.Width - (float)( columns - 1 ) * scaledInnerGap ) / (float)columns;
			float height = ( innerRect.Height - (float)( rows - 1 ) * scaledInnerGap ) / (float)rows;
			int i = 0;
			foreach ( GraphPane pane in _paneList )
			{
				float rowNum = (float)( i / columns );
				float colNum = (float)( i % columns );
				pane.Rect = new RectangleF(
									innerRect.X + colNum * ( width + scaledInnerGap ),
									innerRect.Y + rowNum * ( height + scaledInnerGap ),
									width,
									height );
				i++;
			}
		}
		*/
	#endregion
	}
}