Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'mcs/class/Managed.Windows.Forms/System.Windows.Forms/MonthCalendar.cs')
-rw-r--r--mcs/class/Managed.Windows.Forms/System.Windows.Forms/MonthCalendar.cs2017
1 files changed, 2017 insertions, 0 deletions
diff --git a/mcs/class/Managed.Windows.Forms/System.Windows.Forms/MonthCalendar.cs b/mcs/class/Managed.Windows.Forms/System.Windows.Forms/MonthCalendar.cs
new file mode 100644
index 00000000000..430622d9375
--- /dev/null
+++ b/mcs/class/Managed.Windows.Forms/System.Windows.Forms/MonthCalendar.cs
@@ -0,0 +1,2017 @@
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Copyright (c) 2004-2006 Novell, Inc.
+//
+// Authors:
+// John BouAntoun jba-mono@optusnet.com.au
+//
+// REMAINING TODO:
+// - get the date_cell_size and title_size to be pixel perfect match of SWF
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace System.Windows.Forms {
+ [DefaultProperty("SelectionRange")]
+ [DefaultEvent("DateChanged")]
+ [Designer ("System.Windows.Forms.Design.MonthCalendarDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ public class MonthCalendar : Control {
+ #region Local variables
+ DateTime [] annually_bolded_dates;
+ Color back_color;
+ DateTime [] bolded_dates;
+ Size calendar_dimensions;
+ Day first_day_of_week;
+ Color fore_color;
+ DateTime max_date;
+ int max_selection_count;
+ DateTime min_date;
+ DateTime [] monthly_bolded_dates;
+ int scroll_change;
+ SelectionRange selection_range;
+ bool show_today;
+ bool show_today_circle;
+ bool show_week_numbers;
+ Color title_back_color;
+ Color title_fore_color;
+ DateTime today_date;
+ bool today_date_set;
+ Color trailing_fore_color;
+ ContextMenu menu;
+ NumericUpDown year_updown;
+ Timer timer;
+
+ // internal variables used
+ internal DateTime current_month; // the month that is being displayed in top left corner of the grid
+ internal DateTimePicker owner; // used if this control is popped up
+ internal int button_x_offset;
+ internal Size button_size;
+ internal Size title_size;
+ internal Size date_cell_size;
+ internal Size calendar_spacing;
+ internal int divider_line_offset;
+ internal DateTime clicked_date;
+ internal bool is_date_clicked;
+ internal bool is_previous_clicked;
+ internal bool is_next_clicked;
+ internal bool is_shift_pressed;
+ internal DateTime first_select_start_date;
+ internal int last_clicked_calendar_index;
+ internal Rectangle last_clicked_calendar_rect;
+ private Point month_title_click_location;
+ // this is used to see which item was actually clicked on in the beginning
+ // so that we know which item to fire on timer
+ // 0: date clicked
+ // 1: previous clicked
+ // 2: next clicked
+ private bool[] click_state;
+
+ // arraylists used to store new dates
+ ArrayList added_bolded_dates;
+ ArrayList removed_bolded_dates;
+ ArrayList added_annually_bolded_dates;
+ ArrayList removed_annually_bolded_dates;
+ ArrayList added_monthly_bolded_dates;
+ ArrayList removed_monthly_bolded_dates;
+
+
+ #endregion // Local variables
+
+ #region Public Constructors
+
+ public MonthCalendar () {
+ // set up the control painting
+ SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick, false);
+
+ // mouse down timer
+ timer = new Timer ();
+ timer.Interval = 500;
+ timer.Enabled = false;
+
+ // initialise default values
+ DateTime now = DateTime.Now.Date;
+ selection_range = new SelectionRange (now, now);
+ today_date = now;
+ current_month = new DateTime (now.Year , now.Month, 1);
+
+ // iniatialise local members
+ annually_bolded_dates = null;
+ back_color = ThemeEngine.Current.ColorWindow;
+ bolded_dates = null;
+ calendar_dimensions = new Size (1,1);
+ first_day_of_week = Day.Default;
+ fore_color = SystemColors.ControlText;
+ max_date = new DateTime (9998, 12, 31);
+ max_selection_count = 7;
+ min_date = new DateTime (1953, 1, 1);
+ monthly_bolded_dates = null;
+ scroll_change = 0;
+ show_today = true;
+ show_today_circle = true;
+ show_week_numbers = false;
+ title_back_color = ThemeEngine.Current.ColorActiveCaption;
+ title_fore_color = ThemeEngine.Current.ColorActiveCaptionText;
+ today_date_set = false;
+ trailing_fore_color = Color.Gray;
+
+ // initialise the arraylest for bolded dates
+ added_bolded_dates = new ArrayList ();
+ removed_bolded_dates = new ArrayList ();
+ added_annually_bolded_dates = new ArrayList ();
+ removed_annually_bolded_dates = new ArrayList ();
+ added_monthly_bolded_dates = new ArrayList ();
+ removed_monthly_bolded_dates = new ArrayList ();
+
+ // intiailise internal variables used
+ button_x_offset = 5;
+ button_size = new Size (22, 17);
+ // default settings based on 8.25 pt San Serif Font
+ // Not sure of algorithm used to establish this
+ date_cell_size = new Size (24, 16); // default size at san-serif 8.25
+ divider_line_offset = 4;
+ calendar_spacing = new Size (4, 5); // horiz and vert spacing between months in a calendar grid
+
+ // set some state info
+ clicked_date = now;
+ is_date_clicked = false;
+ is_previous_clicked = false;
+ is_next_clicked = false;
+ is_shift_pressed = false;
+ click_state = new bool [] {false, false, false};
+ first_select_start_date = now;
+ month_title_click_location = Point.Empty;
+
+ // set up context menu
+ SetUpContextMenu ();
+
+
+ // event handlers
+// LostFocus += new EventHandler (LostFocusHandler);
+ timer.Tick += new EventHandler (TimerHandler);
+ MouseMove += new MouseEventHandler (MouseMoveHandler);
+ MouseDown += new MouseEventHandler (MouseDownHandler);
+ KeyDown += new KeyEventHandler (KeyDownHandler);
+ MouseUp += new MouseEventHandler (MouseUpHandler);
+ KeyUp += new KeyEventHandler (KeyUpHandler);
+
+ // this replaces paint so call the control version
+ base.Paint += new PaintEventHandler (PaintHandler);
+ }
+
+ // called when this control is added to date time picker
+ internal MonthCalendar (DateTimePicker owner) : this () {
+ this.owner = owner;
+ this.is_visible = false;
+ this.Size = this.DefaultSize;
+ }
+
+ #endregion // Public Constructors
+
+ #region Public Instance Properties
+
+ // dates to make bold on calendar annually (recurring)
+ [Localizable (true)]
+ public DateTime[] AnnuallyBoldedDates {
+ set {
+ if (annually_bolded_dates == null || annually_bolded_dates != value) {
+ annually_bolded_dates = value;
+ this.UpdateBoldedDates ();
+ this.Invalidate ();
+ }
+ }
+ get {
+ return annually_bolded_dates;
+ }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override Image BackgroundImage {
+ get {
+ return base.BackgroundImage;
+ }
+ set {
+ base.BackgroundImage = value;
+ }
+ }
+
+
+ // the back color for the main part of the calendar
+ public override Color BackColor {
+ set {
+ if (back_color != value) {
+ back_color = value;
+ this.OnBackColorChanged (EventArgs.Empty);
+ this.Invalidate ();
+ }
+ }
+ get {
+ return back_color;
+ }
+ }
+
+ // specific dates to make bold on calendar (non-recurring)
+ [Localizable (true)]
+ public DateTime[] BoldedDates {
+ set {
+ if (bolded_dates == null || bolded_dates != value) {
+ bolded_dates = value;
+ this.UpdateBoldedDates ();
+ this.Invalidate ();
+ }
+ }
+ get {
+ return bolded_dates;
+ }
+ }
+
+ // the configuration of the monthly grid display - only allowed to display at most,
+ // 1 calendar year at a time, will be trimmed to fit it properly
+ [Localizable (true)]
+ public Size CalendarDimensions {
+ set {
+ if (value.Width < 0 || value.Height < 0) {
+ throw new ArgumentException ();
+ }
+ if (calendar_dimensions != value) {
+ // squeeze the grid into 1 calendar year
+ if (value.Width * value.Height > 12) {
+ // iteratively reduce the largest dimension till our
+ // product is less than 12
+ if (value.Width > 12 && value.Height > 12) {
+ calendar_dimensions = new Size (4, 3);
+ } else if (value.Width > 12) {
+ for (int i = 12; i > 0; i--) {
+ if (i * value.Height <= 12) {
+ calendar_dimensions = new Size (i, value.Height);
+ break;
+ }
+ }
+ } else if (value.Height > 12) {
+ for (int i = 12; i > 0; i--) {
+ if (i * value.Width <= 12) {
+ calendar_dimensions = new Size (value.Width, i);
+ break;
+ }
+ }
+ }
+ } else {
+ calendar_dimensions = value;
+ }
+ this.Invalidate ();
+ }
+ }
+ get {
+ return calendar_dimensions;
+ }
+ }
+
+ // the first day of the week to display
+ [Localizable (true)]
+ [DefaultValue (Day.Default)]
+ public Day FirstDayOfWeek {
+ set {
+ if (first_day_of_week != value) {
+ first_day_of_week = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return first_day_of_week;
+ }
+ }
+
+ // the fore color for the main part of the calendar
+ public override Color ForeColor {
+ set {
+ if (fore_color != value) {
+ fore_color = value;
+ this.OnForeColorChanged (EventArgs.Empty);
+ this.Invalidate ();
+ }
+ }
+ get {
+ return fore_color;
+ }
+ }
+
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public ImeMode ImeMode {
+ get {
+ return ime_mode;
+ }
+
+ set {
+ if (ime_mode != value) {
+ ime_mode = value;
+
+ if (ImeModeChanged != null) {
+ ImeModeChanged(this, EventArgs.Empty);
+ }
+ }
+ }
+ }
+
+ // the maximum date allowed to be selected on this month calendar
+ public DateTime MaxDate {
+ set {
+ if (value < MinDate) {
+ throw new ArgumentException();
+ }
+
+ if (max_date != value) {
+ max_date = value;
+ }
+ }
+ get {
+ return max_date;
+ }
+ }
+
+ // the maximum number of selectable days
+ [DefaultValue (7)]
+ public int MaxSelectionCount {
+ set {
+ if (value < 0) {
+ throw new ArgumentException();
+ }
+
+ // can't set selectioncount less than already selected dates
+ if ((SelectionEnd - SelectionStart).Days > value) {
+ throw new ArgumentException();
+ }
+
+ if (max_selection_count != value) {
+ max_selection_count = value;
+ }
+ }
+ get {
+ return max_selection_count;
+ }
+ }
+
+ // the minimum date allowed to be selected on this month calendar
+ public DateTime MinDate {
+ set {
+ if (value < new DateTime (1953, 1, 1)) {
+ throw new ArgumentException();
+ }
+
+ if (value > MaxDate) {
+ throw new ArgumentException();
+ }
+
+ if (max_date != value) {
+ min_date = value;
+ }
+ }
+ get {
+ return min_date;
+ }
+ }
+
+ // dates to make bold on calendar monthly (recurring)
+ [Localizable (true)]
+ public DateTime[] MonthlyBoldedDates {
+ set {
+ if (monthly_bolded_dates == null || monthly_bolded_dates != value) {
+ monthly_bolded_dates = value;
+ this.UpdateBoldedDates ();
+ this.Invalidate ();
+ }
+ }
+ get {
+ return monthly_bolded_dates;
+ }
+ }
+
+ // the ammount by which to scroll this calendar by
+ [DefaultValue (0)]
+ public int ScrollChange {
+ set {
+ if (value < 0 || value > 20000) {
+ throw new ArgumentException();
+ }
+
+ if (scroll_change != value) {
+ scroll_change = value;
+ }
+ }
+ get {
+ // if zero it to the default -> the total number of months currently visible
+ if (scroll_change == 0) {
+ return CalendarDimensions.Width * CalendarDimensions.Height;
+ }
+ return scroll_change;
+ }
+ }
+
+
+ // the last selected date
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public DateTime SelectionEnd {
+ set {
+ if (value < MinDate || value > MaxDate) {
+ throw new ArgumentException();
+ }
+
+ if (SelectionRange.End != value) {
+ DateTime old_end = SelectionRange.End;
+ // make sure the end obeys the max selection range count
+ if (value < SelectionRange.Start) {
+ SelectionRange.Start = value;
+ }
+ if (value.AddDays((MaxSelectionCount-1)*-1) > SelectionRange.Start) {
+ SelectionRange.Start = value.AddDays((MaxSelectionCount-1)*-1);
+ }
+ SelectionRange.End = value;
+ this.InvalidateDateRange (new SelectionRange (old_end, SelectionRange.End));
+ this.OnDateChanged (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ }
+ }
+ get {
+ return SelectionRange.End;
+ }
+ }
+
+ // the range of selected dates
+ public SelectionRange SelectionRange {
+ set {
+ if (selection_range != value) {
+ SelectionRange old_range = selection_range;
+
+ // make sure the end obeys the max selection range count
+ if (value.End.AddDays((MaxSelectionCount-1)*-1) > value.Start) {
+ selection_range = new SelectionRange (value.End.AddDays((MaxSelectionCount-1)*-1), value.End);
+ } else {
+ selection_range = value;
+ }
+ SelectionRange visible_range = this.GetDisplayRange(true);
+ if(visible_range.Start > selection_range.End) {
+ this.current_month = new DateTime (selection_range.Start.Year, selection_range.Start.Month, 1);
+ this.Invalidate ();
+ } else if (visible_range.End < selection_range.Start) {
+ int year_diff = selection_range.End.Year - visible_range.End.Year;
+ int month_diff = selection_range.End.Month - visible_range.End.Month;
+ this.current_month = current_month.AddMonths(year_diff * 12 + month_diff);
+ this.Invalidate ();
+ }
+ // invalidate the selected range changes
+ DateTime diff_start = old_range.Start;
+ DateTime diff_end = old_range.End;
+ // now decide which region is greated
+ if (old_range.Start > SelectionRange.Start) {
+ diff_start = SelectionRange.Start;
+ } else if (old_range.Start == SelectionRange.Start) {
+ if (old_range.End < SelectionRange.End) {
+ diff_start = old_range.End;
+ } else {
+ diff_start = SelectionRange.End;
+ }
+ }
+ if (old_range.End < SelectionRange.End) {
+ diff_end = SelectionRange.End;
+ } else if (old_range.End == SelectionRange.End) {
+ if (old_range.Start < SelectionRange.Start) {
+ diff_end = SelectionRange.Start;
+ } else {
+ diff_end = old_range.Start;
+ }
+ }
+
+ // invalidate the region required
+ this.InvalidateDateRange (new SelectionRange (diff_start, diff_end));
+ // raise date changed event
+ this.OnDateChanged (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ }
+ }
+ get {
+ return selection_range;
+ }
+ }
+
+ // the first selected date
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public DateTime SelectionStart {
+ set {
+ if (value < MinDate || value > MaxDate) {
+ throw new ArgumentException();
+ }
+
+ if (SelectionRange.Start != value) {
+ DateTime old_start = SelectionRange.Start;
+ // make sure the end obeys the max selection range count
+ if (value > SelectionRange.End) {
+ SelectionRange.End = value;
+ } else if (value.AddDays(MaxSelectionCount-1) < SelectionRange.End) {
+ SelectionRange.End = value.AddDays(MaxSelectionCount-1);
+ }
+ SelectionRange.Start = value;
+ DateTime new_month = new DateTime(value.Year, value.Month, 1);
+ if (current_month != new_month) {
+ current_month = new_month;
+ this.Invalidate ();
+ } else {
+ this.InvalidateDateRange (new SelectionRange (old_start, SelectionRange.Start));
+ }
+ this.OnDateChanged (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ }
+ }
+ get {
+ return selection_range.Start;
+ }
+ }
+
+ // whether or not to show todays date
+ [DefaultValue (true)]
+ public bool ShowToday {
+ set {
+ if (show_today != value) {
+ show_today = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return show_today;
+ }
+ }
+
+ // whether or not to show a circle around todays date
+ [DefaultValue (true)]
+ public bool ShowTodayCircle {
+ set {
+ if (show_today_circle != value) {
+ show_today_circle = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return show_today_circle;
+ }
+ }
+
+ // whether or not to show numbers beside each row of weeks
+ [Localizable (true)]
+ [DefaultValue (false)]
+ public bool ShowWeekNumbers {
+ set {
+ if (show_week_numbers != value) {
+ show_week_numbers = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return show_week_numbers;
+ }
+ }
+
+ // the rectangle size required to render one month based on current font
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public Size SingleMonthSize {
+ get {
+ if (this.Font == null) {
+ throw new InvalidOperationException();
+ }
+
+ // multiplier is sucked out from the font size
+ int multiplier = this.Font.Height;
+
+ // establis how many columns and rows we have
+ int column_count = (ShowWeekNumbers) ? 8 : 7;
+ int row_count = 7; // not including the today date
+
+ // set the date_cell_size and the title_size
+ date_cell_size = new Size ((int) Math.Ceiling (2.5 * multiplier), (int) Math.Ceiling (1.5 * multiplier));
+ title_size = new Size ((date_cell_size.Width * column_count), 3 * multiplier);
+
+ return new Size (column_count * date_cell_size.Width, row_count * date_cell_size.Height + title_size.Height);
+ }
+ }
+
+ [Bindable(false)]
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override string Text {
+ get {
+ return base.Text;
+ }
+ set {
+ base.Text = value;
+ }
+ }
+
+ // the back color for the title of the calendar and the
+ // forecolor for the day of the week text
+ public Color TitleBackColor {
+ set {
+ if (title_back_color != value) {
+ title_back_color = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return title_back_color;
+ }
+ }
+
+ // the fore color for the title of the calendar
+ public Color TitleForeColor {
+ set {
+ if (title_fore_color != value) {
+ title_fore_color = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return title_fore_color;
+ }
+ }
+
+ // the date this calendar is using to refer to today's date
+ public DateTime TodayDate {
+ set {
+ today_date_set = true;
+ if (today_date != value) {
+ today_date = value;
+ this.Invalidate ();
+ }
+ }
+ get {
+ return today_date;
+ }
+ }
+
+ // tells if user specifically set today_date for this control
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public bool TodayDateSet {
+ get {
+ return today_date_set;
+ }
+ }
+
+ // the color used for trailing dates in the calendar
+ public Color TrailingForeColor {
+ set {
+ if (trailing_fore_color != value) {
+ trailing_fore_color = value;
+ SelectionRange bounds = this.GetDisplayRange (false);
+ SelectionRange visible_bounds = this.GetDisplayRange (true);
+ this.InvalidateDateRange (new SelectionRange (bounds.Start, visible_bounds.Start));
+ this.InvalidateDateRange (new SelectionRange (bounds.End, visible_bounds.End));
+ }
+ }
+ get {
+ return trailing_fore_color;
+ }
+ }
+
+ #endregion // Public Instance Properties
+
+ #region Protected Instance Properties
+
+ // overloaded to allow controll to be windowed for drop down
+ protected override CreateParams CreateParams {
+ get {
+ if (this.owner == null) {
+ return base.CreateParams;
+ } else {
+ CreateParams cp = base.CreateParams;
+ cp.Style ^= (int) WindowStyles.WS_CHILD;
+ cp.Style |= (int) WindowStyles.WS_POPUP;
+ cp.ExStyle |= (int)(WindowStyles.WS_EX_TOOLWINDOW | WindowStyles.WS_EX_TOPMOST);
+
+ return cp;
+ }
+ }
+ }
+
+ // not sure what to put in here - just doing a base() call - jba
+ protected override ImeMode DefaultImeMode {
+ get {
+ return base.DefaultImeMode;
+ }
+ }
+
+ protected override Size DefaultSize {
+ get {
+ Size single_month = SingleMonthSize;
+ // get the width
+ int width = calendar_dimensions.Width * single_month.Width;
+ if (calendar_dimensions.Width > 1) {
+ width += (calendar_dimensions.Width - 1) * calendar_spacing.Width;
+ }
+
+ // get the height
+ int height = calendar_dimensions.Height * single_month.Height;
+ if (this.ShowToday) {
+ height += date_cell_size.Height + 2; // add the height of the "Today: " ...
+ }
+ if (calendar_dimensions.Height > 1) {
+ height += (calendar_dimensions.Height - 1) * calendar_spacing.Height;
+ }
+
+ // add the 1 pixel boundary
+ if (width > 0) {
+ width += 2;
+ }
+ if (height > 0) {
+ height +=2;
+ }
+
+ return new Size (width, height);
+ }
+ }
+
+ #endregion // Protected Instance Properties
+
+ #region Public Instance Methods
+
+ // add a date to the anually bolded date arraylist
+ public void AddAnnuallyBoldedDate (DateTime date) {
+ added_annually_bolded_dates.Add (date.Date);
+ }
+
+ // add a date to the normal bolded date arraylist
+ public void AddBoldedDate (DateTime date) {
+ added_bolded_dates.Add (date.Date);
+ }
+
+ // add a date to the anually monthly date arraylist
+ public void AddMonthlyBoldedDate (DateTime date) {
+ added_monthly_bolded_dates.Add (date.Date);
+ }
+
+ // if visible = true, return only the dates of full months, else return all dates visible
+ public SelectionRange GetDisplayRange (bool visible) {
+ DateTime start;
+ DateTime end;
+ start = new DateTime (current_month.Year, current_month.Month, 1);
+ end = start.AddMonths (calendar_dimensions.Width * calendar_dimensions.Height);
+ end = end.AddDays(-1);
+
+ // process all visible dates if needed (including the grayed out dates
+ if (!visible) {
+ start = GetFirstDateInMonthGrid (start);
+ end = GetLastDateInMonthGrid (end);
+ }
+
+ return new SelectionRange (start, end);
+ }
+
+ // HitTest overload that recieve's x and y co-ordinates as separate ints
+ public HitTestInfo HitTest (int x, int y) {
+ return HitTest (new Point (x, y));
+ }
+
+ // returns a HitTestInfo for MonthCalendar element's under the specified point
+ public HitTestInfo HitTest (Point point) {
+ return HitTest (point, out last_clicked_calendar_index, out last_clicked_calendar_rect);
+ }
+
+ // clears all the annually bolded dates
+ public void RemoveAllAnnuallyBoldedDates () {
+ annually_bolded_dates = null;
+ added_annually_bolded_dates.Clear ();
+ removed_annually_bolded_dates.Clear ();
+ }
+
+ // clears all the normal bolded dates
+ public void RemoveAllBoldedDates () {
+ bolded_dates = null;
+ added_bolded_dates.Clear ();
+ removed_bolded_dates.Clear ();
+ }
+
+ // clears all the monthly bolded dates
+ public void RemoveAllMonthlyBoldedDates () {
+ monthly_bolded_dates = null;
+ added_monthly_bolded_dates.Clear ();
+ removed_monthly_bolded_dates.Clear ();
+ }
+
+ // clears the specified annually bolded date (only compares day and month)
+ // only removes the first instance of the match
+ public void RemoveAnnuallyBoldedDate (DateTime date) {
+ if (!removed_annually_bolded_dates.Contains (date.Date)) {
+ removed_annually_bolded_dates.Add (date.Date);
+ }
+ }
+
+ // clears all the normal bolded date
+ // only removes the first instance of the match
+ public void RemoveBoldedDate (DateTime date) {
+ if (!removed_bolded_dates.Contains (date.Date)) {
+ removed_bolded_dates.Add (date.Date);
+ }
+ }
+
+ // clears the specified monthly bolded date (only compares day and month)
+ // only removes the first instance of the match
+ public void RemoveMonthlyBoldedDate (DateTime date) {
+ if (!removed_monthly_bolded_dates.Contains (date.Date)) {
+ removed_monthly_bolded_dates.Add (date.Date);
+ }
+ }
+
+ // sets the calendar_dimensions. If product is > 12, the larger dimension is reduced to make product < 12
+ public void SetCalendarDimensions(int x, int y) {
+ this.CalendarDimensions = new Size(x, y);
+ }
+
+ // sets the currently selected date as date
+ public void SetDate (DateTime date) {
+ this.SetSelectionRange (date.Date, date.Date);
+ }
+
+ // utility method set the SelectionRange property using individual dates
+ public void SetSelectionRange (DateTime date1, DateTime date2) {
+ this.SelectionRange = new SelectionRange(date1, date2);
+ }
+
+ public override string ToString () {
+ return this.GetType().Name + ", " + this.SelectionRange.ToString ();
+ }
+
+ // usually called after an AddBoldedDate method is called
+ // formats monthly and daily bolded dates according to the current calendar year
+ public void UpdateBoldedDates () {
+ UpdateDateArray (ref bolded_dates, added_bolded_dates, removed_bolded_dates);
+ UpdateDateArray (ref monthly_bolded_dates, added_monthly_bolded_dates, removed_monthly_bolded_dates);
+ UpdateDateArray (ref annually_bolded_dates, added_annually_bolded_dates, removed_annually_bolded_dates);
+ }
+
+ #endregion // Public Instance Methods
+
+ #region Protected Instance Methods
+
+ // not sure why this needs to be overriden
+ protected override void CreateHandle () {
+ base.CreateHandle ();
+ }
+
+ private void CreateYearUpDown ()
+ {
+ year_updown = new NumericUpDown ();
+ year_updown.Font = this.Font;
+ year_updown.Minimum = MinDate.Year;
+ year_updown.Maximum = MaxDate.Year;
+ year_updown.ReadOnly = true;
+ year_updown.Visible = false;
+ this.Controls.AddImplicit (year_updown);
+ year_updown.ValueChanged += new EventHandler(UpDownYearChangedHandler);
+ }
+
+ // not sure why this needs to be overriden
+ protected override void Dispose (bool disposing) {
+ base.Dispose (disposing);
+ }
+
+ // not sure why this needs to be overriden
+ protected override bool IsInputKey (Keys keyData) {
+ return base.IsInputKey (keyData);
+ }
+
+ // not sure why this needs to be overriden
+ protected override void OnBackColorChanged (EventArgs e) {
+ base.OnBackColorChanged (e);
+ this.Invalidate ();
+ }
+
+ // raises the date changed event
+ protected virtual void OnDateChanged (DateRangeEventArgs drevent) {
+ if (this.DateChanged != null) {
+ this.DateChanged (this, drevent);
+ }
+ }
+
+ // raises the DateSelected event
+ protected virtual void OnDateSelected (DateRangeEventArgs drevent) {
+ if (this.DateSelected != null) {
+ this.DateSelected (this, drevent);
+ }
+ }
+
+ protected override void OnFontChanged (EventArgs e) {
+ base.OnFontChanged (e);
+ }
+
+ protected override void OnForeColorChanged (EventArgs e) {
+ base.OnForeColorChanged (e);
+ }
+
+ protected override void OnHandleCreated (EventArgs e) {
+ base.OnHandleCreated (e);
+ CreateYearUpDown ();
+ }
+
+ // i think this is overriden to not allow the control to be changed to an arbitrary size
+ protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified) {
+ if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height ||
+ (specified & BoundsSpecified.Width) == BoundsSpecified.Width ||
+ (specified & BoundsSpecified.Size) == BoundsSpecified.Size) {
+ // only allow sizes = default size to be set
+ Size min_size = DefaultSize;
+ Size max_size = new Size (
+ DefaultSize.Width + SingleMonthSize.Width + calendar_spacing.Width,
+ DefaultSize.Height + SingleMonthSize.Height + calendar_spacing.Height);
+ int x_mid_point = (max_size.Width + min_size.Width)/2;
+ int y_mid_point = (max_size.Height + min_size.Height)/2;
+ if (width < x_mid_point) {
+ width = min_size.Width;
+ } else {
+ width = max_size.Width;
+ }
+ if (height < y_mid_point) {
+ height = min_size.Height;
+ } else {
+ height = max_size.Height;
+ }
+ base.SetBoundsCore (x, y, width, height, specified);
+ } else {
+ base.SetBoundsCore (x, y, width, height, specified);
+ }
+ }
+
+ protected override void WndProc (ref Message m) {
+ base.WndProc (ref m);
+ }
+
+ #endregion // Protected Instance Methods
+
+ #region public events
+
+ // fired when the date is changed (either explicitely or implicitely)
+ // when navigating the month selector
+ public event DateRangeEventHandler DateChanged;
+
+ // fired when the user explicitely clicks on date to select it
+ public event DateRangeEventHandler DateSelected;
+
+ [Browsable(false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public event EventHandler BackgroundImageChanged;
+
+ // this event is overridden to supress it from being fired
+ [Browsable(false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public event EventHandler Click;
+
+ // this event is overridden to supress it from being fired
+ [Browsable(false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public event EventHandler DoubleClick;
+
+ [Browsable(false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public event EventHandler ImeModeChanged;
+
+ [Browsable(false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint;
+
+ [Browsable(false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public event EventHandler TextChanged;
+ #endregion // public events
+
+ #region internal properties
+
+ internal DateTime CurrentMonth {
+ set {
+ // only interested in if the month (not actual date) has changed
+ if (value.Month != current_month.Month ||
+ value.Year != current_month.Year) {
+ this.SelectionRange = new SelectionRange(
+ this.SelectionStart.Add(value.Subtract(current_month)),
+ this.SelectionEnd.Add(value.Subtract(current_month)));
+ current_month = value;
+ UpdateBoldedDates();
+ this.Invalidate();
+ }
+ }
+ get {
+ return current_month;
+ }
+ }
+
+ #endregion // internal properties
+
+ #region internal/private methods
+ internal HitTestInfo HitTest (
+ Point point,
+ out int calendar_index,
+ out Rectangle calendar_rect) {
+ // start by initialising the ref parameters
+ calendar_index = -1;
+ calendar_rect = Rectangle.Empty;
+
+ // before doing all the hard work, see if the today's date wasn't clicked
+ Rectangle today_rect = new Rectangle (
+ ClientRectangle.X,
+ ClientRectangle.Bottom - date_cell_size.Height,
+ 7 * date_cell_size.Width,
+ date_cell_size.Height);
+ if (today_rect.Contains (point) && this.ShowToday) {
+ return new HitTestInfo(HitArea.TodayLink, point, DateTime.Now);
+ }
+
+ Size month_size = SingleMonthSize;
+ // define calendar rect's that this thing can land in
+ Rectangle[] calendars = new Rectangle [CalendarDimensions.Width * CalendarDimensions.Height];
+ for (int i=0; i < CalendarDimensions.Width * CalendarDimensions.Height; i ++) {
+ if (i == 0) {
+ calendars[i] = new Rectangle (
+ new Point (ClientRectangle.X + 1, ClientRectangle.Y + 1),
+ month_size);
+ } else {
+ // calendar on the next row
+ if (i % CalendarDimensions.Width == 0) {
+ calendars[i] = new Rectangle (
+ new Point (calendars[i-CalendarDimensions.Width].X, calendars[i-CalendarDimensions.Width].Bottom + calendar_spacing.Height),
+ month_size);
+ } else {
+ // calendar on the next column
+ calendars[i] = new Rectangle (
+ new Point (calendars[i-1].Right + calendar_spacing.Width, calendars[i-1].Y),
+ month_size);
+ }
+ }
+ }
+
+ // through each trying to find a match
+ for (int i = 0; i < calendars.Length ; i++) {
+ if (calendars[i].Contains (point)) {
+ // check the title section
+ Rectangle title_rect = new Rectangle (
+ calendars[i].Location,
+ title_size);
+ if (title_rect.Contains (point) ) {
+ // make sure it's not a previous button
+ if (i == 0) {
+ Rectangle button_rect = new Rectangle(
+ new Point (calendars[i].X + button_x_offset, (title_size.Height - button_size.Height)/2),
+ button_size);
+ if (button_rect.Contains (point)) {
+ return new HitTestInfo(HitArea.PrevMonthButton, point, DateTime.Now);
+ }
+ }
+ // make sure it's not the next button
+ if (i % CalendarDimensions.Height == 0 && i % CalendarDimensions.Width == calendar_dimensions.Width - 1) {
+ Rectangle button_rect = new Rectangle(
+ new Point (calendars[i].Right - button_x_offset - button_size.Width, (title_size.Height - button_size.Height)/2),
+ button_size);
+ if (button_rect.Contains (point)) {
+ return new HitTestInfo(HitArea.NextMonthButton, point, DateTime.Now);
+ }
+ }
+
+ // indicate which calendar and month it was
+ calendar_index = i;
+ calendar_rect = calendars[i];
+
+ // make sure it's not the month or the year of the calendar
+ if (GetMonthNameRectangle (title_rect, i).Contains (point)) {
+ return new HitTestInfo(HitArea.TitleMonth, point, DateTime.Now);
+ }
+ if (GetYearNameRectangle (title_rect, i).Contains (point)) {
+ return new HitTestInfo(HitArea.TitleYear, point, DateTime.Now);
+ }
+
+ // return the hit test in the title background
+ return new HitTestInfo(HitArea.TitleBackground, point, DateTime.Now);
+ }
+
+ Point date_grid_location = new Point (calendars[i].X, title_rect.Bottom);
+
+ // see if it's in the Week numbers
+ if (ShowWeekNumbers) {
+ Rectangle weeks_rect = new Rectangle (
+ date_grid_location,
+ new Size (date_cell_size.Width,Math.Max (calendars[i].Height - title_rect.Height, 0)));
+ if (weeks_rect.Contains (point)) {
+ return new HitTestInfo(HitArea.WeekNumbers, point, DateTime.Now);
+ }
+
+ // move the location of the grid over
+ date_grid_location.X += date_cell_size.Width;
+ }
+
+ // see if it's in the week names
+ Rectangle day_rect = new Rectangle (
+ date_grid_location,
+ new Size (Math.Max (calendars[i].Right - date_grid_location.X, 0), date_cell_size.Height));
+ if (day_rect.Contains (point)) {
+ return new HitTestInfo(HitArea.DayOfWeek, point, DateTime.Now);
+ }
+
+ // finally see if it was a date that was clicked
+ Rectangle date_grid = new Rectangle (
+ new Point (day_rect.X, day_rect.Bottom),
+ new Size (day_rect.Width, Math.Max(calendars[i].Bottom - day_rect.Bottom, 0)));
+ if (date_grid.Contains (point)) {
+ // okay so it's inside the grid, get the offset
+ Point offset = new Point (point.X - date_grid.X, point.Y - date_grid.Y);
+ int row = offset.Y / date_cell_size.Height;
+ int col = offset.X / date_cell_size.Width;
+ // establish our first day of the month
+ DateTime calendar_month = this.CurrentMonth.AddMonths(i);
+ DateTime first_day = GetFirstDateInMonthGrid (calendar_month);
+ DateTime time = first_day.AddDays ((row * 7) + col);
+ // establish which date was clicked
+ if (time.Year != calendar_month.Year || time.Month != calendar_month.Month) {
+ if (time < calendar_month && i == 0) {
+ return new HitTestInfo(HitArea.PrevMonthDate, point, time);
+ } else if (time > calendar_month && i == CalendarDimensions.Width*CalendarDimensions.Height - 1) {
+ return new HitTestInfo(HitArea.NextMonthDate, point, time);
+ }
+ return new HitTestInfo(HitArea.Nowhere, point, time);
+ }
+ return new HitTestInfo(HitArea.Date, point, time);
+ }
+ }
+ }
+
+ return new HitTestInfo ();
+ }
+
+ // returns the date of the first cell of the specified month grid
+ internal DateTime GetFirstDateInMonthGrid (DateTime month) {
+ // convert the first_day_of_week into a DayOfWeekEnum
+ DayOfWeek first_day = GetDayOfWeek (first_day_of_week);
+ // find the first day of the month
+ DateTime first_date_of_month = new DateTime (month.Year, month.Month, 1);
+ DayOfWeek first_day_of_month = first_date_of_month.DayOfWeek;
+ // adjust for the starting day of the week
+ int offset = first_day_of_month - first_day;
+ if (offset < 0) {
+ offset += 7;
+ }
+ return first_date_of_month.AddDays (-1*offset);
+ }
+
+ // returns the date of the last cell of the specified month grid
+ internal DateTime GetLastDateInMonthGrid (DateTime month)
+ {
+ DateTime start = GetFirstDateInMonthGrid(month);
+ return start.AddDays ((7 * 6)-1);
+ }
+
+ internal bool IsBoldedDate (DateTime date) {
+ // check bolded dates
+ if (bolded_dates != null && bolded_dates.Length > 0) {
+ foreach (DateTime bolded_date in bolded_dates) {
+ if (bolded_date.Date == date.Date) {
+ return true;
+ }
+ }
+ }
+ // check monthly dates
+ if (monthly_bolded_dates != null && monthly_bolded_dates.Length > 0) {
+ foreach (DateTime bolded_date in monthly_bolded_dates) {
+ if (bolded_date.Day == date.Day) {
+ return true;
+ }
+ }
+ }
+ // check yearly dates
+ if (annually_bolded_dates != null && annually_bolded_dates.Length > 0) {
+ foreach (DateTime bolded_date in annually_bolded_dates) {
+ if (bolded_date.Month == date.Month && bolded_date.Day == date.Day) {
+ return true;
+ }
+ }
+ }
+
+ return false; // no match
+ }
+
+ // updates the specified bolded dates array with ones to add and ones to remove
+ private void UpdateDateArray (ref DateTime [] dates, ArrayList to_add, ArrayList to_remove) {
+ ArrayList list = new ArrayList ();
+
+ // update normal bolded dates
+ if (dates != null) {
+ foreach (DateTime date in dates) {
+ list.Add (date.Date);
+ }
+ }
+
+ // add new ones
+ foreach (DateTime date in to_add) {
+ if (!list.Contains (date.Date)) {
+ list.Add (date.Date);
+ }
+ }
+ to_add.Clear ();
+
+ // remove ones to remove
+ foreach (DateTime date in to_remove) {
+ if (list.Contains (date.Date)) {
+ list.Remove (date.Date);
+ }
+ }
+ to_remove.Clear ();
+ // set up the array now
+ if (list.Count > 0) {
+ dates = (DateTime []) list.ToArray (typeof (DateTime));
+ Array.Sort (dates);
+ list.Clear ();
+ } else {
+ dates = null;
+ }
+ }
+
+ // initialise the context menu
+ private void SetUpContextMenu () {
+ menu = new ContextMenu ();
+ for (int i=0; i < 12; i++) {
+ MenuItem menu_item = new MenuItem ( new DateTime (2000, i+1, 1).ToString ("MMMM"));
+ menu_item.Click += new EventHandler (MenuItemClickHandler);
+ menu.MenuItems.Add (menu_item);
+ }
+ }
+
+ // initialises text value and show's year up down in correct position
+ private void PrepareYearUpDown (Point p) {
+ Rectangle old_location = year_updown.Bounds;
+
+ // set position
+ Rectangle title_rect = new Rectangle(
+ last_clicked_calendar_rect.Location,
+ title_size);
+
+ year_updown.Bounds = GetYearNameRectangle(
+ title_rect,
+ last_clicked_calendar_index);
+ year_updown.Top -= 4;
+ year_updown.Width += (int) (this.Font.Size * 4);
+ // set year - only do this if this isn't being called because of a year up down click
+ if(year_updown.Bounds != old_location) {
+ year_updown.Value = current_month.AddMonths(last_clicked_calendar_index).Year;
+ }
+
+ if(!year_updown.Visible) {
+ year_updown.Visible = true;
+ }
+ }
+
+ // returns the first date of the month
+ private DateTime GetFirstDateInMonth (DateTime date) {
+ return new DateTime (date.Year, date.Month, 1);
+ }
+
+ // returns the last date of the month
+ private DateTime GetLastDateInMonth (DateTime date) {
+ return new DateTime (date.Year, date.Month, 1).AddMonths(1).AddDays(-1);
+ }
+
+ // called in response to users seletion with shift key
+ private void AddTimeToSelection (int delta, bool isDays)
+ {
+ DateTime cursor_point;
+ DateTime end_point;
+ // okay we add the period to the date that is not the same as the
+ // start date when shift was first clicked.
+ if (SelectionStart != first_select_start_date) {
+ cursor_point = SelectionStart;
+ } else {
+ cursor_point = SelectionEnd;
+ }
+ // add the days
+ if (isDays) {
+ end_point = cursor_point.AddDays (delta);
+ } else {
+ // delta must be months
+ end_point = cursor_point.AddMonths (delta);
+ }
+ // set the new selection range
+ SelectionRange range = new SelectionRange (first_select_start_date, end_point);
+ if (range.Start.AddDays (MaxSelectionCount-1) < range.End) {
+ // okay the date is beyond what is allowed, lets set the maximum we can
+ if (range.Start != first_select_start_date) {
+ range.Start = range.End.AddDays ((MaxSelectionCount-1)*-1);
+ } else {
+ range.End = range.Start.AddDays (MaxSelectionCount-1);
+ }
+ }
+ this.SelectionRange = range;
+ }
+
+ // attempts to add the date to the selection without throwing exception
+ private void SelectDate (DateTime date) {
+ // try and add the new date to the selction range
+ if (is_shift_pressed || (click_state [0])) {
+ SelectionRange range = new SelectionRange (first_select_start_date, date);
+ if (range.Start.AddDays (MaxSelectionCount-1) < range.End) {
+ // okay the date is beyond what is allowed, lets set the maximum we can
+ if (range.Start != first_select_start_date) {
+ range.Start = range.End.AddDays ((MaxSelectionCount-1)*-1);
+ } else {
+ range.End = range.Start.AddDays (MaxSelectionCount-1);
+ }
+ }
+ SelectionRange = range;
+ } else {
+ SelectionRange = new SelectionRange (date, date);
+ first_select_start_date = date;
+ }
+ }
+
+ // gets the week of the year
+ internal int GetWeekOfYear (DateTime date) {
+ // convert the first_day_of_week into a DayOfWeekEnum
+ DayOfWeek first_day = GetDayOfWeek (first_day_of_week);
+ // find the first day of the year
+ DayOfWeek first_day_of_year = new DateTime (date.Year, 1, 1).DayOfWeek;
+ // adjust for the starting day of the week
+ int offset = first_day_of_year - first_day;
+ int week = ((date.DayOfYear + offset) / 7) + 1;
+ return week;
+ }
+
+ // convert a Day enum into a DayOfWeek enum
+ internal DayOfWeek GetDayOfWeek (Day day) {
+ if (day == Day.Default) {
+ return DayOfWeek.Sunday;
+ } else {
+ return (DayOfWeek) DayOfWeek.Parse (typeof (DayOfWeek), day.ToString ());
+ }
+ }
+
+ // returns the rectangle for themonth name
+ internal Rectangle GetMonthNameRectangle (Rectangle title_rect, int calendar_index) {
+ Graphics g = this.DeviceContext;
+ DateTime this_month = this.current_month.AddMonths (calendar_index);
+ Size title_text_size = g.MeasureString (this_month.ToString ("MMMM yyyy"), this.Font).ToSize ();
+ Size month_size = g.MeasureString (this_month.ToString ("MMMM"), this.Font).ToSize ();
+ // return only the month name part of that
+ return new Rectangle (
+ new Point (
+ title_rect.X + ((title_rect.Width - title_text_size.Width)/2),
+ title_rect.Y + ((title_rect.Height - title_text_size.Height)/2)),
+ month_size);
+ }
+
+ // returns the rectangle for the year in the title
+ internal Rectangle GetYearNameRectangle (Rectangle title_rect, int calendar_index) {
+ Graphics g = this.DeviceContext;
+ DateTime this_month = this.current_month.AddMonths (calendar_index);
+ Size title_text_size = g.MeasureString (this_month.ToString ("MMMM yyyy"), this.Font).ToSize ();
+ Size year_size = g.MeasureString (this_month.ToString ("yyyy"), this.Font).ToSize ();
+ // find out how much space the title took
+ Rectangle text_rect = new Rectangle (
+ new Point (
+ title_rect.X + ((title_rect.Width - title_text_size.Width)/2),
+ title_rect.Y + ((title_rect.Height - title_text_size.Height)/2)),
+ title_text_size);
+ // return only the rect of the year
+ return new Rectangle (
+ new Point (
+ text_rect.Right - year_size.Width,
+ text_rect.Y),
+ year_size);
+ }
+
+ // determine if date is allowed to be drawn in month
+ internal bool IsValidWeekToDraw (DateTime month, DateTime date, int row, int col) {
+ DateTime tocheck = month.AddMonths (-1);
+ if ((month.Year == date.Year && month.Month == date.Month) ||
+ (tocheck.Year == date.Year && tocheck.Month == date.Month)) {
+ return true;
+ }
+
+ // check the railing dates (days in the month after the last month in grid)
+ if (row == CalendarDimensions.Height - 1 && col == CalendarDimensions.Width - 1) {
+ tocheck = month.AddMonths (1);
+ return (tocheck.Year == date.Year && tocheck.Month == date.Month) ;
+ }
+
+ return false;
+ }
+
+ // set one item clicked and all others off
+ private void SetItemClick(HitTestInfo hti)
+ {
+ switch(hti.HitArea) {
+ case HitArea.NextMonthButton:
+ this.is_previous_clicked = false;
+ this.is_next_clicked = true;
+ this.is_date_clicked = false;
+ break;
+ case HitArea.PrevMonthButton:
+ this.is_previous_clicked = true;
+ this.is_next_clicked = false;
+ this.is_date_clicked = false;
+ break;
+ case HitArea.PrevMonthDate:
+ case HitArea.NextMonthDate:
+ case HitArea.Date:
+ this.clicked_date = hti.Time;
+ this.is_previous_clicked = false;
+ this.is_next_clicked = false;
+ this.is_date_clicked = true;
+ break;
+ default :
+ this.is_previous_clicked = false;
+ this.is_next_clicked = false;
+ this.is_date_clicked = false;
+ break;
+ }
+ }
+
+ // called when the year is changed
+ private void UpDownYearChangedHandler (object sender, EventArgs e) {
+ int initial_year_value = this.CurrentMonth.AddMonths(last_clicked_calendar_index).Year;
+ int diff = (int) year_updown.Value - initial_year_value;
+ this.CurrentMonth = this.CurrentMonth.AddYears(diff);
+ }
+
+ // called when context menu is clicked
+ private void MenuItemClickHandler (object sender, EventArgs e) {
+ MenuItem item = sender as MenuItem;
+ if (item != null && month_title_click_location != Point.Empty) {
+ // establish which month we want to move to
+ if (item.Parent == null) {
+ return;
+ }
+ int new_month = item.Parent.MenuItems.IndexOf (item) + 1;
+ if (new_month == 0) {
+ return;
+ }
+ // okay let's establish which calendar was hit
+ Size month_size = this.SingleMonthSize;
+ for (int i=0; i < CalendarDimensions.Height; i++) {
+ for (int j=0; j < CalendarDimensions.Width; j++) {
+ int month_index = (i * CalendarDimensions.Width) + j;
+ Rectangle month_rect = new Rectangle ( new Point (0, 0), month_size);
+ if (j == 0) {
+ month_rect.X = this.ClientRectangle.X + 1;
+ } else {
+ month_rect.X = this.ClientRectangle.X + 1 + ((j)*(month_size.Width+calendar_spacing.Width));
+ }
+ if (i == 0) {
+ month_rect.Y = this.ClientRectangle.Y + 1;
+ } else {
+ month_rect.Y = this.ClientRectangle.Y + 1 + ((i)*(month_size.Height+calendar_spacing.Height));
+ }
+ // see if the point is inside
+ if (month_rect.Contains (month_title_click_location)) {
+ DateTime clicked_month = CurrentMonth.AddMonths (month_index);
+ // get the month that we want to move to
+ int month_offset = new_month - clicked_month.Month;
+
+ // move forward however more months we need to
+ this.CurrentMonth = this.CurrentMonth.AddMonths (month_offset);
+ break;
+ }
+ }
+ }
+
+ // clear the point
+ month_title_click_location = Point.Empty;
+ }
+ }
+
+ // raised on the timer, for mouse hold clicks
+ private void TimerHandler (object sender, EventArgs e) {
+ // now find out which area was click
+ if (this.Capture) {
+ HitTestInfo hti = this.HitTest (this.PointToClient (MousePosition));
+ // see if it was clicked on the prev or next mouse
+ if (click_state [1] || click_state [2]) {
+ // invalidate the area where the mouse was last held
+ DoMouseUp ();
+ // register the click
+ if (hti.HitArea == HitArea.PrevMonthButton ||
+ hti.HitArea == HitArea.NextMonthButton) {
+ DoButtonMouseDown (hti);
+ click_state [1] = (hti.HitArea == HitArea.PrevMonthButton);
+ click_state [2] = !click_state [1];
+ }
+ if (timer.Interval != 300) {
+ timer.Interval = 300;
+ }
+ }
+ } else {
+ timer.Enabled = false;
+ }
+ }
+
+ // selects one of the buttons
+ private void DoButtonMouseDown (HitTestInfo hti) {
+ // show the click then move on
+ SetItemClick(hti);
+ if (hti.HitArea == HitArea.PrevMonthButton) {
+ // invalidate the prev monthbutton
+ this.Invalidate(
+ new Rectangle (
+ this.ClientRectangle.X + 1 + button_x_offset,
+ this.ClientRectangle.Y + 1 + (title_size.Height - button_size.Height)/2,
+ button_size.Width,
+ button_size.Height));
+ this.CurrentMonth = this.CurrentMonth.AddMonths (ScrollChange*-1);
+ } else {
+ // invalidate the next monthbutton
+ this.Invalidate(
+ new Rectangle (
+ this.ClientRectangle.Right - 1 - button_x_offset - button_size.Width,
+ this.ClientRectangle.Y + 1 + (title_size.Height - button_size.Height)/2,
+ button_size.Width,
+ button_size.Height));
+ this.CurrentMonth = this.CurrentMonth.AddMonths (ScrollChange);
+ }
+ }
+
+ // selects the clicked date
+ private void DoDateMouseDown (HitTestInfo hti) {
+ SetItemClick(hti);
+ this.SelectDate (clicked_date);
+ this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ }
+
+ // event run on the mouse up event
+ private void DoMouseUp () {
+ // invalidate the next monthbutton
+ if (this.is_next_clicked) {
+ this.Invalidate(
+ new Rectangle (
+ this.ClientRectangle.Right - 1 - button_x_offset - button_size.Width,
+ this.ClientRectangle.Y + 1 + (title_size.Height - button_size.Height)/2,
+ button_size.Width,
+ button_size.Height));
+ }
+ // invalidate the prev monthbutton
+ if (this.is_previous_clicked) {
+ this.Invalidate(
+ new Rectangle (
+ this.ClientRectangle.X + 1 + button_x_offset,
+ this.ClientRectangle.Y + 1 + (title_size.Height - button_size.Height)/2,
+ button_size.Width,
+ button_size.Height));
+ }
+ if (this.is_date_clicked) {
+ // invalidate the area under the cursor, to remove focus rect
+ this.InvalidateDateRange (new SelectionRange (clicked_date, clicked_date));
+ }
+ this.is_previous_clicked = false;
+ this.is_next_clicked = false;
+ this.is_date_clicked = false;
+ }
+
+// // need when in windowed mode
+// private void LostFocusHandler (object sender, EventArgs e)
+// {
+// if (this.owner != null) {
+// if (this.Visible) {
+// this.owner.HideMonthCalendar ();
+// }
+// }
+// }
+
+ // occurs when mouse moves around control, used for selection
+ private void MouseMoveHandler (object sender, MouseEventArgs e) {
+ HitTestInfo hti = this.HitTest (e.X, e.Y);
+ // clear the last clicked item
+ if (click_state [0]) {
+ // register the click
+ if (hti.HitArea == HitArea.PrevMonthDate ||
+ hti.HitArea == HitArea.NextMonthDate ||
+ hti.HitArea == HitArea.Date)
+ {
+ DoDateMouseDown (hti);
+ if (owner == null) {
+ click_state [0] = true;
+ } else {
+ click_state [0] = false;
+ click_state [1] = false;
+ click_state [2] = false;
+ }
+ }
+
+ }
+ }
+
+ // to check if the mouse has come down on this control
+ private void MouseDownHandler (object sender, MouseEventArgs e)
+ {
+ // clear the click_state variables
+ click_state [0] = false;
+ click_state [1] = false;
+ click_state [2] = false;
+
+ // disable the timer if it was enabled
+ if (timer.Enabled) {
+ timer.Stop ();
+ timer.Enabled = false;
+ }
+
+ Point point = new Point (e.X, e.Y);
+ // figure out if we are in drop down mode and a click happened outside us
+ if (this.owner != null) {
+ if (!this.ClientRectangle.Contains (point)) {
+ this.owner.HideMonthCalendar ();
+ return;
+ }
+ }
+
+ //establish where was hit
+ HitTestInfo hti = this.HitTest(point);
+ // hide the year numeric up down if it was clicked
+ if (year_updown != null && year_updown.Visible && hti.HitArea != HitArea.TitleYear)
+ {
+ year_updown.Visible = false;
+ }
+ switch (hti.HitArea) {
+ case HitArea.PrevMonthButton:
+ case HitArea.NextMonthButton:
+ DoButtonMouseDown (hti);
+ click_state [1] = (hti.HitArea == HitArea.PrevMonthDate);
+ click_state [2] = !click_state [1];
+ timer.Interval = 750;
+ timer.Start ();
+ break;
+ case HitArea.Date:
+ case HitArea.PrevMonthDate:
+ case HitArea.NextMonthDate:
+ DoDateMouseDown (hti);
+ // leave clicked state blank if drop down window
+ if (owner == null) {
+ click_state [0] = true;
+ } else {
+ click_state [0] = false;
+ click_state [1] = false;
+ click_state [2] = false;
+ }
+ break;
+ case HitArea.TitleMonth:
+ month_title_click_location = hti.Point;
+ menu.Show (this, hti.Point);
+ break;
+ case HitArea.TitleYear:
+ // place the numeric up down
+ PrepareYearUpDown(hti.Point);
+ break;
+ case HitArea.TodayLink:
+ this.SetSelectionRange (DateTime.Now.Date, DateTime.Now.Date);
+ this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ break;
+ default:
+ this.is_previous_clicked = false;
+ this.is_next_clicked = false;
+ this.is_date_clicked = false;
+ break;
+ }
+ }
+
+ // raised by any key down events
+ private void KeyDownHandler (object sender, KeyEventArgs e) {
+ // send keys to the year_updown control, let it handle it
+ if(year_updown.Visible) {
+ switch (e.KeyCode) {
+ case Keys.Enter:
+ year_updown.Visible = false;
+ break;
+ case Keys.Up:
+ year_updown.Value = year_updown.Value + 1;
+ break;
+ case Keys.Down:
+ year_updown.Value = year_updown.Value - 1;
+ break;
+ }
+ } else {
+ if (!is_shift_pressed && e.Shift) {
+ first_select_start_date = SelectionStart;
+ is_shift_pressed = e.Shift;
+ }
+ switch (e.KeyCode) {
+ case Keys.Home:
+ // set the date to the start of the month
+ if (is_shift_pressed) {
+ DateTime date = GetFirstDateInMonth (first_select_start_date);
+ if (date < first_select_start_date.AddDays ((MaxSelectionCount-1)*-1)) {
+ date = first_select_start_date.AddDays ((MaxSelectionCount-1)*-1);
+ }
+ this.SetSelectionRange (date, first_select_start_date);
+ } else {
+ DateTime date = GetFirstDateInMonth (this.SelectionStart);
+ this.SetSelectionRange (date, date);
+ }
+ this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ break;
+ case Keys.End:
+ // set the date to the last of the month
+ if (is_shift_pressed) {
+ DateTime date = GetLastDateInMonth (first_select_start_date);
+ if (date > first_select_start_date.AddDays (MaxSelectionCount-1)) {
+ date = first_select_start_date.AddDays (MaxSelectionCount-1);
+ }
+ this.SetSelectionRange (date, first_select_start_date);
+ } else {
+ DateTime date = GetLastDateInMonth (this.SelectionStart);
+ this.SetSelectionRange (date, date);
+ }
+ this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ break;
+ case Keys.PageUp:
+ // set the date to the last of the month
+ if (is_shift_pressed) {
+ this.AddTimeToSelection (-1, false);
+ } else {
+ DateTime date = this.SelectionStart.AddMonths (-1);
+ this.SetSelectionRange (date, date);
+ }
+ this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ break;
+ case Keys.PageDown:
+ // set the date to the last of the month
+ if (is_shift_pressed) {
+ this.AddTimeToSelection (1, false);
+ } else {
+ DateTime date = this.SelectionStart.AddMonths (1);
+ this.SetSelectionRange (date, date);
+ }
+ this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ break;
+ case Keys.Up:
+ // set the back 1 week
+ if (is_shift_pressed) {
+ this.AddTimeToSelection (-7, true);
+ } else {
+ DateTime date = this.SelectionStart.AddDays (-7);
+ this.SetSelectionRange (date, date);
+ }
+ this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ break;
+ case Keys.Down:
+ // set the date forward 1 week
+ if (is_shift_pressed) {
+ this.AddTimeToSelection (7, true);
+ } else {
+ DateTime date = this.SelectionStart.AddDays (7);
+ this.SetSelectionRange (date, date);
+ }
+ this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ break;
+ case Keys.Left:
+ // move one left
+ if (is_shift_pressed) {
+ this.AddTimeToSelection (-1, true);
+ } else {
+ DateTime date = this.SelectionStart.AddDays (-1);
+ this.SetSelectionRange (date, date);
+ }
+ this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ break;
+ case Keys.Right:
+ // move one left
+ if (is_shift_pressed) {
+ this.AddTimeToSelection (1, true);
+ } else {
+ DateTime date = this.SelectionStart.AddDays (1);
+ this.SetSelectionRange (date, date);
+ }
+ this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
+ break;
+ default:
+ break;
+ }
+ e.Handled = true;
+ }
+ }
+
+ // to check if the mouse has come up on this control
+ private void MouseUpHandler (object sender, MouseEventArgs e)
+ {
+ if (timer.Enabled) {
+ timer.Stop ();
+ }
+ // clear the click state array
+ click_state [0] = false;
+ click_state [1] = false;
+ click_state [2] = false;
+ // do the regulare mouseup stuff
+ this.DoMouseUp ();
+ }
+
+ // raised by any key up events
+ private void KeyUpHandler (object sender, KeyEventArgs e) {
+ is_shift_pressed = e.Shift ;
+ e.Handled = true;
+ }
+
+ // paint this control now
+ private void PaintHandler (object sender, PaintEventArgs pe) {
+ if (Width <= 0 || Height <= 0 || Visible == false)
+ return;
+
+ Draw (pe.ClipRectangle, pe.Graphics);
+
+ // fire the new paint handler
+ if (this.Paint != null)
+ {
+ this.Paint (sender, pe);
+ }
+ }
+
+ // returns the region of the control that needs to be redrawn
+ private void InvalidateDateRange (SelectionRange range) {
+ SelectionRange bounds = this.GetDisplayRange (false);
+ if (range.End < bounds.Start || range.Start > bounds.End) {
+ // don't invalidate anything, as the modified date range
+ // is outside the visible bounds of this control
+ return;
+ }
+ // adjust the start and end to be inside the visible range
+ if (range.Start < bounds.Start) {
+ range = new SelectionRange (bounds.Start, range.End);
+ }
+ if (range.End > bounds.End) {
+ range = new SelectionRange (range.Start, bounds.End);
+ }
+ // now invalidate the date rectangles as series of rows
+ DateTime last_month = this.current_month.AddMonths ((CalendarDimensions.Width * CalendarDimensions.Height)).AddDays (-1);
+ DateTime current = range.Start;
+ while (current <= range.End) {
+ DateTime month_end = new DateTime (current.Year, current.Month, 1).AddMonths (1).AddDays (-1);;
+ Rectangle start_rect;
+ Rectangle end_rect;
+ // see if entire selection is in this current month
+ if (range.End <= month_end && current < last_month) {
+ // the end is the last date
+ if (current < this.current_month) {
+ start_rect = GetDateRowRect (current_month, current_month);
+ } else {
+ start_rect = GetDateRowRect (current, current);
+ }
+ end_rect = GetDateRowRect (current, range.End);
+ } else if (current < last_month) {
+ // otherwise it simply means we have a selection spaning
+ // multiple months simply set rectangle inside the current month
+ start_rect = GetDateRowRect (current, current);
+ end_rect = GetDateRowRect (month_end, month_end);
+ } else {
+ // it's outside the visible range
+ start_rect = GetDateRowRect (last_month, last_month.AddDays (1));
+ end_rect = GetDateRowRect (last_month, range.End);
+ }
+ // push to the next month
+ current = month_end.AddDays (1);
+ // invalidate from the start row to the end row for this month
+ this.Invalidate (
+ new Rectangle (
+ start_rect.X,
+ start_rect.Y,
+ start_rect.Width,
+ Math.Max (end_rect.Bottom - start_rect.Y, 0)));
+ }
+ }
+
+ // gets the rect of the row where the specified date appears on the specified month
+ private Rectangle GetDateRowRect (DateTime month, DateTime date) {
+ // first get the general rect of the supplied month
+ Size month_size = SingleMonthSize;
+ Rectangle month_rect = Rectangle.Empty;
+ for (int i=0; i < CalendarDimensions.Width*CalendarDimensions.Height; i++) {
+ DateTime this_month = this.current_month.AddMonths (i);
+ if (month.Year == this_month.Year && month.Month == this_month.Month) {
+ month_rect = new Rectangle (
+ this.ClientRectangle.X + 1 + (month_size.Width * (i%CalendarDimensions.Width)) + (this.calendar_spacing.Width * (i%CalendarDimensions.Width)),
+ this.ClientRectangle.Y + 1 + (month_size.Height * (i/CalendarDimensions.Width)) + (this.calendar_spacing.Height * (i/CalendarDimensions.Width)),
+ month_size.Width,
+ month_size.Height);
+ break;
+ }
+ }
+ // now find out where in the month the supplied date is
+ if (month_rect == Rectangle.Empty) {
+ return Rectangle.Empty;
+ }
+ // find out which row this date is in
+ int row = -1;
+ DateTime first_date = GetFirstDateInMonthGrid (month);
+ DateTime end_date = first_date.AddDays (7);
+ for (int i=0; i < 6; i++) {
+ if (date >= first_date && date < end_date) {
+ row = i;
+ break;
+ }
+ first_date = end_date;
+ end_date = end_date.AddDays (7);
+ }
+ // ensure it's a valid row
+ if (row < 0) {
+ return Rectangle.Empty;
+ }
+ int x_offset = (this.ShowWeekNumbers) ? date_cell_size.Width : 0;
+ int y_offset = title_size.Height + (date_cell_size.Height * (row + 1));
+ return new Rectangle (
+ month_rect.X + x_offset,
+ month_rect.Y + y_offset,
+ date_cell_size.Width * 7,
+ date_cell_size.Height);
+ }
+
+ internal void Draw (Rectangle clip_rect, Graphics dc)
+ {
+ ThemeEngine.Current.DrawMonthCalendar (dc, clip_rect, this);
+ }
+
+ #endregion //internal methods
+
+ #region internal drawing methods
+
+
+ #endregion // internal drawing methods
+
+ #region inner classes and enumerations
+
+ // enumeration about what type of area on the calendar was hit
+ public enum HitArea {
+ Nowhere,
+ TitleBackground,
+ TitleMonth,
+ TitleYear,
+ NextMonthButton,
+ PrevMonthButton,
+ CalendarBackground,
+ Date,
+ NextMonthDate,
+ PrevMonthDate,
+ DayOfWeek,
+ WeekNumbers,
+ TodayLink
+ }
+
+ // info regarding to a hit test on this calendar
+ public sealed class HitTestInfo {
+
+ private HitArea hit_area;
+ private Point point;
+ private DateTime time;
+
+ // default constructor
+ internal HitTestInfo () {
+ hit_area = HitArea.Nowhere;
+ point = new Point (0, 0);
+ time = DateTime.Now;
+ }
+
+ // overload receives all properties
+ internal HitTestInfo (HitArea hit_area, Point point, DateTime time) {
+ this.hit_area = hit_area;
+ this.point = point;
+ this.time = time;
+ }
+
+ // the type of area that was hit
+ public HitArea HitArea {
+ get {
+ return hit_area;
+ }
+ }
+
+ // the point that is being test
+ public Point Point {
+ get {
+ return point;
+ }
+ }
+
+ // the date under the hit test point, only valid if HitArea is Date
+ public DateTime Time {
+ get {
+ return time;
+ }
+ }
+ }
+
+ #endregion // inner classes
+ }
+}