2005-06-06 Peter Bartok <pbartok@novell.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / MonthCalendar.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004 Novell, Inc.
21 //
22 // Authors:
23 //      John BouAntoun  jba-mono@optusnet.com.au
24 //
25 // REMAINING TODO:
26 //      - get the date_cell_size and title_size to be pixel perfect match of SWF
27 //      - show the year spin control
28
29 using System;
30 using System.Collections;
31 using System.ComponentModel;
32 using System.ComponentModel.Design;
33 using System.Drawing;
34 using System.Windows.Forms;
35
36 namespace System.Windows.Forms {
37         [DefaultProperty("SelectionRange")]
38         [DefaultEvent("DateChanged")]
39         [Designer ("System.Windows.Forms.Design.MonthCalendarDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
40         public class MonthCalendar : Control {
41                 #region Local variables
42                 DateTime []             annually_bolded_dates;
43                 Color                   back_color;
44                 DateTime []             bolded_dates;
45                 Size                    calendar_dimensions;
46                 Day                     first_day_of_week;
47                 Color                   fore_color;
48                 DateTime                max_date;
49                 int                     max_selection_count;
50                 DateTime                min_date;
51                 DateTime []             monthly_bolded_dates;
52                 int                     scroll_change;
53                 SelectionRange          selection_range;
54                 bool                    show_today;
55                 bool                    show_today_circle;
56                 bool                    show_week_numbers;
57                 Color                   title_back_color;
58                 Color                   title_fore_color;
59                 DateTime                today_date;
60                 bool                    today_date_set;
61                 Color                   trailing_fore_color;
62                 ContextMenu             menu;
63                 Timer                   timer;
64                 
65                 // internal variables used
66                 internal DateTime               current_month;                  // the month that is being displayed in top left corner of the grid             
67                 internal DateTimePicker owner;                                  // used if this control is popped up
68                 internal int                    button_x_offset;
69                 internal Size                   button_size;
70                 internal Size                   title_size;
71                 internal Size                   date_cell_size;
72                 internal Size                   calendar_spacing;
73                 internal int                    divider_line_offset;
74                 internal DateTime               clicked_date;
75                 internal bool                   is_date_clicked;
76                 internal bool                   is_previous_clicked;
77                 internal bool                   is_next_clicked;
78                 internal bool                   is_shift_pressed;
79                 internal DateTime               first_select_start_date;
80                 private Point                   month_title_click_location;
81                 // this is used to see which item was actually clicked on in the beginning
82                 // so that we know which item to fire on timer
83                 //      0: date clicked
84                 //      1: previous clicked
85                 //      2: next clicked
86                 private bool[]                  click_state;    
87                 
88                 // arraylists used to store new dates
89                 ArrayList                               added_bolded_dates;
90                 ArrayList                               removed_bolded_dates;
91                 ArrayList                               added_annually_bolded_dates;
92                 ArrayList                               removed_annually_bolded_dates;
93                 ArrayList                               added_monthly_bolded_dates;
94                 ArrayList                               removed_monthly_bolded_dates;
95                 
96                 
97                 #endregion      // Local variables
98
99                 #region Public Constructors
100
101                 public MonthCalendar () {
102                         // set up the control painting
103                         SetStyle (ControlStyles.UserPaint, true);
104                         SetStyle (ControlStyles.AllPaintingInWmPaint, true);
105                         
106                         // mouse down timer
107                         timer = new Timer ();
108                         timer.Interval = 500;
109                         timer.Enabled = false;
110                         
111                         // initialise default values 
112                         DateTime now = DateTime.Now.Date;
113                         selection_range = new SelectionRange (now, now);
114                         today_date = now;
115                         current_month = new DateTime (now.Year , now.Month, 1);
116
117                         // iniatialise local members
118                         annually_bolded_dates = null;
119                         back_color = ThemeEngine.Current.ColorWindow;
120                         bolded_dates = null;
121                         calendar_dimensions = new Size (1,1);
122                         first_day_of_week = Day.Default;
123                         fore_color = SystemColors.ControlText;
124                         max_date = new DateTime (9998, 12, 31);
125                         max_selection_count = 7;
126                         min_date = new DateTime (1953, 1, 1);
127                         monthly_bolded_dates = null;
128                         scroll_change = 0;
129                         show_today = true;
130                         show_today_circle = true;
131                         show_week_numbers = false;
132                         title_back_color = ThemeEngine.Current.ColorActiveTitle;
133                         title_fore_color = ThemeEngine.Current.ColorTitleText;
134                         today_date_set = false;
135                         trailing_fore_color = Color.Gray;
136                         
137                         // initialise the arraylest for bolded dates
138                         added_bolded_dates = new ArrayList ();
139                         removed_bolded_dates = new ArrayList ();
140                         added_annually_bolded_dates = new ArrayList ();
141                         removed_annually_bolded_dates = new ArrayList ();
142                         added_monthly_bolded_dates = new ArrayList ();
143                         removed_monthly_bolded_dates = new ArrayList ();
144                 
145                         // intiailise internal variables used
146                         button_x_offset = 5;
147                         button_size = new Size (22, 17);
148                         // default settings based on 8.25 pt San Serif Font
149                         // Not sure of algorithm used to establish this
150                         date_cell_size = new Size (24, 16);             // default size at san-serif 8.25
151                         divider_line_offset = 4;
152                         calendar_spacing = new Size (4, 5);             // horiz and vert spacing between months in a calendar grid
153
154                         // set some state info
155                         clicked_date = now;
156                         is_date_clicked = false;
157                         is_previous_clicked = false;
158                         is_next_clicked = false;
159                         is_shift_pressed = false;
160                         click_state = new bool [] {false, false, false};
161                         first_select_start_date = now;
162                         month_title_click_location = Point.Empty;
163
164                         SetUpContextMenu ();
165
166                         // event handlers
167 //                      LostFocus += new EventHandler (LostFocusHandler);
168                         timer.Tick += new EventHandler (TimerHandler);
169                         MouseMove += new MouseEventHandler (MouseMoveHandler);
170                         MouseDown += new MouseEventHandler (MouseDownHandler);
171                         KeyDown += new KeyEventHandler (KeyDownHandler);
172                         MouseUp += new MouseEventHandler (MouseUpHandler);
173                         KeyUp += new KeyEventHandler (KeyUpHandler);
174                         // this replaces paint so call the control version
175                         ((Control)this).Paint += new PaintEventHandler (PaintHandler);
176                 }
177                 
178                 // called when this control is added to date time picker
179                 internal MonthCalendar (DateTimePicker owner) : this () {       
180                         this.owner = owner;
181                         this.is_visible = false;
182                         this.Size = this.DefaultSize;
183                 }
184
185                 #endregion      // Public Constructors
186
187                 #region Public Instance Properties
188
189                 // dates to make bold on calendar annually (recurring)
190                 [Localizable (true)]
191                 public DateTime[] AnnuallyBoldedDates {
192                         set {
193                                 if (annually_bolded_dates == null || annually_bolded_dates != value) {
194                                         annually_bolded_dates = value;
195                                         this.UpdateBoldedDates ();
196                                         this.Invalidate ();
197                                 }
198                         }
199                         get {
200                                         return annually_bolded_dates;
201                         }
202                 }
203
204                 [Browsable(false)]
205                 [EditorBrowsable(EditorBrowsableState.Never)]
206                 public override Image BackgroundImage {\r
207                         get {\r
208                                 return base.BackgroundImage;\r
209                         }\r
210                         set {\r
211                                 base.BackgroundImage = value;\r
212                         }\r
213                 }\r
214
215
216                 // the back color for the main part of the calendar
217                 public Color BackColor {
218                         set {
219                                 if (back_color != value) {
220                                         back_color = value;
221                                         this.OnBackColorChanged (EventArgs.Empty);
222                                         this.Invalidate ();
223                                 }
224                         }
225                         get {
226                                 return back_color;
227                         }
228                 }
229
230                 // specific dates to make bold on calendar (non-recurring)
231                 [Localizable (true)]
232                 public DateTime[] BoldedDates {
233                         set {
234                                 if (bolded_dates == null || bolded_dates != value) {
235                                         bolded_dates = value;
236                                         this.UpdateBoldedDates ();
237                                         this.Invalidate ();
238                                 }
239                         }
240                         get {
241                                         return bolded_dates;
242                         }
243                 }
244
245                 // the configuration of the monthly grid display - only allowed to display at most,
246                 // 1 calendar year at a time, will be trimmed to fit it properly
247                 [Localizable (true)]
248                 public Size CalendarDimensions {
249                         set {
250                                 if (value.Width < 0 || value.Height < 0) {
251                                         throw new ArgumentException ();
252                                 }
253                                 if (calendar_dimensions != value) {
254                                         // squeeze the grid into 1 calendar year
255                                         if (value.Width * value.Height > 12) {
256                                                 // iteratively reduce the largest dimension till our
257                                                 // product is less than 12
258                                                 if (value.Width > 12 && value.Height > 12) {
259                                                         calendar_dimensions = new Size (4, 3);
260                                                 } else if (value.Width > 12) {
261                                                         for (int i = 12; i > 0; i--) {
262                                                                 if (i * value.Height <= 12) {
263                                                                         calendar_dimensions = new Size (i, value.Height);
264                                                                         break;
265                                                                 }
266                                                         }
267                                                 } else if (value.Height > 12) {
268                                                         for (int i = 12; i > 0; i--) {
269                                                                 if (i * value.Width <= 12) {
270                                                                         calendar_dimensions = new Size (value.Width, i);
271                                                                         break;
272                                                                 }
273                                                         }
274                                                 }
275                                         } else {
276                                                 calendar_dimensions = value;
277                                         }
278                                         this.Invalidate ();
279                                 }
280                         }
281                         get {
282                                 return calendar_dimensions;
283                         }
284                 }
285
286                 // the first day of the week to display
287                 [Localizable (true)]
288                 [DefaultValue (Day.Default)]
289                 public Day FirstDayOfWeek {
290                         set {
291                                 if (first_day_of_week != value) {
292                                         first_day_of_week = value;
293                                         this.Invalidate ();
294                                 }
295                         }
296                         get {
297                                 return first_day_of_week;
298                         }
299                 }
300
301                 // the fore color for the main part of the calendar
302                 public Color ForeColor {
303                         set {
304                                 if (fore_color != value) {
305                                         fore_color = value;
306                                         this.OnForeColorChanged (EventArgs.Empty);
307                                         this.Invalidate ();
308                                 }
309                         }
310                         get {
311                                 return fore_color;
312                         }
313                 }
314
315                 [Browsable(false)]
316                 [EditorBrowsable(EditorBrowsableState.Never)]
317                 public ImeMode ImeMode {
318                         get {
319                                 return ime_mode;
320                         }
321
322                         set {
323                                 if (ime_mode != value) {
324                                         ime_mode = value;
325
326                                         if (ImeModeChanged != null) {
327                                                 ImeModeChanged(this, EventArgs.Empty);
328                                         }
329                                 }
330                         }
331                 }
332
333                 // the maximum date allowed to be selected on this month calendar
334                 public DateTime MaxDate {
335                         set {
336                                 if (value < MinDate) {
337                                         throw new ArgumentException();
338                                 }
339
340                                 if (max_date != value) {
341                                         max_date = value;
342                                 }
343                         }
344                         get {
345                                 return max_date;
346                         }
347                 }
348
349                 // the maximum number of selectable days
350                 [DefaultValue (7)]
351                 public int MaxSelectionCount {
352                         set {
353                                 if (value < 0) {
354                                         throw new ArgumentException();
355                                 }
356                 
357                                 // can't set selectioncount less than already selected dates
358                                 if ((SelectionEnd - SelectionStart).Days > value) {
359                                         throw new ArgumentException();
360                                 }
361                         
362                                 if (max_selection_count != value) {
363                                         max_selection_count = value;
364                                 }
365                         }
366                         get {
367                                 return max_selection_count;
368                         }
369                 }
370
371                 // the minimum date allowed to be selected on this month calendar
372                 public DateTime MinDate {
373                         set {
374                                 if (value < new DateTime (1953, 1, 1)) {
375                                         throw new ArgumentException();
376                                 }
377
378                                 if (value > MaxDate) {
379                                         throw new ArgumentException();
380                                 }
381
382                                 if (max_date != value) {
383                                         min_date = value;
384                                 }
385                         }
386                         get {
387                                 return min_date;
388                         }
389                 }
390
391                 // dates to make bold on calendar monthly (recurring)
392                 [Localizable (true)]
393                 public DateTime[] MonthlyBoldedDates {
394                         set {
395                                 if (monthly_bolded_dates == null || monthly_bolded_dates != value) {
396                                         monthly_bolded_dates = value;
397                                         this.UpdateBoldedDates ();
398                                         this.Invalidate ();
399                                 }
400                         }
401                         get {
402                                         return monthly_bolded_dates;
403                         }
404                 }
405
406                 // the ammount by which to scroll this calendar by
407                 [DefaultValue (0)]
408                 public int ScrollChange {
409                         set {
410                                 if (value < 0 || value > 20000) {
411                                         throw new ArgumentException();
412                                 }
413
414                                 if (scroll_change != value) {
415                                         scroll_change = value;
416                                 }
417                         }
418                         get {
419                                 // if zero it to the default -> the total number of months currently visible
420                                 if (scroll_change == 0) {
421                                         return CalendarDimensions.Width * CalendarDimensions.Height;
422                                 }
423                                 return scroll_change;
424                         }
425                 }
426
427
428                 // the last selected date
429                 [Browsable (false)]
430                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
431                 public DateTime SelectionEnd {
432                         set {
433                                 if (value < MinDate || value > MaxDate) {
434                                         throw new ArgumentException();
435                                 }
436
437                                 if (SelectionRange.End != value) {
438                                         DateTime old_end = SelectionRange.End; 
439                                         // make sure the end obeys the max selection range count
440                                         if (value < SelectionRange.Start) {
441                                                 SelectionRange.Start = value;
442                                         }
443                                         if (value.AddDays((MaxSelectionCount-1)*-1) > SelectionRange.Start) {
444                                                 SelectionRange.Start = value.AddDays((MaxSelectionCount-1)*-1);
445                                         }
446                                         SelectionRange.End = value;
447                                         this.InvalidateDateRange (new SelectionRange (old_end, SelectionRange.End));
448                                         this.OnDateChanged (new DateRangeEventArgs (SelectionStart, SelectionEnd));
449                                 }
450                         }
451                         get {
452                                 return SelectionRange.End;
453                         }
454                 }
455
456                 // the range of selected dates
457                 public SelectionRange SelectionRange {
458                         set {
459                                 if (selection_range != value) {
460                                         SelectionRange old_range = selection_range;
461
462                                         // make sure the end obeys the max selection range count
463                                         if (value.End.AddDays((MaxSelectionCount-1)*-1) > value.Start) {
464                                                 selection_range = new SelectionRange (value.End.AddDays((MaxSelectionCount-1)*-1), value.End);
465                                         } else {
466                                                 selection_range = value;
467                                         }
468                                         SelectionRange visible_range = this.GetDisplayRange(true);
469                                         if(visible_range.Start > selection_range.End) {
470                                                 this.current_month = new DateTime (selection_range.Start.Year, selection_range.Start.Month, 1);
471                                                 this.Invalidate ();
472                                         } else if (visible_range.End < selection_range.Start) {
473                                                 int year_diff = selection_range.End.Year - visible_range.End.Year;
474                                                 int month_diff = selection_range.End.Month - visible_range.End.Month;
475                                                 this.current_month = current_month.AddMonths(year_diff * 12 + month_diff);
476                                                 this.Invalidate ();
477                                         }
478                                         // invalidate the selected range changes
479                                         DateTime diff_start = old_range.Start;
480                                         DateTime diff_end = old_range.End;
481                                         // now decide which region is greated
482                                         if (old_range.Start > SelectionRange.Start) {
483                                                 diff_start = SelectionRange.Start;
484                                         } else if (old_range.Start == SelectionRange.Start) {
485                                                 if (old_range.End < SelectionRange.End) {
486                                                         diff_start = old_range.End;
487                                                 } else {
488                                                         diff_start = SelectionRange.End;
489                                                 }
490                                         }
491                                         if (old_range.End < SelectionRange.End) {
492                                                 diff_end = SelectionRange.End;
493                                         } else if (old_range.End == SelectionRange.End) {
494                                                 if (old_range.Start < SelectionRange.Start) {
495                                                         diff_end = SelectionRange.Start;
496                                                 } else {
497                                                         diff_end = old_range.Start;
498                                                 }
499                                         }
500
501                                         // invalidate the region required
502                                         this.InvalidateDateRange (new SelectionRange (diff_start, diff_end));
503                                         // raise date changed event
504                                         this.OnDateChanged (new DateRangeEventArgs (SelectionStart, SelectionEnd));
505                                 }
506                         }
507                         get {
508                                 return selection_range;
509                         }
510                 }
511
512                 // the first selected date
513                 [Browsable (false)]
514                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
515                 public DateTime SelectionStart {
516                         set {
517                                 if (value < MinDate || value > MaxDate) {
518                                         throw new ArgumentException();
519                                 }
520
521                                 if (SelectionRange.Start != value) {
522                                         DateTime old_start = SelectionRange.Start; 
523                                         // make sure the end obeys the max selection range count
524                                         if (value > SelectionRange.End) {
525                                                 SelectionRange.End = value;
526                                         } else if (value.AddDays(MaxSelectionCount-1) < SelectionRange.End) {
527                                                 SelectionRange.End = value.AddDays(MaxSelectionCount-1);
528                                         }
529                                         SelectionRange.Start = value;
530                                         DateTime new_month = new DateTime(value.Year, value.Month, 1);
531                                         if (current_month != new_month) {
532                                                 current_month = new_month;
533                                                 this.Invalidate ();
534                                         } else {
535                                                 this.InvalidateDateRange (new SelectionRange (old_start, SelectionRange.Start));
536                                         }
537                                         this.OnDateChanged (new DateRangeEventArgs (SelectionStart, SelectionEnd));
538                                 }
539                         }
540                         get {
541                                 return selection_range.Start;
542                         }
543                 }
544
545                 // whether or not to show todays date
546                 [DefaultValue (true)]
547                 public bool ShowToday {
548                         set {
549                                 if (show_today != value) {
550                                         show_today = value;
551                                         this.Invalidate ();
552                                 }
553                         }
554                         get {
555                                 return show_today;
556                         }
557                 }
558
559                 // whether or not to show a circle around todays date
560                 [DefaultValue (true)]
561                 public bool ShowTodayCircle {
562                         set {
563                                 if (show_today_circle != value) {
564                                         show_today_circle = value;
565                                         this.Invalidate ();
566                                 }
567                         }
568                         get {
569                                 return show_today_circle;
570                         }
571                 }
572
573                 // whether or not to show numbers beside each row of weeks
574                 [Localizable (true)]
575                 [DefaultValue (false)]
576                 public bool ShowWeekNumbers {
577                         set {
578                                 if (show_week_numbers != value) {
579                                         show_week_numbers = value;
580                                         this.Invalidate ();
581                                 }
582                         }
583                         get {
584                                 return show_week_numbers;
585                         }
586                 }
587
588                 // the rectangle size required to render one month based on current font
589                 [Browsable (false)]
590                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
591                 public Size SingleMonthSize {
592                         get {
593                                 if (this.Font == null) {
594                                         throw new InvalidOperationException();
595                                 }
596
597                                 // multiplier is sucked out from the font size
598                                 int multiplier = this.Font.Height;
599
600                                 // establis how many columns and rows we have                                   
601                                 int column_count = (ShowWeekNumbers) ? 8 : 7;
602                                 int row_count = 7;              // not including the today date
603
604                                 // set the date_cell_size and the title_size
605                                 date_cell_size = new Size ((int) Math.Ceiling (2.5 * multiplier), (int) Math.Ceiling (1.5 * multiplier));
606                                 title_size = new Size ((date_cell_size.Width * column_count), 3 * multiplier);
607
608                                 return new Size (column_count * date_cell_size.Width, row_count * date_cell_size.Height + title_size.Height);
609                         }
610                 }
611
612                 [Bindable(false)]
613                 [Browsable(false)]
614                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
615                 [EditorBrowsable(EditorBrowsableState.Never)]
616                 public override string Text {\r
617                         get {\r
618                                 return base.Text;\r
619                         }\r
620                         set {\r
621                                 base.Text = value;\r
622                         }\r
623                 }\r
624
625                 // the back color for the title of the calendar and the
626                 // forecolor for the day of the week text
627                 public Color TitleBackColor {
628                         set {
629                                 if (title_back_color != value) {
630                                         title_back_color = value;
631                                         this.Invalidate ();
632                                 }
633                         }
634                         get {
635                                 return title_back_color;
636                         }
637                 }
638
639                 // the fore color for the title of the calendar
640                 public Color TitleForeColor {
641                         set {
642                                 if (title_fore_color != value) {
643                                         title_fore_color = value;
644                                         this.Invalidate ();
645                                 }
646                         }
647                         get {
648                                 return title_fore_color;
649                         }
650                 }
651
652                 // the date this calendar is using to refer to today's date
653                 public DateTime TodayDate {
654                         set {
655                                 today_date_set = true;
656                                 if (today_date != value) {
657                                         today_date = value;
658                                         this.Invalidate ();
659                                 }
660                         }
661                         get {
662                                 return today_date;
663                         }
664                 }
665
666                 // tells if user specifically set today_date for this control           
667                 [Browsable (false)]
668                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
669                 public bool TodayDateSet {
670                         get {
671                                 return today_date_set;
672                         }
673                 }
674
675                 // the color used for trailing dates in the calendar
676                 public Color TrailingForeColor {
677                         set {
678                                 if (trailing_fore_color != value) {
679                                         trailing_fore_color = value;
680                                         SelectionRange bounds = this.GetDisplayRange (false);
681                                         SelectionRange visible_bounds = this.GetDisplayRange (true);
682                                         this.InvalidateDateRange (new SelectionRange (bounds.Start, visible_bounds.Start));
683                                         this.InvalidateDateRange (new SelectionRange (bounds.End, visible_bounds.End));
684                                 }
685                         }
686                         get {
687                                 return trailing_fore_color;
688                         }
689                 }
690
691                 #endregion      // Public Instance Properties
692
693                 #region Protected Instance Properties
694
695                 // overloaded to allow controll to be windowed for drop down
696                 protected override CreateParams CreateParams {
697                         get {
698                                 if (this.owner == null) {
699                                         return base.CreateParams;                                       
700                                 } else {
701                                         CreateParams cp = base.CreateParams;                                    
702                                         cp.Style = unchecked ((int)(WindowStyles.WS_POPUP | WindowStyles.WS_VISIBLE | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN));
703                                         cp.ExStyle |= (int)(WindowStyles.WS_EX_TOOLWINDOW | WindowStyles.WS_EX_TOPMOST);
704                                         return cp;
705                                 }
706                         }
707                 }
708         
709                 // not sure what to put in here - just doing a base() call - jba
710                 protected override ImeMode DefaultImeMode {
711                         get {
712                                 return base.DefaultImeMode;
713                         }
714                 }
715
716                 protected override Size DefaultSize {
717                         get {
718                                 Size single_month = SingleMonthSize;
719                                 // get the width
720                                 int width = calendar_dimensions.Width * single_month.Width;
721                                 if (calendar_dimensions.Width > 1) {
722                                         width += (calendar_dimensions.Width - 1) * calendar_spacing.Width;
723                                 }
724
725                                 // get the height
726                                 int height = calendar_dimensions.Height * single_month.Height;
727                                 if (this.ShowToday) {
728                                         height += date_cell_size.Height + 2;            // add the height of the "Today: " ...
729                                 }
730                                 if (calendar_dimensions.Height > 1) {
731                                         height += (calendar_dimensions.Height - 1) * calendar_spacing.Height;
732                                 }
733
734                                 // add the 1 pixel boundary
735                                 if (width > 0) {
736                                         width += 2;
737                                 }
738                                 if (height > 0) {
739                                         height +=2;
740                                 }
741
742                                 return new Size (width, height);
743                         }
744                 }
745
746                 #endregion      // Protected Instance Properties
747
748                 #region Public Instance Methods
749
750                 // add a date to the anually bolded date arraylist
751                 public void AddAnnuallyBoldedDate (DateTime date) {
752                         added_annually_bolded_dates.Add (date.Date);
753                 }
754
755                 // add a date to the normal bolded date arraylist
756                 public void AddBoldedDate (DateTime date) {
757                         added_bolded_dates.Add (date.Date);
758                 }
759
760                 // add a date to the anually monthly date arraylist
761                 public void AddMonthlyBoldedDate (DateTime date) {
762                         added_monthly_bolded_dates.Add (date.Date);
763                 }
764
765                 // if visible = true, return only the dates of full months, else return all dates visible
766                 public SelectionRange GetDisplayRange (bool visible) {
767                         DateTime start;
768                         DateTime end;
769                         start = new DateTime (current_month.Year, current_month.Month, 1);
770                         end = start.AddMonths (calendar_dimensions.Width * calendar_dimensions.Height);
771                         end = end.AddDays(-1);
772
773                         // process all visible dates if needed (including the grayed out dates
774                         if (!visible) {
775                                 start = GetFirstDateInMonthGrid (start);
776                                 end = GetLastDateInMonthGrid (end);
777                         }
778
779                         return new SelectionRange (start, end);
780                 }
781
782                 // HitTest overload that recieve's x and y co-ordinates as separate ints
783                 public HitTestInfo HitTest (int x, int y) {
784                         return HitTest (new Point (x, y));
785                 }
786
787                 // returns a HitTestInfo for MonthCalendar element's under the specified point
788                 public HitTestInfo HitTest (Point point) {
789                         // before doing all the hard work, see if the today's date wasn't clicked
790                         Rectangle today_rect = new Rectangle (
791                                 ClientRectangle.X, 
792                                 ClientRectangle.Bottom - date_cell_size.Height,
793                                 7 * date_cell_size.Width,
794                                 date_cell_size.Height);
795                         if (today_rect.Contains (point) && this.ShowToday) {
796                                 return new HitTestInfo(HitArea.TodayLink, point, DateTime.Now);
797                         }
798
799                         Size month_size = SingleMonthSize;
800                         // define calendar rect's that this thing can land in
801                         Rectangle[] calendars = new Rectangle [CalendarDimensions.Width * CalendarDimensions.Height];
802                         for (int i=0; i < CalendarDimensions.Width * CalendarDimensions.Height; i ++) {
803                                 if (i == 0) {
804                                         calendars[i] = new Rectangle (
805                                                 new Point (ClientRectangle.X + 1, ClientRectangle.Y + 1),
806                                                 month_size);
807                                 } else {
808                                         // calendar on the next row
809                                         if (i % CalendarDimensions.Width == 0) {
810                                                 calendars[i] = new Rectangle (
811                                                         new Point (calendars[i-CalendarDimensions.Width].X, calendars[i-CalendarDimensions.Width].Bottom + calendar_spacing.Height),
812                                                         month_size);
813                                         } else {
814                                                 // calendar on the next column
815                                                 calendars[i] = new Rectangle (
816                                                         new Point (calendars[i-1].Right + calendar_spacing.Width, calendars[i-1].Y),
817                                                         month_size);
818                                         }
819                                 }
820                         }
821                         
822                         // through each trying to find a match
823                         for (int i = 0; i < calendars.Length ; i++) {
824                                 if (calendars[i].Contains (point)) {                                    
825                                         // check the title section
826                                         Rectangle title_rect = new Rectangle (
827                                                 calendars[i].Location,
828                                                 title_size);
829                                         if (title_rect.Contains (point) ) {
830                                                 // make sure it's not a previous button
831                                                 if (i == 0) {
832                                                         Rectangle button_rect = new Rectangle(
833                                                                 new Point (calendars[i].X + button_x_offset, (title_size.Height - button_size.Height)/2),
834                                                                 button_size);
835                                                         if (button_rect.Contains (point)) \r
836                                                         {
837                                                                 return new HitTestInfo(HitArea.PrevMonthButton, point, DateTime.Now);
838                                                         }
839                                                 }
840                                                 // make sure it's not the next button
841                                                 if (i % CalendarDimensions.Height == 0 && i % CalendarDimensions.Width == calendar_dimensions.Width - 1) {
842                                                         Rectangle button_rect = new Rectangle(
843                                                                 new Point (calendars[i].Right - button_x_offset - button_size.Width, (title_size.Height - button_size.Height)/2),
844                                                                 button_size);
845                                                         if (button_rect.Contains (point)) \r
846                                                         {
847                                                                 return new HitTestInfo(HitArea.NextMonthButton, point, DateTime.Now);
848                                                         }
849                                                 }
850
851                                                 // make sure it's not the month or the year of the calendar
852                                                 if (GetMonthNameRectangle (title_rect, i).Contains (point)) {
853                                                         return new HitTestInfo(HitArea.TitleMonth, point, DateTime.Now);
854                                                 }
855                                                 if (GetYearNameRectangle (title_rect, i).Contains (point)) {
856                                                         return new HitTestInfo(HitArea.TitleYear, point, DateTime.Now);
857                                                 }
858
859                                                 // return the hit test in the title background
860                                                 return new HitTestInfo(HitArea.TitleBackground, point, DateTime.Now);
861                                         }
862
863                                         Point date_grid_location = new Point (calendars[i].X, title_rect.Bottom);
864
865                                         // see if it's in the Week numbers
866                                         if (ShowWeekNumbers) {
867                                                 Rectangle weeks_rect = new Rectangle (
868                                                         date_grid_location,
869                                                         new Size (date_cell_size.Width,Math.Max (calendars[i].Height - title_rect.Height, 0)));
870                                                 if (weeks_rect.Contains (point)) {
871                                                         return new HitTestInfo(HitArea.WeekNumbers, point, DateTime.Now);
872                                                 }
873
874                                                 // move the location of the grid over
875                                                 date_grid_location.X += date_cell_size.Width;
876                                         }
877
878                                         // see if it's in the week names
879                                         Rectangle day_rect = new Rectangle (
880                                                 date_grid_location,
881                                                 new Size (Math.Max (calendars[i].Right - date_grid_location.X, 0), date_cell_size.Height));
882                                         if (day_rect.Contains (point)) {                                                
883                                                 return new HitTestInfo(HitArea.DayOfWeek, point, DateTime.Now);
884                                         }
885                                                 
886                                         // finally see if it was a date that was clicked
887                                         Rectangle date_grid = new Rectangle (
888                                                 new Point (day_rect.X, day_rect.Bottom),
889                                                 new Size (day_rect.Width, Math.Max(calendars[i].Bottom - day_rect.Bottom, 0)));
890                                         if (date_grid.Contains (point)) {
891                                                 // okay so it's inside the grid, get the offset
892                                                 Point offset = new Point (point.X - date_grid.X, point.Y - date_grid.Y);
893                                                 int row = offset.Y / date_cell_size.Height;
894                                                 int col = offset.X / date_cell_size.Width;
895                                                 // establish our first day of the month
896                                                 DateTime calendar_month = this.CurrentMonth.AddMonths(i);
897                                                 DateTime first_day = GetFirstDateInMonthGrid (calendar_month);
898                                                 DateTime time = first_day.AddDays ((row * 7) + col);
899                                                 // establish which date was clicked
900                                                 if (time.Year != calendar_month.Year || time.Month != calendar_month.Month) {
901                                                         if (time < calendar_month && i == 0) {
902                                                                 return new HitTestInfo(HitArea.PrevMonthDate, point, time);
903                                                         } else if (time > calendar_month && i == CalendarDimensions.Width*CalendarDimensions.Height - 1) {
904                                                                 return new HitTestInfo(HitArea.NextMonthDate, point, time);
905                                                         }
906                                                         return new HitTestInfo(HitArea.Nowhere, point, time);
907                                                 }
908                                                 return new HitTestInfo(HitArea.Date, point, time);
909                                         }
910                                 }                               
911                         }
912
913                         return new HitTestInfo ();
914                 }
915
916                 // clears all the annually bolded dates
917                 public void RemoveAllAnnuallyBoldedDates () {
918                         annually_bolded_dates = null;
919                         added_annually_bolded_dates.Clear ();
920                         removed_annually_bolded_dates.Clear ();
921                 }
922
923                 // clears all the normal bolded dates
924                 public void RemoveAllBoldedDates () {
925                         bolded_dates = null;
926                         added_bolded_dates.Clear ();
927                         removed_bolded_dates.Clear ();
928                 }
929
930                 // clears all the monthly bolded dates
931                 public void RemoveAllMonthlyBoldedDates () {
932                         monthly_bolded_dates = null;
933                         added_monthly_bolded_dates.Clear ();
934                         removed_monthly_bolded_dates.Clear ();
935                 }
936
937                 // clears the specified annually bolded date (only compares day and month)
938                 // only removes the first instance of the match
939                 public void RemoveAnnuallyBoldedDate (DateTime date) {
940                         if (!removed_annually_bolded_dates.Contains (date.Date)) {
941                                 removed_annually_bolded_dates.Add (date.Date);
942                         }
943                 }
944
945                 // clears all the normal bolded date
946                 // only removes the first instance of the match
947                 public void RemoveBoldedDate (DateTime date) {
948                         if (!removed_bolded_dates.Contains (date.Date)) {
949                                 removed_bolded_dates.Add (date.Date);
950                         }
951                 }
952
953                 // clears the specified monthly bolded date (only compares day and month)
954                 // only removes the first instance of the match
955                 public void RemoveMonthlyBoldedDate (DateTime date) {
956                         if (!removed_monthly_bolded_dates.Contains (date.Date)) {
957                                 removed_monthly_bolded_dates.Add (date.Date);
958                         }
959                 }
960
961                 // sets the calendar_dimensions. If product is > 12, the larger dimension is reduced to make product < 12
962                 public void SetCalendarDimensions(int x, int y) {
963                         this.CalendarDimensions = new Size(x, y);
964                 }
965
966                 // sets the currently selected date as date
967                 public void SetDate (DateTime date) {
968                         this.SetSelectionRange (date.Date, date.Date);
969                 }
970
971                 // utility method set the SelectionRange property using individual dates
972                 public void SetSelectionRange (DateTime date1, DateTime date2) {
973                         this.SelectionRange = new SelectionRange(date1, date2);
974                 }
975
976                 public override string ToString () {
977                         return this.GetType().Name + ", " + this.SelectionRange.ToString ();
978                 }
979                                 
980                 // usually called after an AddBoldedDate method is called
981                 // formats monthly and daily bolded dates according to the current calendar year
982                 public void UpdateBoldedDates () {
983                         UpdateDateArray (ref bolded_dates, added_bolded_dates, removed_bolded_dates);
984                         UpdateDateArray (ref monthly_bolded_dates, added_monthly_bolded_dates, removed_monthly_bolded_dates);
985                         UpdateDateArray (ref annually_bolded_dates, added_annually_bolded_dates, removed_annually_bolded_dates);
986                 }
987
988                 #endregion      // Public Instance Methods
989
990                 #region Protected Instance Methods
991
992                 // not sure why this needs to be overriden
993                 protected override void CreateHandle () {
994                         base.CreateHandle ();
995                 }
996
997                 // not sure why this needs to be overriden
998                 protected override void Dispose (bool disposing) {
999                         base.Dispose (disposing);
1000                 }
1001
1002                 // not sure why this needs to be overriden
1003                 protected override bool IsInputKey (Keys keyData) {
1004                         return base.IsInputKey (keyData);
1005                 }
1006
1007                 // not sure why this needs to be overriden
1008                 protected override void OnBackColorChanged (EventArgs e) {
1009                         base.OnBackColorChanged (e);
1010                         this.Invalidate ();
1011                 }
1012
1013                 // raises the date changed event
1014                 protected virtual void OnDateChanged (DateRangeEventArgs drevent) {
1015                         if (this.DateChanged != null) {
1016                                 this.DateChanged (this, drevent);
1017                         }
1018                 }
1019
1020                 // raises the DateSelected event
1021                 protected virtual void OnDateSelected (DateRangeEventArgs drevent) {
1022                         if (this.DateSelected != null) {
1023                                 this.DateSelected (this, drevent);
1024                         }
1025                 }
1026
1027                 protected override void OnFontChanged (EventArgs e) {
1028                         base.OnFontChanged (e);
1029                 }
1030
1031                 protected override void OnForeColorChanged (EventArgs e) {
1032                         base.OnForeColorChanged (e);
1033                 }
1034
1035                 protected override void OnHandleCreated (EventArgs e) {
1036                         base.OnHandleCreated (e);
1037                 }
1038
1039                 // i think this is overriden to not allow the control to be changed to an arbitrary size
1040                 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified) {
1041                         if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height ||
1042                                 (specified & BoundsSpecified.Width) == BoundsSpecified.Width ||
1043                                 (specified & BoundsSpecified.Size) == BoundsSpecified.Size) {
1044                                 // only allow sizes = default size to be set
1045                                 Size min_size = DefaultSize;
1046                                 Size max_size = new Size (
1047                                         DefaultSize.Width + SingleMonthSize.Width + calendar_spacing.Width,
1048                                         DefaultSize.Height + SingleMonthSize.Height + calendar_spacing.Height);
1049                                 int x_mid_point = (max_size.Width + min_size.Width)/2;
1050                                 int y_mid_point = (max_size.Height + min_size.Height)/2;
1051                                 if (width < x_mid_point) {
1052                                         width = min_size.Width;
1053                                 } else {
1054                                         width = max_size.Width;
1055                                 }
1056                                 if (height < y_mid_point) {
1057                                         height = min_size.Height;
1058                                 } else {
1059                                         height = max_size.Height;
1060                                 }
1061                                 base.SetBoundsCore (x, y, width, height, specified);
1062                         } else {
1063                                 base.SetBoundsCore (x, y, width, height, specified);
1064                         }
1065                 }
1066
1067                 protected override void WndProc (ref Message m) {
1068                         base.WndProc (ref m);
1069                 }
1070
1071                 #endregion      // Protected Instance Methods
1072
1073                 #region public events
1074
1075                 // fired when the date is changed (either explicitely or implicitely)
1076                 // when navigating the month selector
1077                 public event DateRangeEventHandler DateChanged;
1078
1079                 // fired when the user explicitely clicks on date to select it
1080                 public event DateRangeEventHandler DateSelected;
1081
1082                 [Browsable(false)]
1083                 [EditorBrowsable (EditorBrowsableState.Never)]
1084                 public event EventHandler BackgroundImageChanged;
1085
1086                 [MonoTODO("Fire Click event")]
1087                 [Browsable(false)]
1088                 [EditorBrowsable (EditorBrowsableState.Never)]
1089                 public event EventHandler Click;
1090
1091                 [MonoTODO("Fire DoubleClick event")]
1092                 [Browsable(false)]
1093                 [EditorBrowsable (EditorBrowsableState.Never)]
1094                 public event EventHandler DoubleClick;
1095
1096                 [Browsable(false)]
1097                 [EditorBrowsable (EditorBrowsableState.Never)]
1098                 public event EventHandler ImeModeChanged;
1099
1100                 [Browsable(false)]
1101                 [EditorBrowsable (EditorBrowsableState.Never)]
1102                 public new event PaintEventHandler Paint;
1103
1104                 [Browsable(false)]
1105                 [EditorBrowsable (EditorBrowsableState.Never)]
1106                 public event EventHandler TextChanged;
1107                 #endregion      // public events
1108
1109                 #region internal properties
1110
1111                 internal DateTime CurrentMonth {
1112                         set {
1113                                 // only interested in if the month (not actual date) has changed
1114                                 if (value.Month != current_month.Month ||
1115                                         value.Year != current_month.Year) {
1116                                         this.SelectionRange = new SelectionRange(
1117                                                 this.SelectionStart.Add(value.Subtract(current_month)),
1118                                                 this.SelectionEnd.Add(value.Subtract(current_month)));
1119                                         current_month = value;
1120                                         UpdateBoldedDates();
1121                                         this.Invalidate();
1122                                 }
1123                         }
1124                         get {
1125                                 return current_month;
1126                         }
1127                 }
1128
1129                 #endregion      // internal properties
1130
1131                 #region internal/private methods
1132
1133                 // returns the date of the first cell of the specified month grid
1134                 internal DateTime GetFirstDateInMonthGrid (DateTime month) {
1135                         // convert the first_day_of_week into a DayOfWeekEnum
1136                         DayOfWeek first_day = GetDayOfWeek (first_day_of_week);
1137                         // find the first day of the month
1138                         DateTime first_date_of_month = new DateTime (month.Year, month.Month, 1);
1139                         DayOfWeek first_day_of_month = first_date_of_month.DayOfWeek;                   
1140                         // adjust for the starting day of the week
1141                         int offset = first_day_of_month - first_day;
1142                         if (offset < 0) {
1143                                 offset += 7;
1144                         }
1145                         return first_date_of_month.AddDays (-1*offset);
1146                 }
1147
1148                 // returns the date of the last cell of the specified month grid
1149                 internal DateTime GetLastDateInMonthGrid (DateTime month) \r
1150                 {
1151                         DateTime start = GetFirstDateInMonthGrid(month);
1152                         return start.AddDays ((7 * 6)-1);
1153                 }
1154                 
1155                 internal bool IsBoldedDate (DateTime date) {
1156                         // check bolded dates
1157                         if (bolded_dates != null && bolded_dates.Length > 0) {
1158                                 foreach (DateTime bolded_date in bolded_dates) {
1159                                         if (bolded_date.Date == date.Date) {
1160                                                 return true;
1161                                         }
1162                                 }
1163                         }
1164                         // check monthly dates
1165                         if (monthly_bolded_dates != null && monthly_bolded_dates.Length > 0) {
1166                                 foreach (DateTime bolded_date in monthly_bolded_dates) {
1167                                         if (bolded_date.Day == date.Day) {
1168                                                 return true;
1169                                         }
1170                                 }
1171                         }
1172                         // check yearly dates
1173                         if (annually_bolded_dates != null && annually_bolded_dates.Length > 0) {
1174                                 foreach (DateTime bolded_date in annually_bolded_dates) {
1175                                         if (bolded_date.Month == date.Month && bolded_date.Day == date.Day) {
1176                                                 return true;
1177                                         }
1178                                 }
1179                         }
1180                         
1181                         return false;  // no match
1182                 }
1183                 
1184                 // updates the specified bolded dates array with ones to add and ones to remove
1185                 private void UpdateDateArray (ref DateTime [] dates, ArrayList to_add, ArrayList to_remove) {
1186                         ArrayList list = new ArrayList ();
1187                         
1188                         // update normal bolded dates
1189                         if (dates != null) {
1190                                 foreach (DateTime date in dates) {
1191                                         list.Add (date.Date);
1192                                 }
1193                         }
1194                         
1195                         // add new ones
1196                         foreach (DateTime date in to_add) {
1197                                 if (!list.Contains (date.Date)) {
1198                                         list.Add (date.Date);
1199                                 }
1200                         }
1201                         to_add.Clear ();
1202                         
1203                         // remove ones to remove
1204                         foreach (DateTime date in to_remove) {
1205                                 if (list.Contains (date.Date)) {
1206                                         list.Remove (date.Date);
1207                                 }
1208                         }
1209                         to_remove.Clear ();
1210                         // set up the array now 
1211                         if (list.Count > 0) {
1212                                 dates = (DateTime []) list.ToArray (typeof (DateTime));
1213                                 Array.Sort (dates);
1214                                 list.Clear ();
1215                         } else {
1216                                 dates = null;
1217                         }
1218                 }
1219
1220                 // initialise the context menu
1221                 private void SetUpContextMenu () {
1222                         menu = new ContextMenu ();
1223                         for (int i=0; i < 12; i++) {
1224                                 MenuItem menu_item = new MenuItem ( new DateTime (2000, i+1, 1).ToString ("MMMM"));
1225                                 menu_item.Click += new EventHandler (MenuItemClickHandler);
1226                                 menu.MenuItems.Add (menu_item);
1227                         }
1228                 }
1229
1230                 // returns the first date of the month
1231                 private DateTime GetFirstDateInMonth (DateTime date) {
1232                         return new DateTime (date.Year, date.Month, 1);
1233                 }
1234
1235                 // returns the last date of the month
1236                 private DateTime GetLastDateInMonth (DateTime date) {
1237                         return new DateTime (date.Year, date.Month, 1).AddMonths(1).AddDays(-1);
1238                 }
1239
1240                 // called in response to users seletion with shift key
1241                 private void AddTimeToSelection (int delta, bool isDays)
1242                 {
1243                         DateTime cursor_point;
1244                         DateTime end_point;
1245                         // okay we add the period to the date that is not the same as the 
1246                         // start date when shift was first clicked.
1247                         if (SelectionStart != first_select_start_date) {
1248                                 cursor_point = SelectionStart;
1249                         } else {
1250                                 cursor_point = SelectionEnd;
1251                         }
1252                         // add the days
1253                         if (isDays) {
1254                                 end_point = cursor_point.AddDays (delta);
1255                         } else {
1256                                 // delta must be months
1257                                 end_point = cursor_point.AddMonths (delta);
1258                         }
1259                         // set the new selection range
1260                         SelectionRange range = new SelectionRange (first_select_start_date, end_point);
1261                         if (range.Start.AddDays (MaxSelectionCount-1) < range.End) {
1262                                 // okay the date is beyond what is allowed, lets set the maximum we can
1263                                 if (range.Start != first_select_start_date) {
1264                                         range.Start = range.End.AddDays ((MaxSelectionCount-1)*-1);
1265                                 } else {
1266                                         range.End = range.Start.AddDays (MaxSelectionCount-1);
1267                                 }
1268                         }
1269                         this.SelectionRange = range;
1270                 }
1271
1272                 // attempts to add the date to the selection without throwing exception
1273                 private void SelectDate (DateTime date) {
1274                         // try and add the new date to the selction range
1275                         if (is_shift_pressed || (click_state [0])) {
1276                                 SelectionRange range = new SelectionRange (first_select_start_date, date);
1277                                 if (range.Start.AddDays (MaxSelectionCount-1) < range.End) {
1278                                         // okay the date is beyond what is allowed, lets set the maximum we can
1279                                         if (range.Start != first_select_start_date) {
1280                                                 range.Start = range.End.AddDays ((MaxSelectionCount-1)*-1);
1281                                         } else {
1282                                                 range.End = range.Start.AddDays (MaxSelectionCount-1);
1283                                         }
1284                                 }
1285                                 SelectionRange = range;
1286                         } else {
1287                                 SelectionRange = new SelectionRange (date, date);
1288                                 first_select_start_date = date;
1289                         }
1290                 }
1291
1292                 // gets the week of the year
1293                 internal int GetWeekOfYear (DateTime date) {
1294                         // convert the first_day_of_week into a DayOfWeekEnum
1295                         DayOfWeek first_day = GetDayOfWeek (first_day_of_week);
1296                         // find the first day of the year
1297                         DayOfWeek first_day_of_year = new DateTime (date.Year, 1, 1).DayOfWeek;
1298                         // adjust for the starting day of the week
1299                         int offset = first_day_of_year - first_day;
1300                         int week = ((date.DayOfYear + offset) / 7) + 1;
1301                         return week;
1302                 }
1303
1304                 // convert a Day enum into a DayOfWeek enum
1305                 internal DayOfWeek GetDayOfWeek (Day day) {
1306                         if (day == Day.Default) {
1307                                 return DayOfWeek.Sunday;
1308                         } else {
1309                                 return (DayOfWeek) DayOfWeek.Parse (typeof (DayOfWeek), day.ToString ());
1310                         }
1311                 }
1312
1313                 // returns the rectangle for themonth name
1314                 internal Rectangle GetMonthNameRectangle (Rectangle title_rect, int calendar_index) {
1315                         Graphics g = this.DeviceContext;
1316                         DateTime this_month = this.current_month.AddMonths (calendar_index);
1317                         Size title_text_size = g.MeasureString (this_month.ToString ("MMMM yyyy"), this.Font).ToSize ();
1318                         Size month_size = g.MeasureString (this_month.ToString ("MMMM"), this.Font).ToSize ();
1319                         // return only the month name part of that
1320                         return new Rectangle (
1321                                 new Point (
1322                                         title_rect.X + ((title_rect.Width - title_text_size.Width)/2),
1323                                         title_rect.Y + ((title_rect.Height - title_text_size.Height)/2)),
1324                                 month_size);
1325                 }
1326
1327                 // returns the rectangle for the year in the title
1328                 internal Rectangle GetYearNameRectangle (Rectangle title_rect, int calendar_index) {                    
1329                         Graphics g = this.DeviceContext;
1330                         DateTime this_month = this.current_month.AddMonths (calendar_index);
1331                         Size title_text_size = g.MeasureString (this_month.ToString ("MMMM yyyy"), this.Font).ToSize ();
1332                         Size year_size = g.MeasureString (this_month.ToString ("yyyy"), this.Font).ToSize ();
1333                         // find out how much space the title took
1334                         Rectangle text_rect =  new Rectangle (
1335                                 new Point (
1336                                         title_rect.X + ((title_rect.Width - title_text_size.Width)/2),
1337                                         title_rect.Y + ((title_rect.Height - title_text_size.Height)/2)),
1338                                 title_text_size);
1339                         // return only the rect of the year
1340                         return new Rectangle (
1341                                 new Point (
1342                                         text_rect.Right - year_size.Width,
1343                                         text_rect.Y),
1344                                 year_size);
1345                 }
1346
1347                 // determine if date is allowed to be drawn in month
1348                 internal bool IsValidWeekToDraw (DateTime month, DateTime date, int row, int col) {
1349                         DateTime tocheck = month.AddMonths (-1);
1350                         if ((month.Year == date.Year && month.Month == date.Month) ||
1351                                 (tocheck.Year == date.Year && tocheck.Month == date.Month)) {
1352                                 return true;
1353                         }
1354
1355                         // check the railing dates (days in the month after the last month in grid)
1356                         if (row == CalendarDimensions.Height - 1 && col == CalendarDimensions.Width - 1) {
1357                                 tocheck = month.AddMonths (1);
1358                                 return (tocheck.Year == date.Year && tocheck.Month == date.Month) ;
1359                         }
1360
1361                         return false;                   
1362                 }
1363
1364                 // set one item clicked and all others off
1365                 private void SetItemClick(HitTestInfo hti) \r
1366                 {
1367                         switch(hti.HitArea) {
1368                                 case HitArea.NextMonthButton:
1369                                         this.is_previous_clicked = false;
1370                                         this.is_next_clicked = true;
1371                                         this.is_date_clicked = false;
1372                                         break;
1373                                 case HitArea.PrevMonthButton:
1374                                         this.is_previous_clicked = true;
1375                                         this.is_next_clicked = false;
1376                                         this.is_date_clicked = false;
1377                                         break;
1378                                 case HitArea.PrevMonthDate:
1379                                 case HitArea.NextMonthDate:
1380                                 case HitArea.Date:
1381                                         this.clicked_date = hti.Time;
1382                                         this.is_previous_clicked = false;
1383                                         this.is_next_clicked = false;
1384                                         this.is_date_clicked = true;
1385                                         break;
1386                                 default :
1387                                         this.is_previous_clicked = false;
1388                                         this.is_next_clicked = false;
1389                                         this.is_date_clicked = false;
1390                                         break;
1391                         }
1392                 }
1393
1394                 // called when context menu is clicked
1395                 private void MenuItemClickHandler (object sender, EventArgs e) {
1396                         MenuItem item = sender as MenuItem;
1397                         if (item != null && month_title_click_location != Point.Empty) {
1398                                 // establish which month we want to move to
1399                                 if (item.Parent == null) {
1400                                         return;
1401                                 }
1402                                 int new_month = item.Parent.MenuItems.IndexOf (item) + 1;
1403                                 if (new_month == 0) {
1404                                         return;
1405                                 }
1406                                 // okay let's establish which calendar was hit
1407                                 Size month_size = this.SingleMonthSize;
1408                                 for (int i=0; i < CalendarDimensions.Height; i++) {
1409                                         for (int j=0; j < CalendarDimensions.Width; j++) {
1410                                                 int month_index = (i * CalendarDimensions.Width) + j;
1411                                                 Rectangle month_rect = new Rectangle ( new Point (0, 0), month_size);
1412                                                 if (j == 0) {
1413                                                         month_rect.X = this.ClientRectangle.X + 1;
1414                                                 } else {
1415                                                         month_rect.X = this.ClientRectangle.X + 1 + ((j)*(month_size.Width+calendar_spacing.Width));
1416                                                 }
1417                                                 if (i == 0) {
1418                                                         month_rect.Y = this.ClientRectangle.Y + 1;
1419                                                 } else {
1420                                                         month_rect.Y = this.ClientRectangle.Y + 1 + ((i)*(month_size.Height+calendar_spacing.Height));
1421                                                 }
1422                                                 // see if the point is inside
1423                                                 if (month_rect.Contains (month_title_click_location)) {
1424                                                         DateTime clicked_month = CurrentMonth.AddMonths (month_index);
1425                                                         // get the month that we want to move to
1426                                                         int month_offset = new_month - clicked_month.Month;
1427                                                         
1428                                                         // move forward however more months we need to
1429                                                         this.CurrentMonth = this.CurrentMonth.AddMonths (month_offset);
1430                                                         break;
1431                                                 }
1432                                         }
1433                                 }
1434
1435                                 // clear the point
1436                                 month_title_click_location = Point.Empty;
1437                         }
1438                 }
1439                 
1440                 // raised on the timer, for mouse hold clicks
1441                 private void TimerHandler (object sender, EventArgs e) {
1442                         // now find out which area was click
1443                         if (this.Capture) {
1444                                 HitTestInfo hti = this.HitTest (this.PointToClient (MousePosition));
1445                                 // see if it was clicked on the prev or next mouse 
1446                                 if (click_state [1] || click_state [2]) {
1447                                         // invalidate the area where the mouse was last held
1448                                         DoMouseUp ();
1449                                         Application.DoEvents ();
1450                                         // register the click
1451                                         if (hti.HitArea == HitArea.PrevMonthButton ||
1452                                                 hti.HitArea == HitArea.NextMonthButton) {
1453                                                 DoButtonMouseDown (hti);
1454                                                 click_state [1] = (hti.HitArea == HitArea.PrevMonthButton);
1455                                                 click_state [2] = !click_state [1];
1456                                         }
1457                                         if (timer.Interval != 100) {
1458                                                 timer.Interval = 100;
1459                                         }
1460                                 }
1461                         } else  {
1462                                 timer.Enabled = false;
1463                         }
1464                 }
1465                 
1466                 // selects one of the buttons
1467                 private void DoButtonMouseDown (HitTestInfo hti) {
1468                         // show the click then move on
1469                         SetItemClick(hti);
1470                         if (hti.HitArea == HitArea.PrevMonthButton) {
1471                                 // invalidate the prev monthbutton
1472                                 this.Invalidate(
1473                                         new Rectangle (
1474                                                 this.ClientRectangle.X + 1 + button_x_offset,
1475                                                 this.ClientRectangle.Y + 1 + (title_size.Height - button_size.Height)/2,
1476                                                 button_size.Width,
1477                                                 button_size.Height));
1478                                 this.CurrentMonth = this.CurrentMonth.AddMonths (ScrollChange*-1);
1479                         } else {
1480                                 // invalidate the next monthbutton
1481                                 this.Invalidate(
1482                                         new Rectangle (
1483                                                 this.ClientRectangle.Right - 1 - button_x_offset - button_size.Width,
1484                                                 this.ClientRectangle.Y + 1 + (title_size.Height - button_size.Height)/2,
1485                                                 button_size.Width,
1486                                                 button_size.Height));                                   
1487                                 this.CurrentMonth = this.CurrentMonth.AddMonths (ScrollChange);
1488                         }
1489                 }
1490                 
1491                 // selects the clicked date
1492                 private void DoDateMouseDown (HitTestInfo hti) {
1493                         SetItemClick(hti);
1494                         this.SelectDate (clicked_date);
1495                         this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
1496                 }
1497                 
1498                 // event run on the mouse up event
1499                 private void DoMouseUp () {
1500                         // invalidate the next monthbutton
1501                         if (this.is_next_clicked) {
1502                                 this.Invalidate(
1503                                         new Rectangle (
1504                                                 this.ClientRectangle.Right - 1 - button_x_offset - button_size.Width,
1505                                                 this.ClientRectangle.Y + 1 + (title_size.Height - button_size.Height)/2,
1506                                                 button_size.Width,
1507                                                 button_size.Height));
1508                         }                                       
1509                         // invalidate the prev monthbutton
1510                         if (this.is_previous_clicked) {
1511                                 this.Invalidate(
1512                                         new Rectangle (
1513                                                 this.ClientRectangle.X + 1 + button_x_offset,
1514                                                 this.ClientRectangle.Y + 1 + (title_size.Height - button_size.Height)/2,
1515                                                 button_size.Width,
1516                                                 button_size.Height));
1517                         }
1518                         if (this.is_date_clicked) {
1519                                 // invalidate the area under the cursor, to remove focus rect
1520                                 this.InvalidateDateRange (new SelectionRange (clicked_date, clicked_date));                             
1521                         }
1522                         this.is_previous_clicked = false;
1523                         this.is_next_clicked = false;
1524                         this.is_date_clicked = false;
1525                 }
1526                 
1527 //              // need when in windowed mode
1528 //              private void LostFocusHandler (object sender, EventArgs e) 
1529 //              {
1530 //                      if (this.owner != null) {
1531 //                              if (this.Visible) {
1532 //                                      this.owner.HideMonthCalendar ();
1533 //                              }
1534 //                      }
1535 //              }
1536                 
1537                 // occurs when mouse moves around control, used for selection
1538                 private void MouseMoveHandler (object sender, MouseEventArgs e) {
1539                         HitTestInfo hti = this.HitTest (e.X, e.Y);
1540                         // clear the last clicked item 
1541                         if (click_state [0]) {
1542                                 // register the click
1543                                 if (hti.HitArea == HitArea.PrevMonthDate ||
1544                                         hti.HitArea == HitArea.NextMonthDate ||
1545                                         hti.HitArea == HitArea.Date)
1546                                 {
1547                                         DoDateMouseDown (hti);
1548                                         if (owner == null) {
1549                                                 click_state [0] = true;
1550                                         } else {
1551                                                 click_state [0] = false;
1552                                                 click_state [1] = false;
1553                                                 click_state [2] = false;
1554                                         }
1555                                 }
1556                                 
1557                         }
1558                 }
1559                 
1560                 // to check if the mouse has come down on this control
1561                 private void MouseDownHandler (object sender, MouseEventArgs e)
1562                 {
1563                         // clear the click_state variables
1564                         click_state [0] = false;
1565                         click_state [1] = false;
1566                         click_state [2] = false;
1567
1568                         // disable the timer if it was enabled 
1569                         if (timer.Enabled) {
1570                                 timer.Stop ();
1571                                 timer.Enabled = false;
1572                         }
1573                         
1574                         Point point = new Point (e.X, e.Y);
1575                         // figure out if we are in drop down mode and a click happened outside us
1576                         if (this.owner != null) {
1577                                 if (!this.ClientRectangle.Contains (point)) {
1578                                         this.owner.HideMonthCalendar ();
1579                                         return;                                 
1580                                 }
1581                         }
1582                         
1583                         //establish where was hit
1584                         HitTestInfo hti = this.HitTest(point);
1585                         switch (hti.HitArea) {\r
1586                                 case HitArea.PrevMonthButton:
1587                                 case HitArea.NextMonthButton:
1588                                         DoButtonMouseDown (hti);
1589                                         click_state [1] = (hti.HitArea == HitArea.PrevMonthDate);
1590                                         click_state [2] = !click_state [1];                                     
1591                                         timer.Interval = 500;
1592                                         timer.Start ();
1593                                         break;
1594                                 case HitArea.Date:
1595                                 case HitArea.PrevMonthDate:
1596                                 case HitArea.NextMonthDate:
1597                                         DoDateMouseDown (hti);
1598                                         // leave clicked state blank if drop down window
1599                                         if (owner == null) {
1600                                                 click_state [0] = true;
1601                                         } else {
1602                                                 click_state [0] = false;
1603                                                 click_state [1] = false;
1604                                                 click_state [2] = false;
1605                                         }
1606                                         break;
1607                                 case HitArea.TitleMonth:
1608                                         month_title_click_location = hti.Point;
1609                                         menu.Show (this, hti.Point);            
1610                                         break;
1611                                 case HitArea.TitleYear:
1612                                         //TODO: show the year spin control
1613                                         System.Console.WriteLine ("//TODO: show the year spin control");
1614                                         break;
1615                                 case HitArea.TodayLink:
1616                                         this.SetSelectionRange (DateTime.Now.Date, DateTime.Now.Date);
1617                                         this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
1618                                         break;
1619                                 default:
1620                                         this.is_previous_clicked = false;
1621                                         this.is_next_clicked = false;
1622                                         this.is_date_clicked = false;                           
1623                                         break;
1624                         }
1625                 }
1626
1627                 // raised by any key down events
1628                 private void KeyDownHandler (object sender, KeyEventArgs e) {
1629                         if (!is_shift_pressed && e.Shift) {
1630                                 first_select_start_date = SelectionStart;
1631                                 is_shift_pressed = e.Shift;
1632                         }
1633                         switch (e.KeyCode) {
1634                                 case Keys.Home:
1635                                         // set the date to the start of the month
1636                                         if (is_shift_pressed) {
1637                                                 DateTime date = GetFirstDateInMonth (first_select_start_date);
1638                                                 if (date < first_select_start_date.AddDays ((MaxSelectionCount-1)*-1)) {
1639                                                         date = first_select_start_date.AddDays ((MaxSelectionCount-1)*-1);
1640                                                 }
1641                                                 this.SetSelectionRange (date, first_select_start_date);
1642                                         } else {
1643                                                 DateTime date = GetFirstDateInMonth (this.SelectionStart);
1644                                                 this.SetSelectionRange (date, date);
1645                                         }
1646                                         this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
1647                                         break;
1648                                 case Keys.End:
1649                                         // set the date to the last of the month
1650                                         if (is_shift_pressed) {
1651                                                 DateTime date = GetLastDateInMonth (first_select_start_date);
1652                                                 if (date > first_select_start_date.AddDays (MaxSelectionCount-1)) {
1653                                                         date = first_select_start_date.AddDays (MaxSelectionCount-1);
1654                                                 }
1655                                                 this.SetSelectionRange (date, first_select_start_date);
1656                                         } else {
1657                                                 DateTime date = GetLastDateInMonth (this.SelectionStart);
1658                                                 this.SetSelectionRange (date, date);
1659                                         }
1660                                         this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
1661                                         break;
1662                                 case Keys.PageUp:
1663                                         // set the date to the last of the month
1664                                         if (is_shift_pressed) {
1665                                                 this.AddTimeToSelection (-1, false);
1666                                         } else {
1667                                                 DateTime date = this.SelectionStart.AddMonths (-1);
1668                                                 this.SetSelectionRange (date, date);
1669                                         }
1670                                         this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
1671                                         break;
1672                                 case Keys.PageDown:
1673                                         // set the date to the last of the month
1674                                         if (is_shift_pressed) {
1675                                                 this.AddTimeToSelection (1, false);
1676                                         } else {
1677                                                 DateTime date = this.SelectionStart.AddMonths (1);
1678                                                 this.SetSelectionRange (date, date);
1679                                         }
1680                                         this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
1681                                         break;
1682                                 case Keys.Up:
1683                                         // set the back 1 week
1684                                         if (is_shift_pressed) {
1685                                                 this.AddTimeToSelection (-7, true);                                             
1686                                         } else {
1687                                                 DateTime date = this.SelectionStart.AddDays (-7);
1688                                                 this.SetSelectionRange (date, date);
1689                                         }
1690                                         this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
1691                                         break;
1692                                 case Keys.Down:
1693                                         // set the date forward 1 week
1694                                         if (is_shift_pressed) {
1695                                                 this.AddTimeToSelection (7, true);
1696                                         } else {
1697                                                 DateTime date = this.SelectionStart.AddDays (7);
1698                                                 this.SetSelectionRange (date, date);
1699                                         }
1700                                         this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
1701                                         break;
1702                                 case Keys.Left:
1703                                         // move one left
1704                                         if (is_shift_pressed) {
1705                                                 this.AddTimeToSelection (-1, true);
1706                                         } else {
1707                                                 DateTime date = this.SelectionStart.AddDays (-1);
1708                                                 this.SetSelectionRange (date, date);
1709                                         }
1710                                         this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
1711                                         break;
1712                                 case Keys.Right:
1713                                         // move one left
1714                                         if (is_shift_pressed) {
1715                                                 this.AddTimeToSelection (1, true);
1716                                         } else {
1717                                                 DateTime date = this.SelectionStart.AddDays (1);
1718                                                 this.SetSelectionRange (date, date);
1719                                         }
1720                                         this.OnDateSelected (new DateRangeEventArgs (SelectionStart, SelectionEnd));
1721                                         break;
1722                                 default:
1723                                         break;
1724                         }
1725                         e.Handled = true;
1726                 }
1727
1728                 // to check if the mouse has come up on this control
1729                 private void MouseUpHandler (object sender, MouseEventArgs e)
1730                 {
1731                         if (timer.Enabled) {
1732                                 timer.Stop ();
1733                         }
1734                         // clear the click state array
1735                         click_state [0] = false;
1736                         click_state [1] = false;
1737                         click_state [2] = false;
1738                         // do the regulare mouseup stuff
1739                         this.DoMouseUp ();
1740                 }
1741
1742                 // raised by any key up events
1743                 private void KeyUpHandler (object sender, KeyEventArgs e) {
1744                         is_shift_pressed = e.Shift ;
1745                         e.Handled = true;
1746                 }
1747
1748                 // paint this control now
1749                 private void PaintHandler (object sender, PaintEventArgs pe) {
1750                         if (Width <= 0 || Height <=  0 || Visible == false)
1751                                 return;
1752
1753                         Draw (pe.ClipRectangle, pe.Graphics);
1754
1755                         // fire the new paint handler
1756                         if (this.Paint != null) 
1757                         {
1758                                 this.Paint (sender, pe);
1759                         }
1760                 }
1761                 
1762                 // returns the region of the control that needs to be redrawn 
1763                 private void InvalidateDateRange (SelectionRange range) {
1764                         SelectionRange bounds = this.GetDisplayRange (false);
1765                         if (range.End < bounds.Start || range.Start > bounds.End) {
1766                                 // don't invalidate anything, as the modified date range
1767                                 // is outside the visible bounds of this control
1768                                 return;
1769                         }
1770                         // adjust the start and end to be inside the visible range
1771                         if (range.Start < bounds.Start) {
1772                                 range = new SelectionRange (bounds.Start, range.End);
1773                         }
1774                         if (range.End > bounds.End) {
1775                                 range = new SelectionRange (range.Start, bounds.End);
1776                         }
1777                         // now invalidate the date rectangles as series of rows
1778                         DateTime last_month = this.current_month.AddMonths ((CalendarDimensions.Width * CalendarDimensions.Height)).AddDays (-1);
1779                         DateTime current = range.Start;
1780                         while (current <= range.End) {
1781                                 DateTime month_end = new DateTime (current.Year, current.Month, 1).AddMonths (1).AddDays (-1);;
1782                                 Rectangle start_rect;
1783                                 Rectangle end_rect;
1784                                 // see if entire selection is in this current month
1785                                 if (range.End <= month_end && current < last_month)     {
1786                                         // the end is the last date
1787                                         if (current < this.current_month) {
1788                                                 start_rect = GetDateRowRect (current_month, current_month);
1789                                         } else {
1790                                                 start_rect = GetDateRowRect (current, current);
1791                                         }
1792                                         end_rect = GetDateRowRect (current, range.End);
1793                                 } else if (current < last_month) {
1794                                         // otherwise it simply means we have a selection spaning
1795                                         // multiple months simply set rectangle inside the current month
1796                                         start_rect = GetDateRowRect (current, current);
1797                                         end_rect = GetDateRowRect (last_month, month_end);
1798                                 } else {
1799                                         // it's outside the visible range
1800                                         start_rect = GetDateRowRect (last_month, last_month.AddDays (1));
1801                                         end_rect = GetDateRowRect (last_month, range.End);
1802                                 }
1803                                 // push to the next month
1804                                 current = month_end.AddDays (1);
1805                                 // invalidate from the start row to the end row for this month                          
1806                                 this.Invalidate (
1807                                         new Rectangle (
1808                                                 start_rect.X,
1809                                                 start_rect.Y,
1810                                                 start_rect.Width,
1811                                                 Math.Max (end_rect.Bottom - start_rect.Y, 0)));
1812                                 }
1813                 } 
1814                 
1815                 // gets the rect of the row where the specified date appears on the specified month
1816                 private Rectangle GetDateRowRect (DateTime month, DateTime date) {
1817                         // first get the general rect of the supplied month
1818                         Size month_size = SingleMonthSize;
1819                         Rectangle month_rect = Rectangle.Empty;
1820                         for (int i=0; i < CalendarDimensions.Width*CalendarDimensions.Height; i++) {
1821                                 DateTime this_month = this.current_month.AddMonths (i);
1822                                 if (month.Year == this_month.Year && month.Month == this_month.Month) {
1823                                         month_rect = new Rectangle (
1824                                                 this.ClientRectangle.X + 1 + (month_size.Width * (i%CalendarDimensions.Width)) + (this.calendar_spacing.Width * (i%CalendarDimensions.Width)),
1825                                                 this.ClientRectangle.Y + 1 + (month_size.Height * (i/CalendarDimensions.Width)) + (this.calendar_spacing.Height * (i/CalendarDimensions.Width)),
1826                                                 month_size.Width,
1827                                                 month_size.Height);
1828                                                 break;          
1829                                 }
1830                         }
1831                         // now find out where in the month the supplied date is
1832                         if (month_rect == Rectangle.Empty) {
1833                                 return Rectangle.Empty;
1834                         }
1835                         // find out which row this date is in
1836                         int row = -1;
1837                         DateTime first_date = GetFirstDateInMonthGrid (month);
1838                         DateTime end_date = first_date.AddDays (7); 
1839                         for (int i=0; i < 6; i++) {
1840                                 if (date >= first_date && date < end_date) {
1841                                         row = i;
1842                                         break;
1843                                 }
1844                                 first_date = end_date;
1845                                 end_date = end_date.AddDays (7);
1846                         }
1847                         // ensure it's a valid row
1848                         if (row < 0) {
1849                                 return Rectangle.Empty;
1850                         }
1851                         int x_offset = (this.ShowWeekNumbers) ? date_cell_size.Width : 0;
1852                         int y_offset = title_size.Height + (date_cell_size.Height * (row + 1));
1853                         return new Rectangle (
1854                                 month_rect.X + x_offset,
1855                                 month_rect.Y + y_offset,
1856                                 date_cell_size.Width * 7,
1857                                 date_cell_size.Height);
1858                 }
1859
1860                 internal void Draw (Rectangle clip_rect, Graphics dc)
1861                 {
1862                         ThemeEngine.Current.DrawMonthCalendar (dc, clip_rect, this);
1863                 }
1864
1865                 #endregion      //internal methods
1866
1867                 #region internal drawing methods
1868
1869
1870                 #endregion      // internal drawing methods
1871
1872                 #region inner classes and enumerations
1873
1874                 // enumeration about what type of area on the calendar was hit 
1875                 public enum HitArea {
1876                         Nowhere,
1877                         TitleBackground,
1878                         TitleMonth,
1879                         TitleYear,
1880                         NextMonthButton,
1881                         PrevMonthButton,
1882                         CalendarBackground,
1883                         Date,
1884                         NextMonthDate,
1885                         PrevMonthDate,
1886                         DayOfWeek,
1887                         WeekNumbers,
1888                         TodayLink
1889                 }
1890                 
1891                 // info regarding to a hit test on this calendar
1892                 public sealed class HitTestInfo {
1893
1894                         private HitArea hit_area;
1895                         private Point point;
1896                         private DateTime time;
1897
1898                         // default constructor
1899                         internal HitTestInfo () {
1900                                 hit_area = HitArea.Nowhere;
1901                                 point = new Point (0, 0);
1902                                 time = DateTime.Now;
1903                         }
1904
1905                         // overload receives all properties
1906                         internal HitTestInfo (HitArea hit_area, Point point, DateTime time) {
1907                                 this.hit_area = hit_area;
1908                                 this.point = point;
1909                                 this.time = time;
1910                         }
1911
1912                         // the type of area that was hit
1913                         public HitArea HitArea {
1914                                 get {
1915                                         return hit_area;
1916                                 }
1917                         }
1918
1919                         // the point that is being test
1920                         public Point Point {
1921                                 get {
1922                                         return point;
1923                                 }
1924                         }
1925                         
1926                         // the date under the hit test point, only valid if HitArea is Date
1927                         public DateTime Time {
1928                                 get {
1929                                         return time;
1930                                 }
1931                         }
1932                 }
1933
1934                 #endregion      // inner classes
1935         }
1936 }