// *****************************************************************************
//
// (c) Cabot Software, Inc. 2003
// All rights reserved. No warranty is intended or implied. If you use this
// software in your application please give credit where credit is due. Just
// a quick thank you in the about box is enough. You are not licensed to
// sell this software in it's either it's original state or based off your
// modifications.
//
// *****************************************************************************
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Forms;
namespace CabotSoftware.Controls
{
#region Helper Class
[ToolboxItem(false)]
public class TableSizeEventArgs : EventArgs
{
#region Member Variables
Size newTableSize;
#endregion
#region Constructors
public TableSizeEventArgs(Size newTableSize)
{
this.newTableSize = newTableSize;
}
#endregion
#region Properties
public Size NewTableSize
{
get { return this.newTableSize; }
}
#endregion
}
#endregion
#region Delegates
public delegate void TableSizeChangedEventHandler(object sender, TableSizeEventArgs e);
public delegate void TableSizeSelectedOKEventHandler(object sender, TableSizeEventArgs e);
public delegate void TableSizeSelectedCancelEventHandler(object sender, System.EventArgs e);
#endregion
public class TableSizeSelectorDropDown : System.Windows.Forms.Form
{
#region Event Declarations
public event TableSizeChangedEventHandler TableSizeChanged;
public event TableSizeSelectedOKEventHandler TableSizeSelectedOK;
public event TableSizeSelectedCancelEventHandler TableSizeSelectedCancel;
#endregion
#region Event Handlers
protected virtual void OnTableSizeChanged()
{
if( TableSizeChanged != null )
TableSizeChanged(this, new TableSizeEventArgs(this.tableSize));
}
protected virtual void OnTableSizeSelectedOK()
{
if( TableSizeSelectedOK != null )
TableSizeSelectedOK(this, new TableSizeEventArgs(this.tableSize));
}
protected virtual void OnTableSizeSelectedCancel()
{
if( TableSizeSelectedCancel != null )
TableSizeSelectedCancel(this, new System.EventArgs());
}
#endregion
#region Member Variables
// Internal variables.
private bool DrawLeftToRight = true;
private bool DrawTopToBottom = true;
private bool SelectingByKeyboard = false;
private int TextDisplayHeight = 0;
private Point StartLocation = new Point(0,0);
private Size maxDisplaySize = new Size(-1, -1);
private Rectangle ParentRectangle;
// Exposed property values.
private int cellSpacing = 4;
private int displayCellSize = 20;
private Size displaySize = new Size(4,4);
private Size tableSize = new Size(0,0);
private Size maxTableSize = new Size(0,0);
private Color selectedRectColor = SystemColors.Highlight;
private Color textColor = SystemColors.WindowText;
private Color outlineRectColor = SystemColors.WindowText;
private System.ComponentModel.Container components = null;
#endregion
#region Constructors
public TableSizeSelectorDropDown(Control ParentControl)
{
ParentRectangle = ParentControl.RectangleToScreen(ParentControl.ClientRectangle);
InitializeComponent();
// Set appropriate styles to avoid flickering.
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
}
#endregion
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// TableSizeSelectorDropDown
//
this.AutoScale = false;
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.BackColor = System.Drawing.SystemColors.Window;
this.ClientSize = new System.Drawing.Size(50, 47);
this.ForeColor = System.Drawing.SystemColors.Control;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "TableSizeSelectorDropDown";
this.ShowInTaskbar = false;
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
}
#endregion
#region Form Override methods
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
protected override void OnActivated(EventArgs e)
{
UpdateSizingInfo();
base.OnActivated(e);
}
protected override void OnDeactivate(EventArgs e)
{
Close();
if(this.DialogResult == DialogResult.OK )
OnTableSizeSelectedOK();
else
{
if( this.DialogResult == DialogResult.None )
this.DialogResult = DialogResult.Cancel;
OnTableSizeSelectedCancel();
}
base.OnDeactivate(e);
}
protected override void OnMouseLeave(System.EventArgs e)
{
// Once we've started sizing our table by the keyboard, we ignore the mouse move events.
if( SelectingByKeyboard == false )
UpdateTableSize(new Size(0,0));
}
protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
{
// Once we've started sizing our table by the keyboard, we can ignore the mouse move event.
if( SelectingByKeyboard == false )
{
// Is it time to resize our form? (We do this if we moved 1 pixel past the
// edge of the last rectangles.
Size newDisplaySize = displaySize;
if( e.Button == MouseButtons.Left || e.Button == MouseButtons.Right )
{
if( DrawLeftToRight )
{
if(e.X >= (this.displaySize.Width * (DisplayCellSize+CellSpacing)))
newDisplaySize.Width++;
}
else if( e.X <= CellSpacing )
{
newDisplaySize.Width++;
}
if( DrawTopToBottom )
{
if(e.Y >= (this.displaySize.Height * (DisplayCellSize+CellSpacing)))
newDisplaySize.Height++;
}
else if( (OverCancelRect(e.Y) == false ) && e.Y <= (TextDisplayHeight + CellSpacing) )
newDisplaySize.Height++;
}
System.Drawing.Size newTableSize = new System.Drawing.Size (0,0);
if( OverCancelRect( e.Y ) == false )
{
// Determine the 'size' of our selected region.
if( DrawLeftToRight == true )
{
newTableSize.Width = e.X / (CellSpacing + DisplayCellSize) +
((((e.X % (CellSpacing + DisplayCellSize))-CellSpacing)>0) ? 1 : 0);
}
else
{
newTableSize.Width = (ClientRectangle.Width - e.X) / (CellSpacing + DisplayCellSize) +
(((((ClientRectangle.Width - e.X ) % (CellSpacing + DisplayCellSize)) - CellSpacing)>0) ? 1 : 0);
}
if( DrawTopToBottom == true )
{
newTableSize.Height = e.Y / (CellSpacing + DisplayCellSize) +
((((e.Y % (CellSpacing + DisplayCellSize))-CellSpacing)>0) ? 1 : 0);
}
else
{
int DivResult = (ClientRectangle.Bottom - e.Y ) / (CellSpacing + DisplayCellSize);
int Remainder = (((((ClientRectangle.Bottom - e.Y ) % (CellSpacing + DisplayCellSize)))>0) ? 1 : 0);
newTableSize.Height = DivResult + Remainder;
}
}
UpdateDisplayAndSelectedSize(newDisplaySize, newTableSize);
}
}
protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs e)
{
base.OnMouseUp(e);
// Clicked in the 'text' area of the control?
if( OverCancelRect(e.Y) )
{
this.DialogResult = (TableSize.Height == 0 || TableSize.Width == 0) ?
DialogResult.Cancel : DialogResult.OK;
Close();
}
else if( e.Button != MouseButtons.Right )
{
this.DialogResult = DialogResult.OK;
Close();
}
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
// Create the arrays to display!
if( displaySize.Width > 0 && displaySize.Height> 0 )
{
System.Drawing.Pen pen = new System.Drawing.Pen(OutlineRectColor);
System.Drawing.SolidBrush brush = new System.Drawing.SolidBrush(SelectedRectColor);
System.Drawing.SolidBrush brushText = new System.Drawing.SolidBrush(this.textColor);
System.Drawing.Graphics g = e.Graphics;
//create our offscreen bitmap
Bitmap localBitmap = new Bitmap(ClientRectangle.Width,
ClientRectangle.Height);
Graphics bitmapGraphics = Graphics.FromImage(localBitmap);
bitmapGraphics.Clear(this.BackColor);
// Draw a rect around the outer edge of the control.
Rectangle rect = new Rectangle(
ClientRectangle.X,
ClientRectangle.Y,
ClientRectangle.Width-1,
ClientRectangle.Height-1);
bitmapGraphics.DrawRectangle(pen, rect);
rect.X = 0;
rect.Y = 0;
rect.Height = rect.Width = DisplayCellSize;
RectangleToClient(rect);
// Drawing from left to right?
int MinXPos = DrawLeftToRight ? 0 : (displaySize.Width - TableSize.Width);
int MaxXPos = DrawLeftToRight ? TableSize.Width : displaySize.Width;
Debug.WriteLine(string.Format("MinXPos: {0:d} MaxXPos: {1:d}", MinXPos, MaxXPos));
// Drawing from top to bottom?
int MinYPos = DrawTopToBottom ? 0 : (displaySize.Height - TableSize.Height);
int MaxYPos = DrawTopToBottom ? TableSize.Height : displaySize.Height;
Debug.WriteLine(string.Format("MinYPos: {0:d} MaxYPos: {1:d}", MinYPos, MaxYPos));
// Determine the initial point!
rect.X = CellSpacing;
rect.Y = CellSpacing + ((DrawTopToBottom) ? 0 : this.TextDisplayHeight);
for( int RowIdx = 0; RowIdx < DisplaySize.Height; RowIdx++ )
{
for( int ColIdx = 0; ColIdx < displaySize.Width; ColIdx++ )
{
// Fill in the rect?
if( (RowIdx >= MinYPos && RowIdx < MaxYPos)
&& ColIdx >= MinXPos && ColIdx < MaxXPos )
{
bitmapGraphics.FillRectangle(brush,rect);
}
// Outline the rectangle.
bitmapGraphics.DrawRectangle(pen,rect);
// Calculate next rect to draw.
rect.Offset(DisplayCellSize + CellSpacing, 0);
}
// Move rect back to leftmost starting position.
rect.X = CellSpacing;
rect.Y += (CellSpacing + DisplayCellSize);// * RowIdx + CellSpacing + TextOffset;
}
rect.X = 0;
rect.Width = ClientRectangle.Width;
if( DrawTopToBottom )
{
rect.Y = (CellSpacing + DisplayCellSize) * displaySize.Height + CellSpacing;
rect.Height = this.TextDisplayHeight;
bitmapGraphics.DrawLine(pen, 0, rect.Top, rect.Width, rect.Top);
rect.Y++;
}
else
{
rect.Y = 0;
rect.Height = this.TextDisplayHeight;
bitmapGraphics.DrawLine(pen,0, TextDisplayHeight, ClientRectangle.Width, TextDisplayHeight);
}
// output the text
StringFormat fmt = new StringFormat();
fmt.LineAlignment = StringAlignment.Center;
fmt.Alignment = StringAlignment.Center;
bitmapGraphics.DrawString(GetTablePropText(), Font, brushText, rect, fmt );
//push our bitmap forward to the screen
g.DrawImage(localBitmap, 0, 0);
bitmapGraphics.Dispose();
pen.Dispose();
brush.Dispose();
brushText.Dispose();
localBitmap.Dispose();
}
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
System.Drawing.Size newSelectedSize = TableSize;
System.Drawing.Size newDisplaySize = displaySize;
switch(keyData)
{
case Keys.Up:
case Keys.Down:
{
bool Expanding = ((keyData == Keys.Down && DrawTopToBottom) || (keyData == Keys.Up && (DrawTopToBottom == false)))
? true : false;
if( Expanding )
{
if( newSelectedSize.Height == newDisplaySize.Height )
{
newDisplaySize.Height = newDisplaySize.Height + 1;
newSelectedSize.Height = newSelectedSize.Height+1;
if( newSelectedSize.Width == 0 )
newSelectedSize.Width = 1;
UpdateDisplayAndSelectedSize(newDisplaySize, newSelectedSize);
}
else
{
newSelectedSize.Height = newSelectedSize.Height+1;
if( newSelectedSize.Width == 0 )
newSelectedSize.Width = 1;
UpdateTableSize(newSelectedSize);
}
}
else if( newSelectedSize.Height > 0 )
{
newSelectedSize.Height = newSelectedSize.Height-1;
if( newSelectedSize.Height == 0 )
newSelectedSize.Width = 0;
UpdateTableSize(newSelectedSize);
}
SelectingByKeyboard = true;
}
break;
case Keys.Right:
case Keys.Left:
{
bool Expanding = ((keyData == Keys.Right && DrawLeftToRight) || (keyData == Keys.Left && (DrawLeftToRight == false)))
? true : false;
if( Expanding )
{
if( newSelectedSize.Width == newDisplaySize.Width )
{
newDisplaySize.Width = newDisplaySize.Width + 1;
}
newSelectedSize.Width = newSelectedSize.Width+1;
if( newSelectedSize.Height == 0 )
newSelectedSize.Height = 1;
UpdateDisplayAndSelectedSize(newDisplaySize,newSelectedSize);
}
else if( newSelectedSize.Width > 0 )
{
newSelectedSize.Width = newSelectedSize.Width - 1;
// Time to zero out our selected size?
if( newSelectedSize.Width == 0 )
newSelectedSize.Height = 0;
UpdateTableSize(newSelectedSize);
}
SelectingByKeyboard = true;
}
break;
case Keys.Enter:
this.DialogResult = (TableSize.Height == 0 || TableSize.Width == 0) ?
DialogResult.Cancel : DialogResult.OK;
Close();
break;
case Keys.Escape:
this.DialogResult = DialogResult.Cancel;
Close();
break;
default:
break;
}
return base.ProcessCmdKey(ref msg,keyData);
}
protected override void SetBoundsCore(int x, int y, int w, int h, BoundsSpecified bs)
{
base.SetBoundsCore(
x,
y,
CalcWidth(),
CalcHeight(),
bs);
}
#endregion
#region Properties
public int CellSpacing
{
get { return this.cellSpacing; }
set { this.cellSpacing = value; }
}
public int DisplayCellSize
{
get { return this.displayCellSize; }
set { this.displayCellSize = value; }
}
public Size DisplaySize
{
get { return this.displaySize; }
set { this.displaySize = value; }
}
public System.Drawing.Size TableSize
{
get { return this.tableSize; }
}
public System.Drawing.Size MaxTableSize
{
get { return this.maxTableSize; }
set { this.maxTableSize = value; }
}
public System.Drawing.Color SelectedRectColor
{
get { return this.selectedRectColor; }
set { this.selectedRectColor = value; }
}
public System.Drawing.Color TextColor
{
get { return this.textColor; }
set { this.textColor = value; }
}
public System.Drawing.Color OutlineRectColor
{
get { return this.outlineRectColor; }
set { this.outlineRectColor = value; }
}
#endregion
#region Implementation
private void CalcMaxSize()
{
Screen DisplayOnScreen = Screen.FromPoint(new Point(ParentRectangle.X, ParentRectangle.Bottom));
Rectangle rect = RectangleToScreen(ClientRectangle);
// Get the total width we have available.
int TotalWidthAvailable = DrawLeftToRight ?
Math.Abs(DisplayOnScreen.WorkingArea.Width - rect.Left)
: Math.Abs(rect.Left + rect.Width - DisplayOnScreen.WorkingArea.X);
this.maxDisplaySize.Width = (TotalWidthAvailable - CellSpacing) / (DisplayCellSize + CellSpacing );
int TotalHeightAvailable = DrawTopToBottom ?
Math.Abs(DisplayOnScreen.WorkingArea.Height - rect.Top)
: Math.Abs(rect.Bottom - DisplayOnScreen.WorkingArea.Y );
this.maxDisplaySize.Height = (TotalHeightAvailable - TextDisplayHeight) / (DisplayCellSize + CellSpacing);
// Adjust if display size is greater than table size and max table size has been set.
if( maxTableSize.IsEmpty == false )
{
if( maxDisplaySize.Width > this.maxTableSize.Width )
maxDisplaySize.Width = this.MaxTableSize.Width;
if( maxDisplaySize.Height > this.maxTableSize.Height )
maxDisplaySize.Height = this.MaxTableSize.Height;
}
}
private bool CheckDisplaySizeIs(Size sizeToCheck)
{
return (sizeToCheck.Width <= this.maxDisplaySize.Width
&& sizeToCheck.Height <= this.maxDisplaySize.Height );
}
private int CalcHeight()
{
return ((CellSpacing + DisplayCellSize) * displaySize.Height) + CellSpacing + TextDisplayHeight;
}
private void CalcLocation()
{
// Determine which screen we're on and how big it is.
Screen DisplayedOnScreen = Screen.FromPoint(new Point(ParentRectangle.X, ParentRectangle.Bottom));
int MinScreenXPos = DisplayedOnScreen.Bounds.X;
int MinScreenYPos = DisplayedOnScreen.Bounds.Y;
int MaxScreenXPos = DisplayedOnScreen.Bounds.X + DisplayedOnScreen.Bounds.Width;
int MaxScreenYPos = DisplayedOnScreen.Bounds.Y + DisplayedOnScreen.Bounds.Height;
int DropdownWidth = CalcWidth();
int DropdownHeight = CalcHeight();
// Will we bump into the right edge of the window when we first display the control?
if((ParentRectangle.X + DropdownWidth) <= MaxScreenXPos )
{
if( ParentRectangle.X < MinScreenXPos )
StartLocation.X = MinScreenXPos;
else
StartLocation.X = ParentRectangle.X;
}
else
{
DrawLeftToRight = false;
// Make sure we aren't overhanging the left side of the screen.
if( Screen.FromPoint(new Point((ParentRectangle.X + ParentRectangle.Width), 0)) == DisplayedOnScreen )
StartLocation.X = ParentRectangle.Right-DropdownWidth;
else
StartLocation.X = MaxScreenXPos - DropdownWidth;
}
// And now check the bottom of the screen.
if( (ParentRectangle.Bottom + DropdownHeight) <= MaxScreenYPos )
StartLocation.Y = ParentRectangle.Bottom;
else
{
DrawTopToBottom = false;
StartLocation.Y = ParentRectangle.Y-DropdownHeight;
}
this.Location = StartLocation;
}
private int CalcWidth()
{
return ((CellSpacing + DisplayCellSize) * displaySize.Width) + CellSpacing + 1;
}
private string GetTablePropText()
{
string TablePropText = "Cancel";
Point pt = System.Windows.Forms.Cursor.Position;
pt = this.PointToClient(pt);
if( (SelectingByKeyboard == true ||
( pt.X >= this.ClientRectangle.Left && pt.X <= this.ClientRectangle.Right && OverCancelRect(pt.Y) == false))
&& (TableSize.Width > 0 && TableSize.Height > 0) )
{
TablePropText = string.Format("{0:d} x {1:d} Table", TableSize.Height, TableSize.Width);
}
return TablePropText;
}
private bool OverCancelRect( int MousePosY )
{
return DrawTopToBottom ?
MousePosY >= ((CellSpacing + DisplayCellSize) * displaySize.Height + CellSpacing + 1)
: MousePosY <= TextDisplayHeight;
}
private void UpdateDisplayAndSelectedSize(Size newDisplaySize, Size newTableSize)
{
bool Redraw = false;
if( CheckDisplaySizeIs(newDisplaySize) )
{
Redraw = true;
this.displaySize = newDisplaySize;
this.Size = new Size( CalcWidth(), CalcHeight());
if( DrawLeftToRight == false || DrawTopToBottom == false )
{
this.CalcLocation();
}
}
if( newTableSize != TableSize
&& newTableSize.Width <= displaySize.Width
&& newTableSize.Height <= displaySize.Height )
{
Redraw = true;
this.tableSize = newTableSize;
OnTableSizeChanged();
}
if( Redraw )
Invalidate();
}
private void UpdateSizingInfo()
{
// Calculate space to write out the text.
TextDisplayHeight = (int)Font.GetHeight() + CellSpacing * 2;
CalcLocation();
CalcMaxSize();
}
private void UpdateTableSize(System.Drawing.Size newTableSize)
{
// Verify that table size is different and that we
// haven't extended past our display boundaries.
if( newTableSize != TableSize
&& newTableSize.Width <= displaySize.Width
&& newTableSize.Height <= displaySize.Height )
{
System.Diagnostics.Debug.WriteLine(string.Format("TableSize updated from:\n\t{0:d}\nto:\t {1:s}",
TableSize.ToString(), newTableSize.ToString() ));
this.tableSize = newTableSize;
OnTableSizeChanged();
Invalidate();
}
}
#endregion
}
}