/// /// Copyright (c) 2020 Sensus Slovensko a.s. /// using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Globalization; using System.Linq; using System.Windows.Forms; using Config.CalendarEvent; using TBF.Resources; namespace TBF.UI.Calendar { /// /// An enumeration describing various ways to view the calendar /// public enum CalendarViews { Month = 1, /// Renders the Calendar in a month view Day = 2 /// Renders the Calendar in a day view } /// /// A Winforms Calendar Control /// public class CalendarCtrl : UserControl { private DateTime _calendarDate; private Font _dayOfWeekFont; private Font _daysFont; private Font _todayFont; private Font _dateHeaderFont; private Font _dayViewTimeFont; private bool _showArrowControls; private bool _showTodayButton; private bool _showDateInHeader; private TodayButtonCtrl _btnToday; private NavigateLeftButtonCtrl _btnLeft; private NavigateRightButtonCtrl _btnRight; private bool _showingToolTip; private bool _showEventTooltips; private bool _loadPresetHolidays; private CalendarEventInstance _clickedEvent; private bool _showHiddenEvents; private bool _showDashedBorderOnDisabledEvents; private bool _dimDisabledEvents; private bool _highlightCurrentDay; private CalendarViews _calendarView; private readonly ScrollPanelCtrl _scrollPanel; private readonly List _events; private readonly List _rectangles; private readonly Dictionary _calendarDays; private readonly List _eventInstances; private readonly EventToolTipCtrl _eventTip; private ContextMenuStrip _contextMenuStrip1; private System.ComponentModel.IContainer components; private ToolStripMenuItem _miProperties; private const int MarginSize = 20; /// /// Indicates the font for the times on the day view /// public Font DayViewTimeFont { get { return _dayViewTimeFont; } set { _dayViewTimeFont = value; if (_calendarView == CalendarViews.Day) _scrollPanel.Refresh(); else Refresh(); } } /// /// Indicates the type of calendar to render, Month or Day view /// public CalendarViews CalendarView { get { return _calendarView; } set { _calendarView = value; _scrollPanel.Visible = value == CalendarViews.Day; Refresh(); } } /// /// Indicates whether today's date should be highlighted /// public bool HighlightCurrentDay { get { return _highlightCurrentDay; } set { _highlightCurrentDay = value; Refresh(); } } /// /// Indicates whether events can be right-clicked and edited /// public bool AllowEditingEvents { get; set; } /// /// Indicates whether disabled events will appear as "dimmed". /// This property is only used if is set to true. /// public bool DimDisabledEvents { get { return _dimDisabledEvents; } set { _dimDisabledEvents = value; Refresh(); } } /// /// Indicates if a dashed border should show up around disabled events. /// This property is only used if is set to true. /// public bool ShowDashedBorderOnDisabledEvents { get { return _showDashedBorderOnDisabledEvents; } set { _showDashedBorderOnDisabledEvents = value; Refresh(); } } /// /// Indicates whether disabled events should show up on the calendar control /// public bool ShowDisabledEvents { get { return _showHiddenEvents; } set { _showHiddenEvents = value; Refresh(); } } /// /// Indicates whether Federal Holidays are automatically preloaded onto the calendar /// public bool LoadPresetHolidays { get { return _loadPresetHolidays; } set { _loadPresetHolidays = value; if (_loadPresetHolidays) { _events.Clear(); PresetHolidays(); Refresh(); } else { _events.Clear(); Refresh(); } } } /// /// Indicates whether hovering over an event will display a tooltip of the event /// public bool ShowEventTooltips { get { return _showEventTooltips; } set { _showEventTooltips = value; _eventTip.Visible = false; } } /// /// Get or Set this value to the Font you wish to use to render the date in the upper right corner /// public Font DateHeaderFont { get { return _dateHeaderFont; } set { _dateHeaderFont = value; Refresh(); } } /// /// Indicates whether the date should be displayed in the upper right hand corner of the calendar control /// public bool ShowDateInHeader { get { return _showDateInHeader; } set { _showDateInHeader = value; if (_calendarView == CalendarViews.Day) ResizeScrollPanel(); Refresh(); } } /// /// Indicates whether the calendar control should render the previous/next month buttons /// public bool ShowArrowControls { get { return _showArrowControls; } set { _showArrowControls = value; _btnLeft.Visible = value; _btnRight.Visible = value; if (_calendarView == CalendarViews.Day) ResizeScrollPanel(); Refresh(); } } /// /// Indicates whether the calendar control should render the Today button /// public bool ShowTodayButton { get { return _showTodayButton; } set { _showTodayButton = value; _btnToday.Visible = value; if (_calendarView == CalendarViews.Day) ResizeScrollPanel(); Refresh(); } } /// /// The font used to render the Today button /// public Font TodayFont { get { return _todayFont; } set { _todayFont = value; Refresh(); } } /// /// The font used to render the number days on the calendar /// public Font DaysFont { get { return _daysFont; } set { _daysFont = value; Refresh(); } } /// /// The font used to render the days of the week text /// public Font DayOfWeekFont { get { return _dayOfWeekFont; } set { _dayOfWeekFont = value; Refresh(); } } /// /// The Date that the calendar is currently showing /// public DateTime CalendarDate { get { return _calendarDate; } set { _calendarDate = value; Refresh(); } } /// /// Calendar Constructor /// public CalendarCtrl() { InitializeComponent(); _events = new List(); _rectangles = new List(); _calendarDays = new Dictionary(); _eventInstances = new List(); _calendarDate = DateTime.Now; _dayOfWeekFont = new Font("Verdana", 10, FontStyle.Regular); _daysFont = new Font("Verdana", 10, FontStyle.Regular); _todayFont = new Font("Verdana", 10, FontStyle.Bold); _dateHeaderFont = new Font("Verdana", 12, FontStyle.Bold); _dayViewTimeFont = new Font("Verdana", 10, FontStyle.Bold); _showArrowControls = true; _showDateInHeader = true; _showTodayButton = true; _showingToolTip = false; _clickedEvent = null; _showHiddenEvents = true; _showDashedBorderOnDisabledEvents = false; _dimDisabledEvents = true; AllowEditingEvents = true; _highlightCurrentDay = true; _calendarView = CalendarViews.Month; _scrollPanel = new ScrollPanelCtrl(); _scrollPanel.RightButtonClicked += ScrollPanelRightButtonClicked; _showEventTooltips = true; _eventTip = new EventToolTipCtrl { Visible = false }; Controls.Add(_eventTip); LoadPresetHolidays = false; _scrollPanel.Visible = false; Controls.Add(_scrollPanel); Refresh(); } private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this._contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); this._miProperties = new System.Windows.Forms.ToolStripMenuItem(); this._btnRight = new TBF.UI.Calendar.NavigateRightButtonCtrl(); this._btnLeft = new TBF.UI.Calendar.NavigateLeftButtonCtrl(); this._btnToday = new TBF.UI.Calendar.TodayButtonCtrl(); this._contextMenuStrip1.SuspendLayout(); this.SuspendLayout(); // // _contextMenuStrip1 // this._contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this._miProperties}); this._contextMenuStrip1.Name = "_contextMenuStrip1"; this._contextMenuStrip1.Size = new System.Drawing.Size(137, 26); // // _miProperties // this._miProperties.Name = "_miProperties"; this._miProperties.Size = new System.Drawing.Size(136, 22); this._miProperties.Text = "Properties..."; this._miProperties.Click += new System.EventHandler(this.MenuItemPropertiesClick); // // _btnRight // this._btnRight.BackColor = System.Drawing.Color.Transparent; this._btnRight.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220))))); this._btnRight.ButtonColor = System.Drawing.Color.FromArgb(((int)(((byte)(243)))), ((int)(((byte)(243)))), ((int)(((byte)(243))))); this._btnRight.ButtonFont = new System.Drawing.Font("Verdana", 8F, System.Drawing.FontStyle.Bold); this._btnRight.ButtonText = ">"; this._btnRight.FocusColor = System.Drawing.Color.FromArgb(((int)(((byte)(77)))), ((int)(((byte)(144)))), ((int)(((byte)(254))))); this._btnRight.HighlightBorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(198)))), ((int)(((byte)(198)))), ((int)(((byte)(198))))); this._btnRight.HighlightButtonColor = System.Drawing.Color.FromArgb(((int)(((byte)(246)))), ((int)(((byte)(246)))), ((int)(((byte)(246))))); this._btnRight.Location = new System.Drawing.Point(138, 20); this._btnRight.Name = "_btnRight"; this._btnRight.Size = new System.Drawing.Size(42, 29); this._btnRight.TabIndex = 2; this._btnRight.TextColor = System.Drawing.Color.Black; this._btnRight.ButtonClicked += new Common.UIControls.CoolButtonCtrl.ButtonClickedArgs(this.BtnRightButtonClicked); // // _btnLeft // this._btnLeft.BackColor = System.Drawing.Color.Transparent; this._btnLeft.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220))))); this._btnLeft.ButtonColor = System.Drawing.Color.FromArgb(((int)(((byte)(243)))), ((int)(((byte)(243)))), ((int)(((byte)(243))))); this._btnLeft.ButtonFont = new System.Drawing.Font("Verdana", 8F, System.Drawing.FontStyle.Bold); this._btnLeft.ButtonText = "<"; this._btnLeft.FocusColor = System.Drawing.Color.FromArgb(((int)(((byte)(77)))), ((int)(((byte)(144)))), ((int)(((byte)(254))))); this._btnLeft.HighlightBorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(198)))), ((int)(((byte)(198)))), ((int)(((byte)(198))))); this._btnLeft.HighlightButtonColor = System.Drawing.Color.FromArgb(((int)(((byte)(246)))), ((int)(((byte)(246)))), ((int)(((byte)(246))))); this._btnLeft.Location = new System.Drawing.Point(98, 20); this._btnLeft.Name = "_btnLeft"; this._btnLeft.Size = new System.Drawing.Size(42, 29); this._btnLeft.TabIndex = 1; this._btnLeft.TextColor = System.Drawing.Color.Black; this._btnLeft.ButtonClicked += new Common.UIControls.CoolButtonCtrl.ButtonClickedArgs(this.BtnLeftButtonClicked); // // _btnToday // this._btnToday.BackColor = System.Drawing.Color.Transparent; this._btnToday.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220))))); this._btnToday.ButtonColor = System.Drawing.Color.FromArgb(((int)(((byte)(243)))), ((int)(((byte)(243)))), ((int)(((byte)(243))))); this._btnToday.ButtonFont = new System.Drawing.Font("Verdana", 8F, System.Drawing.FontStyle.Bold); this._btnToday.ButtonText = "Today"; this._btnToday.FocusColor = System.Drawing.Color.FromArgb(((int)(((byte)(77)))), ((int)(((byte)(144)))), ((int)(((byte)(254))))); this._btnToday.HighlightBorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(198)))), ((int)(((byte)(198)))), ((int)(((byte)(198))))); this._btnToday.HighlightButtonColor = System.Drawing.Color.FromArgb(((int)(((byte)(246)))), ((int)(((byte)(246)))), ((int)(((byte)(246))))); this._btnToday.Location = new System.Drawing.Point(19, 20); this._btnToday.Name = "_btnToday"; this._btnToday.Size = new System.Drawing.Size(72, 29); this._btnToday.TabIndex = 0; this._btnToday.TextColor = System.Drawing.Color.Black; this._btnToday.ButtonClicked += new Common.UIControls.CoolButtonCtrl.ButtonClickedArgs(this.BtnTodayButtonClicked); // // CalendarCtrl // this.Controls.Add(this._btnRight); this.Controls.Add(this._btnLeft); this.Controls.Add(this._btnToday); this.DoubleBuffered = true; this.Name = "CalendarCtrl"; this.Size = new System.Drawing.Size(512, 512); this.Load += new System.EventHandler(this.CalendarLoad); this.Paint += new System.Windows.Forms.PaintEventHandler(this.CalendarPaint); this.MouseClick += new System.Windows.Forms.MouseEventHandler(this.CalendarMouseClick); this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.CalendarMouseMove); this.Resize += new System.EventHandler(this.CalendarResize); this._contextMenuStrip1.ResumeLayout(false); this.ResumeLayout(false); } /// /// Clears all calendar events /// /// The to add to the calendar public void ClearEvents() { _events.Clear(); Refresh(); } /// /// Adds an event to the calendar /// /// The to add to the calendar public void AddEvent(ICalendarEvent calendarEvent) { _events.Add(calendarEvent); Refresh(); } /// /// Removes an event from the calendar /// /// The to remove to the calendar public void RemoveEvent(ICalendarEvent calendarEvent) { _events.Remove(calendarEvent); Refresh(); } private void CalendarLoad(object sender, EventArgs e) { if (Parent != null) Parent.Resize += ParentResize; ResizeScrollPanel(); } private void CalendarPaint(object sender, PaintEventArgs e) { if (_showingToolTip) return; if (_calendarView == CalendarViews.Month) { RenderMonthCalendar(e); } else if (_calendarView == CalendarViews.Day) { RenderDayCalendar(e); } } private void CalendarMouseMove(object sender, MouseEventArgs e) { if (!_showEventTooltips) return; foreach (var evInst in _eventInstances) { if ((evInst.EventArea.Contains(e.X, e.Y) && evInst.Event.TooltipEnabled && _calendarView == CalendarViews.Month) || (_calendarView == CalendarViews.Day && evInst.EventArea.Contains(e.X, e.Y + _scrollPanel.ScrollOffset) && evInst.Event.TooltipEnabled)) { _eventTip.ShouldRender = false; _showingToolTip = true; _eventTip.EventToolTipText = evInst.Event.ToString(); if (evInst.Event.AllDay == false) _eventTip.EventToolTipText += "\n" + evInst.Event.Date.ToShortTimeString(); _eventTip.Location = new Point(e.X + 5, e.Y - _eventTip.CalculateSize().Height); _eventTip.ShouldRender = true; _eventTip.Visible = true; _showingToolTip = false; return; } } _eventTip.Visible = false; _eventTip.ShouldRender = false; } private void ScrollPanelRightButtonClicked(object sender, MouseEventArgs e) { if (AllowEditingEvents && _calendarView == CalendarViews.Day) { foreach (var evInst in _eventInstances) { if (evInst.EventArea.Contains(e.X, e.Y + _scrollPanel.ScrollOffset) && !evInst.Event.ReadOnly) { _clickedEvent = evInst; _contextMenuStrip1.Show(_scrollPanel, new Point(e.X, e.Y)); _eventTip.Visible = false; _eventTip.ShouldRender = false; break; } } } } private void CalendarMouseClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right && AllowEditingEvents && _calendarView == CalendarViews.Month) { foreach (var evInst in _eventInstances) { if (evInst.EventArea.Contains(e.X, e.Y) && !evInst.Event.ReadOnly) { _clickedEvent = evInst; _contextMenuStrip1.Show(this, e.Location); _eventTip.Visible = false; _eventTip.ShouldRender = false; break; } } } } private void BtnTodayButtonClicked(object sender) { _calendarDate = DateTime.Now; Refresh(); } private void BtnLeftButtonClicked(object sender) { if (_calendarView == CalendarViews.Month) _calendarDate = _calendarDate.AddMonths(-1); else if (_calendarView == CalendarViews.Day) _calendarDate = _calendarDate.AddDays(-1); Refresh(); } private void BtnRightButtonClicked(object sender) { if (_calendarView == CalendarViews.Day) _calendarDate = _calendarDate.AddDays(1); else if (_calendarView == CalendarViews.Month) _calendarDate = _calendarDate.AddMonths(1); Refresh(); } private void MenuItemPropertiesClick(object sender, EventArgs e) { if (_clickedEvent == null) return; var ed = new EventDetailsForm { Event = _clickedEvent.Event }; if (ed.ShowDialog(this) == DialogResult.OK) { _events.Remove(_clickedEvent.Event); _events.Add(ed.NewEvent); Refresh(); } _clickedEvent = null; } private void ParentResize(object sender, EventArgs e) { ResizeScrollPanel(); Refresh(); } private void PresetHolidays() { var aprilFools = new HolidayEvent { Date = new DateTime(DateTime.Now.Year, 4, 1), Frequency = Frequency.Yearly, Title = "April Fools Day" }; AddEvent(aprilFools); var memorialDay = new HolidayEvent { Date = new DateTime(DateTime.Now.Year, 5, 28), Frequency = Frequency.Custom, Title = "Memorial Day", CustomRecurringFunction = MemorialDayHandler }; AddEvent(memorialDay); var newYears = new HolidayEvent { Date = new DateTime(DateTime.Now.Year, 1, 1), Frequency = Frequency.Yearly, Title = "New Years Day" }; AddEvent(newYears); var mlkDay = new HolidayEvent { Date = new DateTime(DateTime.Now.Year, 1, 15), Frequency = Frequency.Custom, Title = "Martin Luther King Jr. Day", CustomRecurringFunction = MlkDayHandler }; AddEvent(mlkDay); var presidentsDay = new HolidayEvent { Date = new DateTime(DateTime.Now.Year, 2, 15), Frequency = Frequency.Custom, Title = "President's Day", CustomRecurringFunction = MlkDayHandler }; AddEvent(presidentsDay); var independanceDay = new HolidayEvent { Date = new DateTime(DateTime.Now.Year, 7, 4), Frequency = Frequency.Yearly, Title = "Independence Day" }; AddEvent(independanceDay); var laborDay = new HolidayEvent { Date = new DateTime(DateTime.Now.Year, 9, 1), Frequency = Frequency.Custom, Title = "Labor Day", CustomRecurringFunction = LaborDayHandler }; AddEvent(laborDay); var columbusDay = new HolidayEvent { Date = new DateTime(DateTime.Now.Year, 10, 14), Frequency = Frequency.Custom, Title = "Columbus Day", CustomRecurringFunction = ColumbusDayHandler }; AddEvent(columbusDay); var veteransDay = new HolidayEvent { Date = new DateTime(DateTime.Now.Year, 11, 11), Frequency = Frequency.Yearly, Title = "Veteran's Day" }; AddEvent(veteransDay); var thanksgivingDay = new HolidayEvent { Date = new DateTime(DateTime.Now.Year, 11, 11), Frequency = Frequency.Custom, Title = "Thanksgiving Day", CustomRecurringFunction = ThanksgivingDayHandler }; AddEvent(thanksgivingDay); var christmas = new HolidayEvent { Date = new DateTime(DateTime.Now.Year, 12, 25), Frequency = Frequency.Yearly, Title = "Christmas Day" }; AddEvent(christmas); } [CustomRecurringFunction("Thanksgiving Day Handler", "Selects the fourth Thursday in the month")] private bool ThanksgivingDayHandler(ICalendarEvent evnt, DateTime dt) { if (dt.DayOfWeek == DayOfWeek.Thursday && dt.Day > 21 && dt.Day <= 28 && dt.Month == evnt.Date.Month) return true; return false; } [CustomRecurringFunction("Columbus Day Handler", "Selects the second Monday in the month")] private bool ColumbusDayHandler(ICalendarEvent evnt, DateTime dt) { if (dt.DayOfWeek == DayOfWeek.Monday && dt.Day > 7 && dt.Day <= 14 && dt.Month == evnt.Date.Month) return true; return false; } [CustomRecurringFunction("Labor Day Handler", "Selects the first Monday in the month")] private bool LaborDayHandler(ICalendarEvent evnt, DateTime dt) { if (dt.DayOfWeek == DayOfWeek.Monday && dt.Day <= 7 && dt.Month == evnt.Date.Month) return true; return false; } [CustomRecurringFunction("Martin Luther King Jr. Day Handler", "Selects the third Monday in the month")] private bool MlkDayHandler(ICalendarEvent evnt, DateTime dt) { if (dt.DayOfWeek == DayOfWeek.Monday && dt.Day > 14 && dt.Day <= 21 && dt.Month == evnt.Date.Month) return true; return false; } [CustomRecurringFunction("Memorial Day Handler", "Selects the last Monday in the month")] private bool MemorialDayHandler(ICalendarEvent evnt, DateTime dt) { DateTime dt2 = LastDayOfWeekInMonth(dt, DayOfWeek.Monday); if (dt.Month == evnt.Date.Month && dt2.Day == dt.Date.Day) return true; return false; } private DateTime LastDayOfWeekInMonth(DateTime day, DayOfWeek dow) { DateTime lastDay = new DateTime(day.Year, day.Month, 1).AddMonths(1).AddDays(-1); DayOfWeek lastDow = lastDay.DayOfWeek; int diff = dow - lastDow; if (diff > 0) diff -= 7; System.Diagnostics.Debug.Assert(diff <= 0); return lastDay.AddDays(diff); } private int Max(params float[] value) { return (int)value.Max(i => Math.Ceiling(i)); } internal Bitmap RequestImage() { const int cellHourWidth = 60; const int cellHourHeight = 30; var bmp = new Bitmap(ClientSize.Width, cellHourWidth * 24); Graphics g = Graphics.FromImage(bmp); g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; var dt = new DateTime(_calendarDate.Year, _calendarDate.Month, _calendarDate.Day, 0, 0, 0); int xStart = 0; int yStart = 0; g.DrawRectangle(Pens.Black, 0, 0, ClientSize.Width - MarginSize * 2 - 2, cellHourHeight * 24); for (int i = 0; i < 24; i++) { var textWidth = (int)g.MeasureString(dt.ToString("htt").ToLower(), _dayViewTimeFont).Width; g.DrawRectangle(Pens.Black, xStart, yStart, cellHourWidth, cellHourHeight); g.DrawLine(Pens.Black, xStart + cellHourWidth, yStart + cellHourHeight, ClientSize.Width - MarginSize * 2 - 3, yStart + cellHourHeight); g.DrawLine(Pens.DarkGray, xStart + cellHourWidth, yStart + cellHourHeight / 2, ClientSize.Width - MarginSize * 2 - 3, yStart + cellHourHeight / 2); g.DrawString(dt.ToString("htt").ToLower(), _dayViewTimeFont, Brushes.Black, xStart + cellHourWidth - textWidth, yStart); yStart += cellHourHeight; dt = dt.AddHours(1); } dt = new DateTime(_calendarDate.Year, _calendarDate.Month, _calendarDate.Day, 23, 59, 0); List evnts = _events.Where(e => (!e.Hidden || _showHiddenEvents) && Config.CalendarEvent.Utils.DayMatchesExactly(e, dt)) .ToList() .OrderBy(d => d.Date) .ToList(); xStart = cellHourWidth + 1; yStart = 0; g.Clip = new Region(new Rectangle(0, 0, ClientSize.Width - MarginSize * 2 - 2, cellHourHeight * 24)); _eventInstances.Clear(); for (int i = 0; i < 24; i++) { dt = new DateTime(_calendarDate.Year, _calendarDate.Month, _calendarDate.Day, 0, 0, 0); dt = dt.AddHours(i); foreach (var evnt in evnts) { TimeSpan ts = TimeSpan.FromHours(1); if (evnt.Date.Ticks >= dt.Ticks && evnt.Date.Ticks < dt.Add(ts).Ticks && i >= evnt.Date.Hour) { int divisor = evnt.Date.Minute == 0 ? 1 : 60 / evnt.Date.Minute; Color clr = Color.FromArgb((evnt.BackColor & 0x7FFFFFFF) | unchecked((int)0xAF000000)); g.FillRectangle(new SolidBrush(GetFinalBackColor()), xStart, yStart + cellHourHeight / divisor + 1, ClientSize.Width - MarginSize * 2 - cellHourWidth - 3, cellHourHeight * ts.Hours - 1); g.FillRectangle(new SolidBrush(clr), xStart, yStart + cellHourHeight / divisor + 1, ClientSize.Width - MarginSize * 2 - cellHourWidth - 3, cellHourHeight * ts.Hours - 1); g.DrawString(evnt.ToString(), new Font("Verdana", 8, FontStyle.Bold), new SolidBrush(Color.FromArgb(evnt.TextColor)), xStart, yStart + cellHourHeight / divisor); _eventInstances.Add(new CalendarEventInstance { Event = evnt, EventArea = new Rectangle(xStart, yStart + cellHourHeight / divisor + 1, ClientSize.Width - MarginSize * 2 - cellHourWidth - 3, cellHourHeight * ts.Hours), Date = dt }); } } yStart += cellHourHeight; } g.Dispose(); return bmp; } private Color GetFinalBackColor() { Control c = this; while (c != null) { if (c.BackColor != Color.Transparent) return c.BackColor; c = c.Parent; } return Color.Transparent; } private void ResizeScrollPanel() { int controlsSpacing = ((!_showTodayButton) && (!_showDateInHeader) && (!_showArrowControls)) ? 0 : 30; _scrollPanel.Location = new Point(MarginSize, MarginSize + controlsSpacing); _scrollPanel.Size = new Size(ClientSize.Width - MarginSize * 2 - 1, ClientSize.Height - MarginSize - 1 - controlsSpacing); } private void RenderDayCalendar(PaintEventArgs e) { Graphics g = e.Graphics; if (_showDateInHeader) { SizeF dateHeaderSize = g.MeasureString( _calendarDate.ToString("MMMM") + " " + _calendarDate.Day.ToString(CultureInfo.InvariantCulture) + ", " + _calendarDate.Year.ToString(CultureInfo.InvariantCulture), DateHeaderFont); g.DrawString( _calendarDate.ToString("MMMM") + " " + _calendarDate.Day.ToString(CultureInfo.InvariantCulture) + ", " + _calendarDate.Year.ToString(CultureInfo.InvariantCulture), _dateHeaderFont, Brushes.Black, ClientSize.Width - MarginSize - dateHeaderSize.Width, MarginSize); } } private void RenderMonthCalendar(PaintEventArgs e) { _calendarDays.Clear(); _eventInstances.Clear(); var bmp = new Bitmap(ClientSize.Width, ClientSize.Height); Graphics g = Graphics.FromImage(bmp); e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; SizeF monSize = g.MeasureString(Strings.Mon, _dayOfWeekFont); SizeF tueSize = g.MeasureString(Strings.Tue, _dayOfWeekFont); SizeF wedSize = g.MeasureString(Strings.Wed, _dayOfWeekFont); SizeF thuSize = g.MeasureString(Strings.Thu, _dayOfWeekFont); SizeF friSize = g.MeasureString(Strings.Fri, _dayOfWeekFont); SizeF satSize = g.MeasureString(Strings.Sat, _dayOfWeekFont); SizeF sunSize = g.MeasureString(Strings.Sun, _dayOfWeekFont); SizeF dateHeaderSize = g.MeasureString(_calendarDate.ToString("MMMM") + " " + _calendarDate.Year.ToString(CultureInfo.InvariantCulture), _dateHeaderFont); int headerSpacing = Max(sunSize.Height, monSize.Height, tueSize.Height, wedSize.Height, thuSize.Height, friSize.Height, satSize.Height) + 5; int controlsSpacing = ((!_showTodayButton) && (!_showDateInHeader) && (!_showArrowControls)) ? 0 : 30; int cellWidth = (ClientSize.Width - MarginSize * 2) / 7; int numWeeks = NumberOfWeeks(_calendarDate.Year, _calendarDate.Month); int cellHeight = (ClientSize.Height - MarginSize * 2 - headerSpacing - controlsSpacing) / numWeeks; int xStart = MarginSize; int yStart = MarginSize; DayOfWeek startWeekEnum = new DateTime(_calendarDate.Year, _calendarDate.Month, 1).DayOfWeek; int startWeek; if (CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek == DayOfWeek.Monday) { startWeek = startWeekEnum == DayOfWeek.Sunday ? 7 : (int)startWeekEnum; } else { startWeek = (int)startWeekEnum + 1; } int rogueDays = startWeek - 1; yStart += headerSpacing + controlsSpacing; int counter = 1; int counter2 = 1; bool first = false; bool first2 = false; _btnToday.Location = new Point(MarginSize, MarginSize); /// /// Draw the month frame without calendar events /// for (int y = 0; y < numWeeks; y++) { for (int x = 0; x < 7; x++) { if (rogueDays == 0 && counter <= DateTime.DaysInMonth(_calendarDate.Year, _calendarDate.Month)) { if (!_calendarDays.ContainsKey(counter)) _calendarDays.Add(counter, new Point(xStart, (int)(yStart + 2f + g.MeasureString(counter.ToString(CultureInfo.InvariantCulture), _daysFont).Height))); if (_calendarDate.Year == DateTime.Now.Year && _calendarDate.Month == DateTime.Now.Month && counter == DateTime.Now.Day && _highlightCurrentDay) { /// Highlight current day g.FillRectangle(new SolidBrush(Color.FromArgb(234, 234, 234)), xStart, yStart, cellWidth, cellHeight); } if (first == false) { first = true; if (_calendarDate.Year == DateTime.Now.Year && _calendarDate.Month == DateTime.Now.Month && counter == DateTime.Now.Day) { g.DrawString(_calendarDate.ToString("MMM") + " " + counter.ToString(CultureInfo.InvariantCulture), _todayFont, Brushes.Black, xStart + 5, yStart + 2); } else { g.DrawString(_calendarDate.ToString("MMM") + " " + counter.ToString(CultureInfo.InvariantCulture), _daysFont, Brushes.Black, xStart + 5, yStart + 2); } } else { if (_calendarDate.Year == DateTime.Now.Year && _calendarDate.Month == DateTime.Now.Month && counter == DateTime.Now.Day) { g.DrawString(counter.ToString(CultureInfo.InvariantCulture), _todayFont, Brushes.Black, xStart + 5, yStart + 2); } else { g.DrawString(counter.ToString(CultureInfo.InvariantCulture), _daysFont, Brushes.Black, xStart + 5, yStart + 2); } } counter++; } else if (rogueDays > 0) { int dm = DateTime.DaysInMonth(_calendarDate.AddMonths(-1).Year, _calendarDate.AddMonths(-1).Month) - rogueDays + 1; g.DrawString(dm.ToString(CultureInfo.InvariantCulture), _daysFont, new SolidBrush(Color.FromArgb(170, 170, 170)), xStart + 5, yStart + 2); rogueDays--; } g.DrawRectangle(Pens.DarkGray, xStart, yStart, cellWidth, cellHeight); if (rogueDays == 0 && counter > DateTime.DaysInMonth(_calendarDate.Year, _calendarDate.Month)) { if (first2 == false) first2 = true; else { if (counter2 == 1) { g.DrawString(_calendarDate.AddMonths(1).ToString("MMM") + " " + counter2.ToString(CultureInfo.InvariantCulture), _daysFont, new SolidBrush(Color.FromArgb(170, 170, 170)), xStart + 5, yStart + 2); } else { g.DrawString(counter2.ToString(CultureInfo.InvariantCulture), _daysFont, new SolidBrush(Color.FromArgb(170, 170, 170)), xStart + 5, yStart + 2); } counter2++; } } xStart += cellWidth; } xStart = MarginSize; yStart += cellHeight; } yStart = MarginSize + controlsSpacing; if (CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek == DayOfWeek.Monday) { g.DrawString(Strings.Mon, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)monSize.Width) / 2) + 0 * cellWidth, yStart); g.DrawString(Strings.Tue, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)tueSize.Width) / 2) + 1 * cellWidth, yStart); g.DrawString(Strings.Wed, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)wedSize.Width) / 2) + 2 * cellWidth, yStart); g.DrawString(Strings.Thu, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)thuSize.Width) / 2) + 3 * cellWidth, yStart); g.DrawString(Strings.Fri, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)friSize.Width) / 2) + 4 * cellWidth, yStart); g.DrawString(Strings.Sat, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)satSize.Width) / 2) + 5 * cellWidth, yStart); g.DrawString(Strings.Sun, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)sunSize.Width) / 2) + 6 * cellWidth, yStart); } else { g.DrawString(Strings.Sun, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)sunSize.Width) / 2) + 0 * cellWidth, yStart); g.DrawString(Strings.Mon, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)monSize.Width) / 2) + 1 * cellWidth, yStart); g.DrawString(Strings.Tue, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)tueSize.Width) / 2) + 2 * cellWidth, yStart); g.DrawString(Strings.Wed, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)wedSize.Width) / 2) + 3 * cellWidth, yStart); g.DrawString(Strings.Thu, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)thuSize.Width) / 2) + 4 * cellWidth, yStart); g.DrawString(Strings.Fri, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)friSize.Width) / 2) + 5 * cellWidth, yStart); g.DrawString(Strings.Sat, _dayOfWeekFont, Brushes.Black, MarginSize + ((cellWidth - (int)satSize.Width) / 2) + 6 * cellWidth, yStart); } if (_showDateInHeader) { g.DrawString(_calendarDate.ToString("MMMM") + " " + _calendarDate.Year.ToString(CultureInfo.InvariantCulture), _dateHeaderFont, Brushes.Black, ClientSize.Width - MarginSize - dateHeaderSize.Width, MarginSize); } _events.Sort(new EventComparer()); /// /// Draw calendar events /// for (int day = 1; day <= DateTime.DaysInMonth(_calendarDate.Year, _calendarDate.Month); day++) { /// Top coordinate of drawing position, first event on this day starts on top int renderOffsetY = 0; foreach (ICalendarEvent ev in _events) { var dt = new DateTime(_calendarDate.Year, _calendarDate.Month, day, 23, 59, _calendarDate.Second); if ((!ev.Hidden || _showHiddenEvents) && Config.CalendarEvent.Utils.DayMatchesExactly(ev, dt)) { /// Determine event background color int alpha = 255; if (ev.Hidden && _dimDisabledEvents) alpha = 64; Color alphaColor = Color.FromArgb(ev.BackColor); /// Determine event text position int offsetY = renderOffsetY; Region r = g.Clip; Point point = _calendarDays[day]; SizeF sz = g.MeasureString(ev.ToString(), new Font("Calibri", 8, FontStyle.Regular)); sz.Width += 2; sz.Height += 2; int yy = point.Y + 1; int xx = (sz.Width > cellWidth) ? point.X + 1 : ((cellWidth - (int)sz.Width) / 2) + point.X; if (renderOffsetY + sz.Height > cellHeight - 10) { /// Skip rendering, too many events this day continue; } /// Render this event g.Clip = new Region(new Rectangle(point.X + 1, point.Y + offsetY, cellWidth - 1, (int)sz.Height)); g.FillRectangle(new SolidBrush(alphaColor), point.X + 1, point.Y + offsetY, cellWidth - 1, sz.Height); if (ev.Hidden && _showDashedBorderOnDisabledEvents) { var p = new Pen(new SolidBrush(Color.FromArgb(255, 0, 0, 0))) { DashStyle = DashStyle.Dash }; g.DrawRectangle(p, point.X + 1, point.Y + offsetY, cellWidth - 2, sz.Height - 1); } g.DrawString(ev.ToString(), new Font("Calibri", 8, FontStyle.Regular), new SolidBrush(Color.FromArgb(ev.TextColor)), xx, yy + offsetY); g.Clip = r; /// Add calendar event instance to a list => support for mouse-clicks, boloons, etc. _eventInstances.Add(new CalendarEventInstance { Event = ev, EventArea = new Rectangle(point.X + 1, point.Y + offsetY, cellWidth - 1, (int)sz.Height), Date = dt }); /// Move top coordinate of drawing position down renderOffsetY += (int)sz.Height + 1; } } } _rectangles.Clear(); g.Dispose(); e.Graphics.DrawImage(bmp, 0, 0, ClientSize.Width, ClientSize.Height); bmp.Dispose(); } private int NumberOfWeeks(int year, int month) { return NumberOfWeeks(new DateTime(year, month, DateTime.DaysInMonth(year, month))); } private int NumberOfWeeks(DateTime date) { DateTime beginningOfMonth = new DateTime(date.Year, date.Month, 1); while (date.Date.AddDays(1).DayOfWeek != CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek) { date = date.AddDays(1); } return (int)Math.Truncate(date.Subtract(beginningOfMonth).TotalDays / 7f) + 1; } private void CalendarResize(object sender, EventArgs e) { if (_calendarView == CalendarViews.Day) { ResizeScrollPanel(); } Invalidate(); } } }