/*
 * Copyright © 2005, Mathew Hall
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 *    - Redistributions of source code must retain the above copyright notice, 
 *      this list of conditions and the following disclaimer.
 * 
 *    - Redistributions in binary form must reproduce the above copyright notice, 
 *      this list of conditions and the following disclaimer in the documentation 
 *      and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 */


using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

using XPTable.Events;
using XPTable.Models.Design;
using XPTable.Renderers;


namespace XPTable.Models
{
	/// <summary>
	/// Represents a Cell that is displayed in a Table
	/// </summary>
	[DesignTimeVisible(true),
	TypeConverter(typeof(CellConverter))]
	public class Cell : IDisposable
	{
		#region EventHandlers

		/// <summary>
		/// Occurs when the value of a Cells property changes
		/// </summary>
		public event CellEventHandler PropertyChanged;

		#endregion


		#region Class Data

		// Cell state flags
		private static readonly int STATE_EDITABLE = 1;
		private static readonly int STATE_ENABLED = 2;
		private static readonly int STATE_SELECTED = 4;

		/// <summary>
		/// The text displayed in the Cell
		/// </summary>
		private string text;

		/// <summary>
		/// An object that contains data to be displayed in the Cell
		/// </summary>
		private object data;

		/// <summary>
		/// An object that contains data about the Cell
		/// </summary>
		private object tag;

		/// <summary>
		/// Stores information used by CellRenderers to record the current 
		/// state of the Cell
		/// </summary>
		private object rendererData;

		/// <summary>
		/// The Row that the Cell belongs to
		/// </summary>
		private Row row;

		/// <summary>
		/// The index of the Cell
		/// </summary>
		private int index;

		/// <summary>
		/// Contains the current state of the the Cell
		/// </summary>
		private byte state;
		
		/// <summary>
		/// The Cells CellStyle settings
		/// </summary>
		private CellStyle cellStyle;

		/// <summary>
		/// The Cells CellCheckStyle settings
		/// </summary>
		private CellCheckStyle checkStyle;

		/// <summary>
		/// The Cells CellImageStyle settings
		/// </summary>
		private CellImageStyle imageStyle;

		/// <summary>
		/// The text displayed in the Cells tooltip
		/// </summary>
		private string tooltipText;

		/// <summary>
		/// Specifies whether the Cell has been disposed
		/// </summary>
		private bool disposed = false;

		#endregion


		#region Constructor
		
		/// <summary>
		/// Initializes a new instance of the Cell class with default settings
		/// </summary>
		public Cell() : base()
		{
			this.Init();
		}


		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text
		/// </summary>
		/// <param name="text">The text displayed in the Cell</param>
		public Cell(string text)
		{	
			this.Init();

			this.text = text;
		}


