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