using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
namespace AMR.MEDS.Prototype.Controls
{
public class ClockControl : Control
{
#region private parts
private int hours = 12;
private int minutes = 0;
private int padding = 20;
private bool firstDigit = true;
private string fontName = "Arial Black";
#endregion
#region public members
public bool AM = true;
public TimeSpan Value;
public event EventHandler TimeChosen;
public string TimeString
{
get { return getHourString(); }
}
#endregion
#region color definitions
Pen hourPen = new Pen(Brushes.Black, 4);
Pen minutePen = new Pen(Brushes.Black, 2);
Pen whitePen = new Pen(Brushes.White, 4);
Pen thicker = new Pen(Brushes.Black, 2);
Brush normalBackBrush = new LinearGradientBrush(new Rectangle(0, 0, 100, 600), Color.White,
Color.Ivory, LinearGradientMode.Vertical);
Brush backBrush;
#endregion
///
/// Constructor
///
public ClockControl()
{
// double buffering, etc.
this.SetStyle(
ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw, true);
// events
this.MouseDown += new MouseEventHandler(ClockControl_Mouse);
this.MouseMove += new MouseEventHandler(ClockControl_Mouse);
this.MouseUp += new MouseEventHandler(ClockControl_MouseUp);
this.TimeChosen += new EventHandler(ClockControl_TimeChosen);
this.KeyDown += new KeyEventHandler(ClockControl_KeyDown);
this.KeyPress += new KeyPressEventHandler(ClockControl_KeyPress);
// init background brush
backBrush = normalBackBrush;
}
public string getHourString()
{
string hourstring = hours == 0 ? "12" : hours.ToString();
hourstring += ":" + minutes.ToString().PadLeft(2, '0');
return hourstring;
}
private void setHour(int h)
{
hours = h;
Refresh();
}
private void setMinute(int m)
{
minutes = m;
Refresh();
}
private void blinkRed()
{
Timer t = new Timer();
t.Interval = 80;
t.Tick += new EventHandler(t_Tick);
backBrush = Brushes.Pink;
Refresh();
t.Start();
}
void t_Tick(object sender, EventArgs e)
{
backBrush = normalBackBrush;
Refresh();
((Timer)sender).Stop();
((Timer)sender).Dispose();
}
#region Keyboard Stuff
void ClockControl_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsNumber(e.KeyChar))
{
int digit = int.Parse(e.KeyChar.ToString());
if (firstDigit)
{
if (digit >= 6)
{
blinkRed();
return;
}
digit *= 10;
firstDigit = false;
}
else
{
digit += minutes;
setMinute(digit);
TimeChosen(this, EventArgs.Empty);
firstDigit = true;
}
setMinute(digit);
}
}
void ClockControl_KeyDown(object sender, KeyEventArgs e)
{
Keys[] fKeys = new Keys[] { Keys.F1, Keys.F2, Keys.F3, Keys.F4, Keys.F5,
Keys.F6, Keys.F7, Keys.F8, Keys.F9, Keys.F10, Keys.F11, Keys.F12 };
for (int i = 0; i < fKeys.Length; i++)
{
if (e.KeyData == fKeys[i])
{
setHour(i+1);
firstDigit = true;
}
}
}
#endregion
void ClockControl_TimeChosen(object sender, EventArgs e)
{
// do nothing
}
///
/// Here's where the magic happens.
///
protected override void OnPaint(PaintEventArgs e)
{
// make it look nice
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
// create a rectangle for the clock, just a lil smaller than the control area
Rectangle surfaceRect = Rectangle.Inflate(ClientRectangle, -padding, -padding);
// keep this thing square
int rectSize = Math.Min(surfaceRect.Width, surfaceRect.Height);
surfaceRect = new Rectangle((ClientRectangle.Width / 2) - (surfaceRect.Width / 2),
surfaceRect.Y, rectSize, rectSize);
// keep this thing centered
surfaceRect.X = (ClientRectangle.Width / 2) - (surfaceRect.Width / 2);
// create a light background shadow - nah it's ugly
/*e.Graphics.FillEllipse(Brushes.LightGray, surfaceRect.X + 7,
surfaceRect.Y + 8, surfaceRect.Width, surfaceRect.Height);*/
// don't paint if it's too small
if (surfaceRect.Width < 20 || surfaceRect.Height < 20)
return;
// center point
Point zero = new Point(surfaceRect.Left + (surfaceRect.Width / 2),
surfaceRect.Top + (surfaceRect.Height / 2));
// fill #1
e.Graphics.FillEllipse(backBrush, surfaceRect);
// border
e.Graphics.DrawEllipse(thicker, surfaceRect);
int tickLen = (int)(surfaceRect.Width * 0.45);
int numLen = (int)(surfaceRect.Width * 0.35);
int inflateSize = (int)(surfaceRect.Width * 0.07);
// draw minute ticks
for (int i = 0; i < 60; i++)
{
double currAngle = 2.0 * Math.PI * (i / 60.0);
Point tickPt = new Point((int)(tickLen * Math.Sin(currAngle)),
(int)(-tickLen * Math.Cos(currAngle)));
tickPt.X += zero.X;
tickPt.Y += zero.Y;
e.Graphics.DrawLine(Pens.Red, zero, tickPt);
}
Rectangle smallerRectangle = Rectangle.Inflate(surfaceRect,
-inflateSize, -inflateSize);
e.Graphics.FillEllipse(backBrush, smallerRectangle);
// draw five minute (bigger) ticks and numbers
for (int i = 0; i < 60; i += 5)
{
double currAngle = 2.0 * Math.PI * (i / 60.0);
Point tickPt = new Point((int)(tickLen * Math.Sin(currAngle)),
(int)(-tickLen * Math.Cos(currAngle)));
tickPt.X += zero.X;
tickPt.Y += zero.Y;
e.Graphics.DrawLine(thicker, zero, tickPt);
}
inflateSize /= 4;
smallerRectangle.Inflate(-inflateSize, -inflateSize);
e.Graphics.FillEllipse(backBrush, smallerRectangle);
// draw numerals
int numeral = 12;
int numeralFontSize = Math.Max(1, inflateSize) * 4;
Font numeralFont = new Font("Arial Black", numeralFontSize);
for (int i = 0; i < 60; i += 5)
{
double currAngle = 2.0 * Math.PI * (i / 60.0);
Point numeralPt = new Point((int)(numLen * Math.Sin(currAngle)),
(int)(-numLen * Math.Cos(currAngle)));
numeralPt.X += zero.X;
numeralPt.Y += zero.Y;
StringFormat numFormat = new StringFormat();
numFormat.Alignment = StringAlignment.Center;
numFormat.LineAlignment = StringAlignment.Center;
e.Graphics.DrawString(numeral.ToString(), numeralFont,
Brushes.Black, numeralPt, numFormat);
numeral = numeral == 12 ? 1 : numeral + 1;
}
// digital time display
int fontSize = Math.Max(1, inflateSize + 3);
Font f = new Font(fontName, fontSize);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
e.Graphics.DrawString(getHourString(), f, Brushes.Black,
zero.X, zero.Y - (zero.Y / 2), sf);
// draw hour/minute hands
double hourAngle = 2.0 * Math.PI * (hours + minutes / 60.0) / 12.0;
double minuteAngle = 2.0 * Math.PI * (minutes / 60.0);
int hourHandLen = (int)(surfaceRect.Width * 0.33);
int minuteHandLen = (int)(surfaceRect.Width * 0.45);
Point hourPt = new Point((int)(hourHandLen * Math.Sin(hourAngle)),
(int)(-hourHandLen * Math.Cos(hourAngle)));
Point minutePt = new Point((int)(minuteHandLen * Math.Sin(minuteAngle)),
(int)(-minuteHandLen * Math.Cos(minuteAngle)));
hourPt.X += zero.X;
hourPt.Y += zero.Y;
minutePt.X += zero.X;
minutePt.Y += zero.Y;
e.Graphics.DrawLine(hourPen, zero, hourPt);
e.Graphics.DrawLine(minutePen, zero, minutePt);
// draw center dot
int dotWidth = (int)(surfaceRect.Width * 0.05);
e.Graphics.FillEllipse(Brushes.Black,
new Rectangle(zero.X - (dotWidth / 2), zero.Y - (dotWidth / 2),
dotWidth, dotWidth));
e.Graphics.DrawEllipse(whitePen,
new Rectangle(zero.X - (dotWidth / 2), zero.Y - (dotWidth / 2),
dotWidth, dotWidth));
}
private void ClockControl_Mouse(object sender, MouseEventArgs e)
{
if (!(e.Button == MouseButtons.Left))
return;
Value = CalculateTimeFromPoint(e.X, e.Y);
this.Refresh();
}
void ClockControl_MouseUp(object sender, MouseEventArgs e)
{
TimeChosen(this, EventArgs.Empty);
}
///
/// Figures out what time the user clicked.
///
/// X-Coordinate of the clicked point
/// Y-Coordinate of the clicked point
private TimeSpan CalculateTimeFromPoint(int x, int y)
{
Point mid = new Point(ClientRectangle.Width / 2, ClientRectangle.Height / 2);
Point p2 = new Point(x, y);
double angle = (Math.Atan2(p2.Y - mid.Y, p2.X - mid.X) * 180.0 / Math.PI);
angle += 90;
if (angle < 0)
angle = 270 + (90 - Math.Abs(angle));
double percent = angle / 720;
TimeSpan span = TimeSpan.FromMilliseconds(86400000 * percent);
if (span.Hours == 0)
span.Add(TimeSpan.FromHours(11));
hours = span.Hours;
minutes = span.Minutes;
return span;
}
///
/// Calculates the angle between two points.
///
protected double calcAngle(Point p1, Point p2)
{
double currRet = (Math.Atan2(p2.Y - p1.Y, p2.X - p1.X) * 180.0 / Math.PI);
currRet += 90;
if (currRet < 0)
currRet = 270 + (90 - Math.Abs(currRet));
return currRet;
}
}
}