		/// <summary>
		/// Initializes a new instance of the Cell class with the specified object
		/// </summary>
		/// <param name="value">The object displayed in the Cell</param>
		public Cell(object value)
		{
			this.Init();

			this.data = value;
		}


		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text 
		/// and object
		/// </summary>
		/// <param name="text">The text displayed in the Cell</param>
		/// <param name="value">The object displayed in the Cell</param>
		public Cell(string text, object value)
		{
			this.Init();

			this.text = text;
			this.data = value;
		}


		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text 
		/// and check value
		/// </summary>
		/// <param name="text">The text displayed in the Cell</param>
		/// <param name="check">Specifies whether the Cell is Checked</param>
		public Cell(string text, bool check)
		{
			this.Init();

			this.text = text;
			this.Checked = check;
		}
		
		
		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text 
		/// and Image value
		/// </summary>
		/// <param name="text">The text displayed in the Cell</param>
		/// <param name="image">The Image displayed in the Cell</param>
		public Cell(string text, Image image)
		{
			this.Init();

			this.text = text;
			this.Image = image;
		}
		
		
		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text, 
		/// fore Color, back Color and Font
		/// </summary>
		/// <param name="text">The text displayed in the Cell</param>
		/// <param name="foreColor">The foreground Color of the Cell</param>
		/// <param name="backColor">The background Color of the Cell</param>
		/// <param name="font">The Font used to draw the text in the Cell</param>
		public Cell(string text, Color foreColor, Color backColor, Font font)
		{
			this.Init();

			this.text = text;
			this.ForeColor = foreColor;
			this.BackColor = backColor;
			this.Font = font;
		}


		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text 
		/// and CellStyle
		/// </summary>
		/// <param name="text">The text displayed in the Cell</param>
		/// <param name="cellStyle">A CellStyle that specifies the visual appearance 
		/// of the Cell</param>
		public Cell(string text, CellStyle cellStyle)
		{
			this.Init();

			this.text = text;
			this.cellStyle = cellStyle;
		}
		
		
		/// <summary>
		/// Initializes a new instance of the Cell class with the specified object, 
		/// fore Color, back Color and Font
		/// </summary>
		/// <param name="value">The object displayed in the Cell</param>
		/// <param name="foreColor">The foreground Color of the Cell</param>
		/// <param name="backColor">The background Color of the Cell</param>
		/// <param name="font">The Font used to draw the text in the Cell</param>
		public Cell(object value, Color foreColor, Color backColor, Font font)
		{
			this.Init();

			this.data = value;
			this.ForeColor = foreColor;
			this.BackColor = backColor;
			this.Font = font;
		}


		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text 
		/// and CellStyle
		/// </summary>
		/// <param name="value">The object displayed in the Cell</param>
		/// <param name="cellStyle">A CellStyle that specifies the visual appearance 
		/// of the Cell</param>
		public Cell(object value, CellStyle cellStyle)
		{
			this.Init();

			this.data = value;
			this.cellStyle = cellStyle;
		}


		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text, 
		/// object, fore Color, back Color and Font
		/// </summary>
		/// <param name="text">The text displayed in the Cell</param>
		/// <param name="value">The object displayed in the Cell</param>
		/// <param name="foreColor">The foreground Color of the Cell</param>
		/// <param name="backColor">The background Color of the Cell</param>
		/// <param name="font">The Font used to draw the text in the Cell</param>
		public Cell(string text, object value, Color foreColor, Color backColor, Font font)
		{
			this.Init();

			this.text = text;
			this.data = value;
			this.ForeColor = foreColor;
			this.BackColor = backColor;
			this.Font = font;
		}


		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text, 
		/// object and CellStyle
		/// </summary>
		/// <param name="text">The text displayed in the Cell</param>
		/// <param name="value">The object displayed in the Cell</param>
		/// <param name="cellStyle">A CellStyle that specifies the visual appearance 
		/// of the Cell</param>
		public Cell(string text, object value, CellStyle cellStyle)
		{
			this.Init();

			this.text = text;
			this.data = value;
			this.cellStyle = cellStyle;
		}


		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text, 
		/// check value, fore Color, back Color and Font
		/// </summary>
		/// <param name="text">The text displayed in the Cell</param>
		/// <param name="check">Specifies whether the Cell is Checked</param>
		/// <param name="foreColor">The foreground Color of the Cell</param>
		/// <param name="backColor">The background Color of the Cell</param>
		/// <param name="font">The Font used to draw the text in the Cell</param>
		public Cell(string text, bool check, Color foreColor, Color backColor, Font font)
		{
			this.Init();

			this.text = text;
			this.Checked = check;
			this.ForeColor = foreColor;
			this.BackColor = backColor;
			this.Font = font;
		}


		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text, 
		/// check value and CellStyle
		/// </summary>
		/// <param name="text">The text displayed in the Cell</param>
		/// <param name="check">Specifies whether the Cell is Checked</param>
		/// <param name="cellStyle">A CellStyle that specifies the visual appearance 
		/// of the Cell</param>
		public Cell(string text, bool check, CellStyle cellStyle)
		{
			this.Init();

			this.text = text;
			this.Checked = check;
			this.cellStyle = cellStyle;
		}
		
		
		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text, 
		/// Image, fore Color, back Color and Font
		/// </summary>
		/// <param name="text">The text displayed in the Cell</param>
		/// <param name="image">The Image displayed in the Cell</param>
		/// <param name="foreColor">The foreground Color of the Cell</param>
		/// <param name="backColor">The background Color of the Cell</param>
		/// <param name="font">The Font used to draw the text in the Cell</param>
		public Cell(string text, Image image, Color foreColor, Color backColor, Font font)
		{
			this.Init();

			this.text = text;
			this.Image = image;
			this.ForeColor = foreColor;
			this.BackColor = backColor;
			this.Font = font;
		}
		
		
		/// <summary>
		/// Initializes a new instance of the Cell class with the specified text, 
		/// Image and CellStyle
		/// </summary>
		/// <param name="text">The text displayed in the Cell</param>
		/// <param name="image">The Image displayed in the Cell</param>
		/// <param name="cellStyle">A CellStyle that specifies the visual appearance 
		/// of the Cell</param>
		public Cell(string text, Image image, CellStyle cellStyle)
		{
			this.Init();

			this.text = text;
			this.Image = image;
			this.cellStyle = cellStyle;
		}


