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