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; } } }