		/// <summary>
		/// Initialise default values
		/// </summary>
		private void Init()
		{
			this.text = null;
			this.data = null;
			this.rendererData = null;
			this.tag = null;
			this.row = null;
			this.index = -1;
			this.cellStyle = null;
			this.checkStyle = null;
			this.imageStyle = null;
			this.tooltipText = null;

			this.state = (byte) (STATE_EDITABLE | STATE_ENABLED);
		}

		#endregion


		#region Methods

		/// <summary>
		/// Releases all resources used by the Cell
		/// </summary>
		public void Dispose()
		{
			if (!this.disposed)
			{
				this.text = null;
				this.data = null;
				this.tag = null;
				this.rendererData = null;

				if (this.row != null)
				{
					this.row.Cells.Remove(this);
				}

				this.row = null;
				this.index = -1;
				this.cellStyle = null;
				this.checkStyle = null;
				this.imageStyle = null;
				this.tooltipText = null;

				this.state = (byte) 0;
				
				this.disposed = true;
			}
		}


		/// <summary>
		/// Returns the state represented by the specified state flag
		/// </summary>
		/// <param name="flag">A flag that represents the state to return</param>
		/// <returns>The state represented by the specified state flag</returns>
		internal bool GetState(int flag)
		{
			return ((this.state & flag) != 0);
		}


		/// <summary>
		/// Sets the state represented by the specified state flag to the specified value
		/// </summary>
		/// <param name="flag">A flag that represents the state to be set</param>
		/// <param name="value">The new value of the state</param>
		internal void SetState(int flag, bool value)
		{
			this.state = (byte) (value ? (this.state | flag) : (this.state & ~flag));
		}

		#endregion


		#region Properties

