/*
* 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.Models;
using XPTable.Themes;
namespace XPTable.Renderers
{
/// <summary>
/// A base class for drawing Cells contents as numbers
/// </summary>
public class NumberCellRenderer : CellRenderer
{
#region Class Data
/// <summary>
/// The width of the ComboBox's dropdown button
/// </summary>
private int buttonWidth;
/// <summary>
/// Specifies whether the up and down buttons should be drawn
/// </summary>
private bool showUpDownButtons;
/// <summary>
/// The alignment of the up and down buttons in the Cell
/// </summary>
private LeftRightAlignment upDownAlignment;
/// <summary>
/// The maximum value for the Cell
/// </summary>
private decimal maximum;
/// <summary>
/// The minimum value for the Cell
/// </summary>
private decimal minimum;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the NumberCellRenderer class with
/// default settings
/// </summary>
public NumberCellRenderer() : base()
{
this.StringFormat.Trimming = StringTrimming.None;
this.Format = "G";
this.buttonWidth = 15;
this.showUpDownButtons = false;
this.upDownAlignment = LeftRightAlignment.Right;
this.maximum = (decimal) 100;
this.minimum = (decimal) 0;
}
#endregion
#region Methods
/// <summary>
/// Returns a Rectangle that specifies the size and location of the
/// up and down buttons
/// </summary>
/// <returns>A Rectangle that specifies the size and location of the
/// up and down buttons</returns>
protected Rectangle CalcButtonBounds()
{
Rectangle buttonRect = this.ClientRectangle;
buttonRect.Width = this.ButtonWidth;
if (this.UpDownAlign == LeftRightAlignment.Right)
{
buttonRect.X = this.ClientRectangle.Right - buttonRect.Width;
}
if (buttonRect.Width > this.ClientRectangle.Width)
{
buttonRect = this.ClientRectangle;
}
return buttonRect;
}
/// <summary>
/// Returns a Rectangle that specifies the size and location of the up button
/// </summary>
/// <returns>A Rectangle that specifies the size and location of the up button</returns>
protected Rectangle GetUpButtonBounds()
{
Rectangle buttonRect = this.CalcButtonBounds();
buttonRect.Height /= 2;
return buttonRect;
}
/// <summary>
/// Returns a Rectangle that specifies the size and location of the down button
/// </summary>
/// <returns>A Rectangle that specifies the size and location of the down button</returns>
protected Rectangle GetDownButtonBounds()
{
Rectangle buttonRect = this.CalcButtonBounds();
int height = buttonRect.Height / 2;
buttonRect.Height -= height;
buttonRect.Y += height;
return buttonRect;
}
/// <summary>
/// Gets the NumberRendererData specific data used by the Renderer from
/// the specified Cell
/// </summary>
/// <param name="cell">The Cell to get the NumberRendererData data for</param>
/// <returns>The NumberRendererData data for the specified Cell</returns>
protected NumberRendererData GetNumberRendererData(Cell cell)
{
object rendererData = this.GetRendererData(cell);
if (rendererData == null || !(rendererData is NumberRendererData))
{
rendererData = new NumberRendererData();
this.SetRendererData(cell, rendererData);
}
return (NumberRendererData) rendererData;
}
/// <summary>
/// Gets whether the specified Table is using a NumericCellEditor to edit the
/// Cell at the specified CellPos
/// </summary>
/// <param name="table">The Table to check</param>
/// <param name="cellPos">A CellPos that represents the Cell to check</param>
/// <returns>true if the specified Table is using a NumericCellEditor to edit the
/// Cell at the specified CellPos, false otherwise</returns>
internal bool TableUsingNumericCellEditor(Table table, CellPos cellPos)
{
return (table.IsEditing && cellPos == table.EditingCell && table.EditingCellEditor is NumberCellEditor);
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the width of the dropdown button
/// </summary>
protected internal int ButtonWidth
{
get
{
return this.buttonWidth;
}
set
{
this.buttonWidth = value;
}
}
/// <summary>
/// Gets or sets whether the up and down buttons should be drawn
/// </summary>
protected bool ShowUpDownButtons
{
get
{
return this.showUpDownButtons;
}
set
{
this.showUpDownButtons = value;
}
}
/// <summary>
/// Gets or sets the alignment of the up and down buttons in the Cell
/// </summary>
protected LeftRightAlignment UpDownAlign
{
get
{
return this.upDownAlignment;
}
set
{
if (!Enum.IsDefined(typeof(LeftRightAlignment), value))
{
throw new InvalidEnumArgumentException("value", (int) value, typeof(LeftRightAlignment));
}
this.upDownAlignment = value;
}
}
/// <summary>
/// Gets or sets the maximum value for the Cell
/// </summary>
protected decimal Maximum
{
get
{
return this.maximum;
}
set
{
this.maximum = value;
if (this.minimum > this.maximum)
{
this.minimum = this.maximum;
}
}
}
/// <summary>
/// Gets or sets the minimum value for the Cell
/// </summary>
protected decimal Minimum
{
get
{
return this.minimum;
}
set
{
this.minimum = value;
if (this.minimum > this.maximum)
{
this.maximum = value;
}
}
}
#endregion
#region Events
#region Mouse
#region MouseLeave
/// <summary>
/// Raises the MouseLeave event
/// </summary>
/// <param name="e">A CellMouseEventArgs that contains the event data</param>
public override void OnMouseLeave(CellMouseEventArgs e)
{
base.OnMouseLeave(e);
if (this.ShowUpDownButtons || this.TableUsingNumericCellEditor(e.Table, e.CellPos))
{
if (e.Table.IsCellEditable(e.CellPos))
{
// get the button renderer data
NumberRendererData rendererData = this.GetNumberRendererData(e.Cell);
if (rendererData.UpButtonState != UpDownStates.Normal)
{
rendererData.UpButtonState = UpDownStates.Normal;
e.Table.Invalidate(e.CellRect);
}
else if (rendererData.DownButtonState != UpDownStates.Normal)
{
rendererData.DownButtonState = UpDownStates.Normal;
e.Table.Invalidate(e.CellRect);
}
}
}
}
#endregion
#region MouseUp
/// <summary>
/// Raises the MouseUp event
/// </summary>
/// <param name="e">A CellMouseEventArgs that contains the event data</param>
public override void OnMouseUp(CellMouseEventArgs e)
{
base.OnMouseUp(e);
//
if (this.ShowUpDownButtons || this.TableUsingNumericCellEditor(e.Table, e.CellPos))
{
if (e.Table.IsCellEditable(e.CellPos))
{
// get the renderer data
NumberRendererData rendererData = this.GetNumberRendererData(e.Cell);
rendererData.ClickPoint = new Point(-1, -1);
if (this.GetUpButtonBounds().Contains(e.X, e.Y))
{
rendererData.UpButtonState = UpDownStates.Hot;
if (!e.Table.IsEditing)
{
e.Table.EditCell(e.CellPos);
}
((IEditorUsesRendererButtons) e.Table.EditingCellEditor).OnEditorButtonMouseUp(this, e);
e.Table.Invalidate(e.CellRect);
}
else if (this.GetDownButtonBounds().Contains(e.X, e.Y))
{
rendererData.DownButtonState = UpDownStates.Hot;
if (!e.Table.IsEditing)
{
e.Table.EditCell(e.CellPos);
}
((IEditorUsesRendererButtons) e.Table.EditingCellEditor).OnEditorButtonMouseUp(this, e);
e.Table.Invalidate(e.CellRect);
}
}
}
}
#endregion
#region MouseDown
/// <summary>
/// Raises the MouseDown event
/// </summary>
/// <param name="e">A CellMouseEventArgs that contains the event data</param>
public override void OnMouseDown(CellMouseEventArgs e)
{
base.OnMouseDown(e);
//
if (this.ShowUpDownButtons || this.TableUsingNumericCellEditor(e.Table, e.CellPos))
{
if (e.Table.IsCellEditable(e.CellPos))
{
// get the button renderer data
NumberRendererData rendererData = this.GetNumberRendererData(e.Cell);
rendererData.ClickPoint = new Point(e.X, e.Y);
if (this.CalcButtonBounds().Contains(e.X, e.Y))
{
if (!(e.Table.ColumnModel.GetCellEditor(e.CellPos.Column) is NumberCellEditor))
{
throw new InvalidOperationException("Cannot edit Cell as NumberCellRenderer requires a NumberColumn that uses a NumberCellEditor");
}
if (!e.Table.IsEditing)
{
e.Table.EditCell(e.CellPos);
}
if (this.GetUpButtonBounds().Contains(e.X, e.Y))
{
rendererData.UpButtonState = UpDownStates.Pressed;
((IEditorUsesRendererButtons) e.Table.EditingCellEditor).OnEditorButtonMouseDown(this, e);
e.Table.Invalidate(e.CellRect);
}
else if (this.GetDownButtonBounds().Contains(e.X, e.Y))
{
rendererData.DownButtonState = UpDownStates.Pressed;
((IEditorUsesRendererButtons) e.Table.EditingCellEditor).OnEditorButtonMouseDown(this, e);
e.Table.Invalidate(e.CellRect);
}
}
}
}
}
#endregion
#region MouseMove
/// <summary>
/// Raises the MouseMove event
/// </summary>
/// <param name="e">A CellMouseEventArgs that contains the event data</param>
public override void OnMouseMove(XPTable.Events.CellMouseEventArgs e)
{
base.OnMouseMove(e);
if (this.ShowUpDownButtons || this.TableUsingNumericCellEditor(e.Table, e.CellPos))
{
if (e.Table.IsCellEditable(e.CellPos))
{
// get the button renderer data
NumberRendererData rendererData = this.GetNumberRendererData(e.Cell);
if (this.GetUpButtonBounds().Contains(e.X, e.Y))
{
if (rendererData.UpButtonState == UpDownStates.Normal)
{
if (e.Button == MouseButtons.Left && e.Row == e.Table.LastMouseDownCell.Row && e.Column == e.Table.LastMouseDownCell.Column)
{
if (this.GetUpButtonBounds().Contains(rendererData.ClickPoint))
{
rendererData.UpButtonState = UpDownStates.Pressed;
if (this.TableUsingNumericCellEditor(e.Table, e.CellPos))
{
((IEditorUsesRendererButtons) e.Table.EditingCellEditor).OnEditorButtonMouseDown(this, e);
}
}
else if (this.GetDownButtonBounds().Contains(rendererData.ClickPoint))
{
rendererData.DownButtonState = UpDownStates.Normal;
if (this.TableUsingNumericCellEditor(e.Table, e.CellPos))
{
((IEditorUsesRendererButtons) e.Table.EditingCellEditor).OnEditorButtonMouseUp(this, e);
}
}
}
else
{
rendererData.UpButtonState = UpDownStates.Hot;
if (rendererData.DownButtonState == UpDownStates.Hot)
{
rendererData.DownButtonState = UpDownStates.Normal;
}
}
e.Table.Invalidate(e.CellRect);
}
}
else if (this.GetDownButtonBounds().Contains(e.X, e.Y))
{
if (rendererData.DownButtonState == UpDownStates.Normal)
{
if (e.Button == MouseButtons.Left && e.Row == e.Table.LastMouseDownCell.Row && e.Column == e.Table.LastMouseDownCell.Column)
{
if (this.GetDownButtonBounds().Contains(rendererData.ClickPoint))
{
rendererData.DownButtonState = UpDownStates.Pressed;
if (this.TableUsingNumericCellEditor(e.Table, e.CellPos))
{
((IEditorUsesRendererButtons) e.Table.EditingCellEditor).OnEditorButtonMouseDown(this, e);
}
}
else if (this.GetUpButtonBounds().Contains(rendererData.ClickPoint))
{
rendererData.UpButtonState = UpDownStates.Normal;
if (this.TableUsingNumericCellEditor(e.Table, e.CellPos))
{
((IEditorUsesRendererButtons) e.Table.EditingCellEditor).OnEditorButtonMouseUp(this, e);
}
}
}
else
{
rendererData.DownButtonState = UpDownStates.Hot;
if (rendererData.UpButtonState == UpDownStates.Hot)
{
rendererData.UpButtonState = UpDownStates.Normal;
}
}
e.Table.Invalidate(e.CellRect);
}
}
else
{
if (rendererData.UpButtonState != UpDownStates.Normal || rendererData.DownButtonState != UpDownStates.Normal)
{
rendererData.UpButtonState = UpDownStates.Normal;
rendererData.DownButtonState = UpDownStates.Normal;
if (this.TableUsingNumericCellEditor(e.Table, e.CellPos))
{
((IEditorUsesRendererButtons) e.Table.EditingCellEditor).OnEditorButtonMouseUp(this, e);
}
e.Table.Invalidate(e.CellRect);
}
}
}
}
}
#endregion
#endregion
#region Paint
/// <summary>
/// Raises the PaintCell event
/// </summary>
/// <param name="e">A PaintCellEventArgs that contains the event data</param>
public override void OnPaintCell(PaintCellEventArgs e)
{
if (e.Table.ColumnModel.Columns[e.Column] is NumberColumn)
{
NumberColumn column = (NumberColumn) e.Table.ColumnModel.Columns[e.Column];
this.ShowUpDownButtons = column.ShowUpDownButtons;
this.UpDownAlign = column.UpDownAlign;
this.Maximum = column.Maximum;
this.Minimum = column.Minimum;
// if the table is editing this cell and the editor is a
// NumberCellEditor then we should display the updown buttons
if (e.Table.IsEditing && e.Table.EditingCell == e.CellPos && e.Table.EditingCellEditor is NumberCellEditor)
{
this.ShowUpDownButtons = true;
}
}
else
{
this.ShowUpDownButtons = false;
this.UpDownAlign = LeftRightAlignment.Right;
this.Maximum = 100;
this.Minimum = 0;
}
base.OnPaintCell(e);
}
/// <summary>
/// Raises the PaintBackground event
/// </summary>
/// <param name="e">A PaintCellEventArgs that contains the event data</param>
protected override void OnPaintBackground(PaintCellEventArgs e)
{
base.OnPaintBackground(e);
// don't bother going any further if the Cell is null
if (e.Cell == null)
{
return;
}
if (this.ShowUpDownButtons)
{
UpDownStates upButtonState = this.GetNumberRendererData(e.Cell).UpButtonState;
UpDownStates downButtonState = this.GetNumberRendererData(e.Cell).DownButtonState;
if (!e.Enabled)
{
upButtonState = UpDownStates.Disabled;
downButtonState = UpDownStates.Disabled;
}
ThemeManager.DrawUpDownButtons(e.Graphics, this.GetUpButtonBounds(), upButtonState, this.GetDownButtonBounds(), downButtonState);
}
}
/// <summary>
/// Raises the Paint event
/// </summary>
/// <param name="e">A PaintCellEventArgs that contains the event data</param>
protected override void OnPaint(PaintCellEventArgs e)
{
base.OnPaint(e);
// don't bother if the Cell is null
if (e.Cell == null)
{
return;
}
// get the Cells value
decimal decimalVal = decimal.MinValue;
if (e.Cell.Data != null && (e.Cell.Data is int || e.Cell.Data is double || e.Cell.Data is float || e.Cell.Data is decimal))
{
decimalVal = Convert.ToDecimal(e.Cell.Data);
}
// draw the value
if (decimalVal != decimal.MinValue)
{
Rectangle textRect = this.ClientRectangle;
if (this.ShowUpDownButtons)
{
textRect.Width -= this.CalcButtonBounds().Width - 1;
if (this.UpDownAlign == LeftRightAlignment.Left)
{
textRect.X = this.ClientRectangle.Right - textRect.Width;
}
}
if (e.Enabled)
{
e.Graphics.DrawString(decimalVal.ToString(this.Format), this.Font, this.ForeBrush, textRect, this.StringFormat);
}
else
{
e.Graphics.DrawString(decimalVal.ToString(this.Format), this.Font, this.GrayTextBrush, textRect, this.StringFormat);
}
}
if (e.Focused && e.Enabled)
{
Rectangle focusRect = this.ClientRectangle;
if (this.ShowUpDownButtons)
{
focusRect.Width -= this.CalcButtonBounds().Width;
if (this.UpDownAlign == LeftRightAlignment.Left)
{
focusRect.X = this.ClientRectangle.Right - focusRect.Width;
}
}
ControlPaint.DrawFocusRectangle(e.Graphics, focusRect);
}
}
#endregion
#endregion
}
}