/*
* 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.Editors;
using XPTable.Events;
using XPTable.Renderers;
namespace XPTable.Models
{
/// <summary>
/// Summary description for Column.
/// </summary>
[DesignTimeVisible(false),
ToolboxItem(false)]
public abstract class Column : Component
{
#region Event Handlers
/// <summary>
/// Occurs when one of the Column's properties changes
/// </summary>
public event ColumnEventHandler PropertyChanged;
#endregion
#region Class Data
// Column state flags
private readonly static int STATE_EDITABLE = 1;
private readonly static int STATE_ENABLED = 2;
private readonly static int STATE_VISIBLE = 4;
private readonly static int STATE_SELECTABLE = 8;
private readonly static int STATE_SORTABLE = 16;
/// <summary>
/// The amount of space on each side of the Column that can
/// be used as a resizing handle
/// </summary>
public static readonly int ResizePadding = 8;
/// <summary>
/// The default width of a Column
/// </summary>
public static readonly int DefaultWidth = 75;
/// <summary>
/// The maximum width of a Column
/// </summary>
public static readonly int MaximumWidth = 1024;
/// <summary>
/// The minimum width of a Column
/// </summary>
public static readonly int MinimumWidth = ResizePadding * 2;
/// <summary>
/// Contains the current state of the the Column
/// </summary>
public byte state;
/// <summary>
/// The text displayed in the Column's header
/// </summary>
private string text;
/// <summary>
/// A string that specifies how a Column's Cell contents are formatted
/// </summary>
private string format;
/// <summary>
/// The alignment of the text displayed in the Column's Cells
/// </summary>
private ColumnAlignment alignment;
/// <summary>
/// The width of the Column
/// </summary>
private int width;
/// <summary>
/// The Image displayed on the Column's header
/// </summary>
private Image image;
/// <summary>
/// Specifies whether the Image displayed on the Column's header should
/// be draw on the right hand side of the Column
/// </summary>
private bool imageOnRight;
/// <summary>
/// The current state of the Column
/// </summary>
private ColumnState columnState;
/// <summary>
/// The text displayed when a ToolTip is shown for the Column's header
/// </summary>
private string tooltipText;
/// <summary>
/// The ColumnModel that the Column belongs to
/// </summary>
private ColumnModel columnModel;
/// <summary>
/// The x-coordinate of the column's left edge in pixels
/// </summary>
private int x;
/// <summary>
/// The current SortOrder of the Column
/// </summary>
private SortOrder sortOrder;
/// <summary>
/// The CellRenderer used to draw the Column's Cells
/// </summary>
private ICellRenderer renderer;
/// <summary>
/// The CellEditor used to edit the Column's Cells
/// </summary>
private ICellEditor editor;
/// <summary>
/// The Type of the IComparer used to compare the Column's Cells
/// </summary>
private Type comparer;
#endregion
#region Constructor
/// <summary>
/// Creates a new Column with default values
/// </summary>
public Column() : base()
{
this.Init();
}
/// <summary>
/// Creates a new Column with the specified header text
/// </summary>
/// <param name="text">The text displayed in the column's header</param>
public Column(string text) : base()
{
this.Init();
this.text = text;
}
/// <summary>
/// Creates a new Column with the specified header text and width
/// </summary>
/// <param name="text">The text displayed in the column's header</param>
/// <param name="width">The column's width</param>
public Column(string text, int width) : base()
{
this.Init();
this.text = text;
this.width = width;
}
/// <summary>
/// Creates a new Column with the specified header text, width and visibility
/// </summary>
/// <param name="text">The text displayed in the column's header</param>
/// <param name="width">The column's width</param>
/// <param name="visible">Specifies whether the column is visible</param>
public Column(string text, int width, bool visible) : base()
{
this.Init();
this.text = text;
this.width = width;
this.Visible = visible;
}
/// <summary>
/// Creates a new Column with the specified header text and image
/// </summary>
/// <param name="text">The text displayed in the column's header</param>
/// <param name="image">The image displayed on the column's header</param>
public Column(string text, Image image) : base()
{
this.Init();
this.text = text;
this.image = image;
}
/// <summary>
/// Creates a new Column with the specified header text, image and width
/// </summary>
/// <param name="text">The text displayed in the column's header</param>
/// <param name="image">The image displayed on the column's header</param>
/// <param name="width">The column's width</param>
public Column(string text, Image image, int width) : base()
{
this.Init();
this.text = text;
this.image = image;
this.width = width;
}
/// <summary>
/// Creates a new Column with the specified header text, image, width and visibility
/// </summary>
/// <param name="text">The text displayed in the column's header</param>
/// <param name="image">The image displayed on the column's header</param>
/// <param name="width">The column's width</param>
/// <param name="visible">Specifies whether the column is visible</param>
public Column(string text, Image image, int width, bool visible) : base()
{
this.Init();
this.text = text;
this.image = image;
this.width = width;
this.Visible = visible;
}
/// <summary>
/// Initialise default values
/// </summary>
private void Init()
{
this.text = null;
this.width = Column.DefaultWidth;
this.columnState = ColumnState.Normal;
this.alignment = ColumnAlignment.Left;
this.image = null;
this.imageOnRight = false;
this.columnModel = null;
this.x = 0;
this.tooltipText = null;
this.format = "";
this.sortOrder = SortOrder.None;
this.renderer = null;
this.editor = null;
this.comparer = null;
this.state = (byte) (STATE_ENABLED | STATE_EDITABLE | STATE_VISIBLE | STATE_SELECTABLE | STATE_SORTABLE);
}
#endregion
#region Methods
/// <summary>
/// Gets a string that specifies the name of the Column's default CellRenderer
/// </summary>
/// <returns>A string that specifies the name of the Column's default
/// CellRenderer</returns>
public abstract string GetDefaultRendererName();
/// <summary>
/// Gets the Column's default CellRenderer
/// </summary>
/// <returns>The Column's default CellRenderer</returns>
public abstract ICellRenderer CreateDefaultRenderer();
/// <summary>
/// Gets a string that specifies the name of the Column's default CellEditor
/// </summary>
/// <returns>A string that specifies the name of the Column's default
/// CellEditor</returns>
public abstract string GetDefaultEditorName();
/// <summary>
/// Gets the Column's default CellEditor
/// </summary>
/// <returns>The Column's default CellEditor</returns>
public abstract ICellEditor CreateDefaultEditor();
/// <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 on the Column header
/// </summary>
[Category("Appearance"),
DefaultValue(null),
Description("The text displayed in the column's header.")]
public string Text
{
get
{
return this.text;
}
set
{
if (value == null)
{
value = "";
}
if (!value.Equals(this.text))
{
string oldText = this.text;
this.text = value;
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.TextChanged, oldText));
}
}
}
/// <summary>
/// Gets or sets the string that specifies how a Column's Cell contents
/// are formatted
/// </summary>
[Category("Appearance"),
DefaultValue(""),
Description("A string that specifies how a column's cell contents are formatted.")]
public string Format
{
get
{
return this.format;
}
set
{
if (value == null)
{
value = "";
}
if (!value.Equals(this.format))
{
string oldFormat = this.format;
this.format = value;
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.FormatChanged, oldFormat));
}
}
}
/// <summary>
/// Gets or sets the horizontal alignment of the Column's Cell contents
/// </summary>
[Category("Appearance"),
DefaultValue(ColumnAlignment.Left),
Description("The horizontal alignment of the column's cell contents.")]
public virtual ColumnAlignment Alignment
{
get
{
return this.alignment;
}
set
{
if (!Enum.IsDefined(typeof(ColumnAlignment), value))
{
throw new InvalidEnumArgumentException("value", (int) value, typeof(ColumnAlignment));
}
if (this.alignment != value)
{
ColumnAlignment oldAlignment = this.alignment;
this.alignment = value;
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.AlignmentChanged, oldAlignment));
}
}
}
/// <summary>
/// Gets or sets the width of the Column
/// </summary>
[Category("Appearance"),
Description("The width of the column.")]
public int Width
{
get
{
return this.width;
}
set
{
if (this.width != value)
{
int oldWidth = this.Width;
// Set the width, and check min & max
this.width = Math.Min(Math.Max(value, MinimumWidth), MaximumWidth);
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.WidthChanged, oldWidth));
}
}
}
/// <summary>
/// Specifies whether the Width property should be serialized at
/// design time
/// </summary>
/// <returns>true if the Width property should be serialized,
/// false otherwise</returns>
private bool ShouldSerializeWidth()
{
return this.Width != Column.DefaultWidth;
}
/// <summary>
/// Gets or sets the Image displayed in the Column's header
/// </summary>
[Category("Appearance"),
DefaultValue(null),
Description("Ihe image displayed in the column's header")]
public Image Image
{
get
{
return this.image;
}
set
{
if (this.image != value)
{
Image oldImage = this.Image;
this.image = value;
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.ImageChanged, oldImage));
}
}
}
/// <summary>
/// Gets or sets whether the Image displayed on the Column's header should
/// be draw on the right hand side of the Column
/// </summary>
[Category("Appearance"),
DefaultValue(false),
Description("Specifies whether the image displayed on the column's header should be drawn on the right hand side of the column")]
public bool ImageOnRight
{
get
{
return this.imageOnRight;
}
set
{
if (this.imageOnRight != value)
{
this.imageOnRight = value;
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.ImageChanged, null));
}
}
}
/// <summary>
/// Gets the state of the Column
/// </summary>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ColumnState ColumnState
{
get
{
return this.columnState;
}
}
/// <summary>
/// Gets or sets the state of the Column
/// </summary>
internal ColumnState InternalColumnState
{
get
{
return this.ColumnState;
}
set
{
if (!Enum.IsDefined(typeof(ColumnState), value))
{
throw new InvalidEnumArgumentException("value", (int) value, typeof(ColumnState));
}
if (this.columnState != value)
{
ColumnState oldState = this.columnState;
this.columnState = value;
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.StateChanged, oldState));
}
}
}
/// <summary>
/// Gets or sets the whether the Column is displayed
/// </summary>
[Category("Appearance"),
DefaultValue(true),
Description("Determines whether the column is visible or hidden.")]
public bool Visible
{
get
{
return this.GetState(STATE_VISIBLE);
}
set
{
bool visible = this.Visible;
this.SetState(STATE_VISIBLE, value);
if (visible != value)
{
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.VisibleChanged, visible));
}
}
}
/// <summary>
/// Gets or sets whether the Column is able to be sorted
/// </summary>
[Category("Appearance"),
DefaultValue(true),
Description("Determines whether the column is able to be sorted.")]
public virtual bool Sortable
{
get
{
return this.GetState(STATE_SORTABLE);
}
set
{
bool sortable = this.Sortable;
this.SetState(STATE_SORTABLE, value);
if (sortable != value)
{
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.SortableChanged, sortable));
}
}
}
/// <summary>
/// Gets or sets the user specified ICellRenderer that is used to draw the
/// Column's Cells
/// </summary>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ICellRenderer Renderer
{
get
{
return this.renderer;
}
set
{
if (this.renderer != value)
{
this.renderer = value;
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.RendererChanged, null));
}
}
}
/// <summary>
/// Gets or sets the user specified ICellEditor that is used to edit the
/// Column's Cells
/// </summary>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ICellEditor Editor
{
get
{
return this.editor;
}
set
{
if (this.editor != value)
{
this.editor = value;
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.EditorChanged, null));
}
}
}
/// <summary>
/// Gets or sets the user specified Comparer type that is used to edit the
/// Column's Cells
/// </summary>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Type Comparer
{
get
{
return this.comparer;
}
set
{
if (this.comparer != value)
{
this.comparer = value;
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.ComparerChanged, null));
}
}
}
/// <summary>
/// Gets the Type of the default Comparer used to compare the Column's Cells when
/// the Column is sorting
/// </summary>
[Browsable(false)]
public abstract Type DefaultComparerType
{
get;
}
/// <summary>
/// Gets the current SortOrder of the Column
/// </summary>
[Browsable(false)]
public SortOrder SortOrder
{
get
{
return this.sortOrder;
}
}
/// <summary>
/// Gets or sets the current SortOrder of the Column
/// </summary>
internal SortOrder InternalSortOrder
{
get
{
return this.SortOrder;
}
set
{
if (!Enum.IsDefined(typeof(SortOrder), value))
{
throw new InvalidEnumArgumentException("value", (int) value, typeof(SortOrder));
}
if (this.sortOrder != value)
{
SortOrder oldOrder = this.sortOrder;
this.sortOrder = value;
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.SortOrderChanged, oldOrder));
}
}
}
/// <summary>
/// Gets or sets a value indicating whether the Column's Cells contents
/// are able to be edited
/// </summary>
[Category("Appearance"),
Description("Controls whether the column's cell contents are able to be changed by the user")]
public virtual bool Editable
{
get
{
if (!this.GetState(STATE_EDITABLE))
{
return false;
}
return this.Visible && this.Enabled;
}
set
{
bool editable = this.GetState(STATE_EDITABLE);
this.SetState(STATE_EDITABLE, value);
if (editable != value)
{
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.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 Column's Cells can respond to
/// user interaction
/// </summary>
[Category("Appearance"),
Description("Indicates whether the column's cells can respond to user interaction")]
public bool Enabled
{
get
{
if (!this.GetState(STATE_ENABLED))
{
return false;
}
if (this.ColumnModel == null)
{
return true;
}
return this.ColumnModel.Enabled;
}
set
{
bool enabled = this.GetState(STATE_ENABLED);
this.SetState(STATE_ENABLED, value);
if (enabled != value)
{
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.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 a value indicating whether the Column's Cells can be selected
/// </summary>
[Category("Appearance"),
DefaultValue(true),
Description("Indicates whether the column's cells can be selected")]
public virtual bool Selectable
{
get
{
return this.GetState(STATE_SELECTABLE);
}
set
{
bool selectable = this.Selectable;
this.SetState(STATE_SELECTABLE, value);
if (selectable != value)
{
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.SelectableChanged, selectable));
}
}
}
/// <summary>
/// Gets or sets the ToolTip text associated with the Column
/// </summary>
[Category("Appearance"),
DefaultValue(null),
Description("The ToolTip text associated with the Column")]
public string ToolTipText
{
get
{
return this.tooltipText;
}
set
{
if (value == null)
{
value = "";
}
if (!value.Equals(this.tooltipText))
{
string oldTip = this.tooltipText;
this.tooltipText = value;
this.OnPropertyChanged(new ColumnEventArgs(this, ColumnEventType.ToolTipTextChanged, oldTip));
}
}
}
/// <summary>
/// Gets the x-coordinate of the column's left edge in pixels
/// </summary>
internal int X
{
get
{
return this.x;
}
set
{
this.x = value;
}
}
/// <summary>
/// Gets the x-coordinate of the column's left edge in pixels
/// </summary>
[Browsable(false)]
public int Left
{
get
{
return this.X;
}
}
/// <summary>
/// Gets the x-coordinate of the column's right edge in pixels
/// </summary>
[Browsable(false)]
public int Right
{
get
{
return this.Left + this.Width;
}
}
/// <summary>
/// Gets or sets the ColumnModel the Column belongs to
/// </summary>
protected internal ColumnModel ColumnModel
{
get
{
return this.columnModel;
}
set
{
this.columnModel = value;
}
}
/// <summary>
/// Gets the ColumnModel the Column belongs to. This member is not
/// intended to be used directly from your code
/// </summary>
[Browsable(false)]
public ColumnModel Parent
{
get
{
return this.ColumnModel;
}
}
/// <summary>
/// Gets whether the Column is able to raise events
/// </summary>
protected bool CanRaiseEvents
{
get
{
// check if the ColumnModel that the Colum belongs to is able to
// raise events (if it can't, the Colum shouldn't raise events either)
if (this.ColumnModel != null)
{
return this.ColumnModel.CanRaiseEvents;
}
return true;
}
}
#endregion
#region Events
/// <summary>
/// Raises the PropertyChanged event
/// </summary>
/// <param name="e">A ColumnEventArgs that contains the event data</param>
protected virtual void OnPropertyChanged(ColumnEventArgs e)
{
if (this.ColumnModel != null)
{
e.SetIndex(this.ColumnModel.Columns.IndexOf(this));
}
if (this.CanRaiseEvents)
{
if (this.ColumnModel != null)
{
this.ColumnModel.OnColumnPropertyChanged(e);
}
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
}
#endregion
}
}