		/// <summary>
		/// Gets or sets the text displayed by the Cell
		/// </summary>
		[Category("Appearance"),
		DefaultValue(null),
		Description("The text displayed by the cell")]
		public string Text
		{
			get
			{
				return this.text;
			}

			set
			{
				if (this.text == null || !this.text.Equals(value))
				{
					string oldText = this.Text;
					
					this.text = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.ValueChanged, oldText));
				}
			}
		}


		/// <summary>
		/// Gets or sets the Cells non-text data
		/// </summary>
		[Category("Appearance"),
		DefaultValue(null),
		Description("The non-text data displayed by the cell"),
		TypeConverter(typeof(StringConverter))]
		public object Data
		{
			get
			{
				return this.data;
			}

			set
			{
				if (this.data != value)
				{
					object oldData = this.Data;
					
					this.data = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.ValueChanged, oldData));
				}
			}
		}


		/// <summary>
		/// Gets or sets the object that contains data about the Cell
		/// </summary>
		[Category("Appearance"),
		DefaultValue(null),
		Description("User defined data associated with the cell"),
		TypeConverter(typeof(StringConverter))]
		public object Tag
		{
			get
			{
				return this.tag;
			}

			set
			{
				this.tag = value;
			}
		}


		/// <summary>
		/// Gets or sets the CellStyle used by the Cell
		/// </summary>
		[Browsable(false),
		DefaultValue(null)]
		public CellStyle CellStyle
		{
			get
			{
				return this.cellStyle;
			}

			set
			{
				if (this.cellStyle != value)
				{
					CellStyle oldStyle = this.CellStyle;
					
					this.cellStyle = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.StyleChanged, oldStyle));
				}
			}
		}


		/// <summary>
		/// Gets or sets whether the Cell is selected
		/// </summary>
		[Browsable(false)]
		public bool Selected
		{
			get
			{
				return this.GetState(STATE_SELECTED);
			}
		}


		/// <summary>
		/// Sets whether the Cell is selected
		/// </summary>
		/// <param name="selected">A boolean value that specifies whether the 
		/// cell is selected</param>
		internal void SetSelected(bool selected)
		{
			this.SetState(STATE_SELECTED, selected);
		}


		/// <summary>
		/// Gets or sets the background Color for the Cell
		/// </summary>
		[Category("Appearance"),
		Description("The background color used to display text and graphics in the cell")]
		public Color BackColor
		{
			get
			{
				if (this.CellStyle == null)
				{
					if (this.Row != null)
					{
						return this.Row.BackColor;
					}

					return Color.Transparent;
				}
				
				return this.CellStyle.BackColor;
			}

			set
			{
				if (this.CellStyle == null)
				{
					this.CellStyle = new CellStyle();
				}
				
				if (this.CellStyle.BackColor != value)
				{
					Color oldBackColor = this.BackColor;
					
					this.CellStyle.BackColor = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.BackColorChanged, oldBackColor));
				}
			}
		}


		/// <summary>
		/// Specifies whether the BackColor property should be serialized at 
		/// design time
		/// </summary>
		/// <returns>true if the BackColor property should be serialized, 
		/// false otherwise</returns>
		private bool ShouldSerializeBackColor()
		{
			return (this.cellStyle != null && this.cellStyle.BackColor != Color.Empty);
		}


		/// <summary>
		/// Gets or sets the foreground Color for the Cell
		/// </summary>
		[Category("Appearance"),
		Description("The foreground color used to display text and graphics in the cell")]
		public Color ForeColor
		{
			get
			{
				if (this.CellStyle == null)
				{
					if (this.Row != null)
					{
						return this.Row.ForeColor;
					}

					return Color.Transparent;
				}
				else
				{
					if (this.CellStyle.ForeColor == Color.Empty || this.CellStyle.ForeColor == Color.Transparent)
					{
						if (this.Row != null)
						{
							return this.Row.ForeColor;
						}
					}

					return this.CellStyle.ForeColor;
				}
			}

			set
			{
				if (this.CellStyle == null)
				{
					this.CellStyle = new CellStyle();
				}
				
				if (this.CellStyle.ForeColor != value)
				{
					Color oldForeColor = this.ForeColor;
					
					this.CellStyle.ForeColor = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.ForeColorChanged, oldForeColor));
				}
			}
		}


		/// <summary>
		/// Specifies whether the ForeColor property should be serialized at 
		/// design time
		/// </summary>
		/// <returns>true if the ForeColor property should be serialized, 
		/// false otherwise</returns>
		private bool ShouldSerializeForeColor()
		{
			return (this.cellStyle != null && this.cellStyle.ForeColor != Color.Empty);
		}


		/// <summary>
		/// Gets or sets the Font used by the Cell
		/// </summary>
		[Category("Appearance"),
		Description("The font used to display text in the cell")]
		public Font Font
		{
			get
			{
				if (this.CellStyle == null)
				{
					if (this.Row != null)
					{
						return this.Row.Font;
					}

					return null;
				}
				else
				{
					if (this.CellStyle.Font == null)
					{
						if (this.Row != null)
						{
							return this.Row.Font;
						}
					}

					return this.CellStyle.Font;
				}
			}

			set
			{
				if (this.CellStyle == null)
				{
					this.CellStyle = new CellStyle();
				}
				
				if (this.CellStyle.Font != value)
				{
					Font oldFont = this.Font;
					
					this.CellStyle.Font = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.FontChanged, oldFont));
				}
			}
		}


		/// <summary>
		/// Specifies whether the Font property should be serialized at 
		/// design time
		/// </summary>
		/// <returns>true if the Font property should be serialized, 
		/// false otherwise</returns>
		private bool ShouldSerializeFont()
		{
			return (this.cellStyle != null && this.cellStyle.Font != null);
		}


		/// <summary>
		/// Gets or sets the amount of space between the Cells Border and its contents
		/// </summary>
		[Category("Appearance"),
		Description("The amount of space between the cells border and its contents")]
		public CellPadding Padding
		{
			get
			{
				if (this.CellStyle == null)
				{
					return CellPadding.Empty;
				}
				
				return this.CellStyle.Padding;
			}

			set
			{
				if (this.CellStyle == null)
				{
					this.CellStyle = new CellStyle();
				}
				
				if (this.CellStyle.Padding != value)
				{
					CellPadding oldPadding = this.Padding;
					
					this.CellStyle.Padding = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.PaddingChanged, oldPadding));
				}
			}
		}


		/// <summary>
		/// Specifies whether the Padding property should be serialized at 
		/// design time
		/// </summary>
		/// <returns>true if the Padding property should be serialized, 
		/// false otherwise</returns>
		private bool ShouldSerializePadding()
		{
			return this.Padding != CellPadding.Empty;
		}


		/// <summary>
		/// Gets or sets whether the Cell is in the checked state
		/// </summary>
		[Category("Appearance"),
		DefaultValue(false),
		Description("Indicates whether the cell is checked or unchecked"),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
		RefreshProperties(RefreshProperties.Repaint)]
		public bool Checked
		{
			get
			{
				if (this.checkStyle == null)
				{
					return false;
				}
				
				return this.checkStyle.Checked;
			}

			set
			{
				if (this.checkStyle == null)
				{
					this.checkStyle = new CellCheckStyle();
				}
				
				if (this.checkStyle.Checked != value)
				{
					bool oldCheck = this.Checked;
					
					this.checkStyle.Checked = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.CheckStateChanged, oldCheck));
				}
			}
		}


		/// <summary>
		/// Gets or sets the state of the Cells check box
		/// </summary>
		[Category("Appearance"),
		DefaultValue(CheckState.Unchecked),
		Description("Indicates the state of the cells check box"),
		RefreshProperties(RefreshProperties.Repaint)]
		public CheckState CheckState
		{
			get
			{
				if (this.checkStyle == null)
				{
					return CheckState.Unchecked;
				}
				
				return this.checkStyle.CheckState;
			}

			set
			{
				if (!Enum.IsDefined(typeof(CheckState), value))
				{
					throw new InvalidEnumArgumentException("value", (int) value, typeof(CheckState));
				}
				
				if (this.checkStyle == null)
				{
					this.checkStyle = new CellCheckStyle();
				}
				
				if (this.checkStyle.CheckState != value)
				{
					CheckState oldCheckState = this.CheckState;
					
					this.checkStyle.CheckState = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.CheckStateChanged, oldCheckState));
				}
			}
		}


		/// <summary>
		/// Gets or sets a value indicating whether the Cells check box 
		/// will allow three check states rather than two
		/// </summary>
		[Category("Appearance"),
		DefaultValue(false),
		Description("Controls whether or not the user can select the indeterminate state of the cells check box"),
		RefreshProperties(RefreshProperties.Repaint)]
		public bool ThreeState
		{
			get
			{
				if (this.checkStyle == null)
				{
					return false;
				}
				
				return this.checkStyle.ThreeState;
			}

			set
			{
				if (this.checkStyle == null)
				{
					this.checkStyle = new CellCheckStyle();
				}
				
				if (this.checkStyle.ThreeState != value)
				{
					bool oldThreeState = this.ThreeState;
					
					this.checkStyle.ThreeState = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.ThreeStateChanged, oldThreeState));
				}
			}
		}


		/// <summary>
		/// Gets or sets the image that is displayed in the Cell
		/// </summary>
		[Category("Appearance"),
		DefaultValue(null),
		Description("The image that will be displayed in the cell")]
		public Image Image
		{
			get
			{
				if (this.imageStyle == null)
				{
					return null;
				}
				
				return this.imageStyle.Image;
			}

			set
			{
				if (this.imageStyle == null)
				{
					this.imageStyle = new CellImageStyle();
				}
				
				if (this.imageStyle.Image != value)
				{
					Image oldImage = this.Image;
					
					this.imageStyle.Image = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.ImageChanged, oldImage));
				}
			}
		}


		/// <summary>
		/// Gets or sets how the Cells image is sized within the Cell
		/// </summary>
		[Category("Appearance"),
		DefaultValue(ImageSizeMode.Normal),
		Description("Controls how the image is sized within the cell")]
		public ImageSizeMode ImageSizeMode
		{
			get
			{
				if (this.imageStyle == null)
				{
					return ImageSizeMode.Normal;
				}
				
				return this.imageStyle.ImageSizeMode;
			}

			set
			{
				if (this.imageStyle == null)
				{
					this.imageStyle = new CellImageStyle();
				}
				
				if (this.imageStyle.ImageSizeMode != value)
				{
					ImageSizeMode oldSizeMode = this.ImageSizeMode;
					
					this.imageStyle.ImageSizeMode = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.ImageSizeModeChanged, oldSizeMode));
				}
			}
		}


		/// <summary>
		/// Gets or sets a value indicating whether the Cells contents are able 
		/// to be edited
		/// </summary>
		[Category("Appearance"),
		Description("Controls whether the cells contents are able to be changed by the user")]
		public bool Editable
		{
			get
			{
				if (!this.GetState(STATE_EDITABLE))
				{
					return false;
				}

				if (this.Row == null)
				{
					return this.Enabled;
				}

				return this.Enabled && this.Row.Editable;
			}

			set
			{
				bool editable = this.Editable;
				
				this.SetState(STATE_EDITABLE, value);
				
				if (editable != value)
				{
					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.EditableChanged, editable));
				}
			}
		}


		/// <summary>
		/// Specifies whether the Editable property should be serialized at 
		/// design time
		/// </summary>
		/// <returns>true if the Editable property should be serialized, 
		/// false otherwise</returns>
		private bool ShouldSerializeEditable()
		{
			return !this.GetState(STATE_EDITABLE);
		}


		/// <summary>
		/// Gets or sets a value indicating whether the Cell 
		/// can respond to user interaction
		/// </summary>
		[Category("Appearance"),
		Description("Indicates whether the cell is enabled")]
		public bool Enabled
		{
			get
			{
				if (!this.GetState(STATE_ENABLED))
				{
					return false;
				}

				if (this.Row == null)
				{
					return true;
				}

				return this.Row.Enabled;
			}

			set
			{
				bool enabled = this.Enabled;
				
				this.SetState(STATE_ENABLED, value);
				
				if (enabled != value)
				{
					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.EnabledChanged, enabled));
				}
			}
		}


		/// <summary>
		/// Specifies whether the Enabled property should be serialized at 
		/// design time
		/// </summary>
		/// <returns>true if the Enabled property should be serialized, 
		/// false otherwise</returns>
		private bool ShouldSerializeEnabled()
		{
			return !this.GetState(STATE_ENABLED);
		}


		/// <summary>
		/// Gets or sets the text displayed in the Cells tooltip
		/// </summary>
		[Category("Appearance"),
		DefaultValue(null),
		Description("The text displayed in the cells tooltip")]
		public string ToolTipText
		{
			get
			{
				return this.tooltipText;
			}

			set
			{
				if (this.tooltipText != value)
				{
					string oldToolTip = this.tooltipText;
					
					this.tooltipText = value;

					this.OnPropertyChanged(new CellEventArgs(this, CellEventType.ToolTipTextChanged, oldToolTip));
				}
			}
		}


		/// <summary>
		/// Gets or sets the information used by CellRenderers to record the current 
		/// state of the Cell
		/// </summary>
		protected internal object RendererData
		{
			get
			{
				return this.rendererData;
			}

			set
			{
				this.rendererData = value;
			}
		}


		/// <summary>
		/// Gets the Row that the Cell belongs to
		/// </summary>
		[Browsable(false)]
		public Row Row
		{
			get
			{
				return this.row;
			}
		}


		/// <summary>
		/// Gets or sets the Row that the Cell belongs to
		/// </summary>
		internal Row InternalRow
		{
			get
			{
				return this.row;
			}

			set
			{
				this.row = value;
			}
		}


		/// <summary>
		/// Gets the index of the Cell within its Row
		/// </summary>
		[Browsable(false)]
		public int Index
		{
			get
			{
				return this.index;
			}
		}


		/// <summary>
		/// Gets or sets the index of the Cell within its Row
		/// </summary>
		internal int InternalIndex
		{
			get
			{
				return this.index;
			}

			set
			{
				this.index = value;
			}
		}


		/// <summary>
		/// Gets whether the Cell is able to raise events
		/// </summary>
		protected internal bool CanRaiseEvents
		{
			get
			{
				// check if the Row that the Cell belongs to is able to 
				// raise events (if it can't, the Cell shouldn't raise 
				// events either)
				if (this.Row != null)
				{
					return this.Row.CanRaiseEvents;
				}

				return true;
			}
		}

		#endregion


		#region Events

		/// <summary>
		/// Raises the PropertyChanged event
		/// </summary>
		/// <param name="e">A CellEventArgs that contains the event data</param>
		protected virtual void OnPropertyChanged(CellEventArgs e)
		{
			e.SetColumn(this.Index);

			if (this.Row != null)
			{
				e.SetRow(this.Row.Index);
			}
			
			if (this.CanRaiseEvents)
			{
				if (this.Row != null)
				{
					this.Row.OnCellPropertyChanged(e);
				}
				
				if (PropertyChanged != null)
				{
					PropertyChanged(this, e);
				}
			}
		}

		#endregion
	}
}
