2008-03-27 Carlos Alberto Cortez <calberto.cortez@gmail.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / DateTimePicker.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-2005 Novell, Inc.
21 //
22 // Authors:
23 //      John BouAntoun  jba-mono@optusnet.com.au
24 //      Rolf Bjarne Kvinge      rolfkvinge@ya.com
25 //
26 // TODO:
27 //              - wire in all events from monthcalendar
28
29
30 using System;
31 using System.Drawing;
32 using System.Collections;
33 using System.ComponentModel;
34 using System.Globalization;
35 using System.Runtime.InteropServices;
36 using System.Windows.Forms;
37
38 namespace System.Windows.Forms {
39 #if NET_2_0
40         [ClassInterface (ClassInterfaceType.AutoDispatch)]
41         [DefaultBindingProperty ("Value")]
42         [ComVisible (true)]
43 #endif
44         [DefaultEvent("ValueChanged")]
45         [DefaultProperty("Value")]
46         [Designer("System.Windows.Forms.Design.DateTimePickerDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
47         public class DateTimePicker : Control {
48
49                 #region Public variables
50                 
51                 // this class has to have the specified hour, minute and second, as it says in msdn
52 #if NET_2_0
53                 [EditorBrowsable (EditorBrowsableState.Never)]
54                 [Browsable (false)]
55 #endif
56                 public static readonly DateTime MaxDateTime = new DateTime (9998, 12, 31, 0, 0, 0);
57 #if NET_2_0
58                 [EditorBrowsable (EditorBrowsableState.Never)]
59                 [Browsable (false)]
60 #endif
61                 public static readonly DateTime MinDateTime = new DateTime (1753, 1, 1);
62
63                 internal const int check_box_size = 13;
64                 internal const int check_box_space = 4;
65                 
66                 #endregion      // Public variables
67                 
68                 #region Local variables
69                 
70                 protected static readonly Color DefaultMonthBackColor = ThemeEngine.Current.ColorWindow;
71                 protected static readonly Color DefaultTitleBackColor = ThemeEngine.Current.ColorActiveCaption;
72                 protected static readonly Color DefaultTitleForeColor = ThemeEngine.Current.ColorActiveCaptionText;
73                 protected static readonly Color DefaultTrailingForeColor = SystemColors.GrayText;
74                 
75                 internal MonthCalendar                  month_calendar;
76                 bool                                                    is_checked;
77                 string                                                  custom_format;
78                 LeftRightAlignment                              drop_down_align;
79                 DateTimePickerFormat                    format;
80                 DateTime                                                max_date;
81                 DateTime                                                min_date;
82                 bool                                                    show_check_box;
83                 bool                                                    show_up_down;
84                 DateTime                                                date_value;
85 #if NET_2_0
86                 bool                                                    right_to_left_layout;
87 #endif
88                 // variables used for drawing and such
89                 internal const int                                      up_down_width = check_box_size;
90                 internal bool                                   is_drop_down_visible;
91                 internal bool                                           is_up_pressed;
92                 internal bool                                           is_down_pressed;
93                 internal Timer                                          updown_timer;
94                 internal const int                                      initial_timer_delay = 500;
95                 internal const int                                      subsequent_timer_delay = 100;
96                 internal bool                                           is_checkbox_selected;
97
98                 // variables for determining how to format the string
99                 internal PartData[]                                     part_data;
100
101                 #endregion      // Local variables
102                 
103                 #region DateTimePickerAccessibleObject Subclass
104                 [ComVisible(true)]
105                 public class DateTimePickerAccessibleObject : ControlAccessibleObject {
106                         #region DateTimePickerAccessibleObject Local Variables
107                         private new DateTimePicker      owner;
108                         #endregion      // DateTimePickerAccessibleObject Local Variables
109
110                         #region DateTimePickerAccessibleObject Constructors
111                         public DateTimePickerAccessibleObject(DateTimePicker owner) : base(owner) {
112                                 this.owner = owner;
113                         }
114                         #endregion      // DateTimePickerAccessibleObject Constructors
115
116                         #region DateTimePickerAccessibleObject Properties
117 #if NET_2_0
118                         public override string KeyboardShortcut {
119                                 get {
120                                         return base.KeyboardShortcut;
121                                 }
122                         }
123
124                         public override AccessibleRole Role {
125                                 get {
126                                         return base.Role;
127                                 }
128                         }
129 #endif
130                         public override AccessibleStates State {
131                                 get {
132                                         AccessibleStates        retval;
133
134                                         retval = AccessibleStates.Default;
135
136                                         if (owner.Checked) {
137                                                 retval |= AccessibleStates.Checked;
138                                         }
139
140                                         return retval;
141                                 }
142                         }
143
144                         public override string Value {
145                                 get {
146                                         return owner.Text;
147                                 }
148                         }
149                         #endregion      // DateTimePickerAccessibleObject Properties
150                 }
151                 #endregion      // DateTimePickerAccessibleObject Sub-class
152
153                 #region public constructors
154                 
155                 // only public constructor
156                 public DateTimePicker () {
157                 
158                         // initialise the month calendar
159                         month_calendar = new MonthCalendar (this);
160                         month_calendar.CalendarDimensions = new Size (1, 1);
161                         month_calendar.MaxSelectionCount = 1;
162                         month_calendar.ForeColor = Control.DefaultForeColor;
163                         month_calendar.BackColor = DefaultMonthBackColor;
164                         month_calendar.TitleBackColor = DefaultTitleBackColor;
165                         month_calendar.TitleForeColor = DefaultTitleForeColor;
166                         month_calendar.TrailingForeColor = DefaultTrailingForeColor;
167                         month_calendar.Visible = false;
168                         // initialize the timer
169                         updown_timer = new Timer();
170                         updown_timer.Interval = initial_timer_delay;
171
172                         
173                         // initialise other variables
174                         is_checked = true;
175                         custom_format = null;
176                         drop_down_align = LeftRightAlignment.Left;
177                         format = DateTimePickerFormat.Long;
178                         max_date = MaxDateTime;
179                         min_date = MinDateTime;
180                         show_check_box = false;
181                         show_up_down = false;
182                         date_value = DateTime.Now;
183                                                 
184                         is_drop_down_visible = false;
185                         BackColor = SystemColors.Window;
186                         ForeColor = SystemColors.WindowText;
187                         
188                         month_calendar.DateChanged += new DateRangeEventHandler (MonthCalendarDateChangedHandler);
189                         month_calendar.DateSelected += new DateRangeEventHandler (MonthCalendarDateSelectedHandler);
190                         month_calendar.LostFocus += new EventHandler (MonthCalendarLostFocusHandler);
191                         updown_timer.Tick += new EventHandler (UpDownTimerTick);
192                         KeyPress += new KeyPressEventHandler (KeyPressHandler);
193                         KeyDown += new KeyEventHandler (KeyDownHandler);
194                         LostFocus += new EventHandler (LostFocusHandler);
195                         MouseDown += new MouseEventHandler (MouseDownHandler);                  
196                         MouseUp += new MouseEventHandler (MouseUpHandler);
197                         Paint += new PaintEventHandler (PaintHandler);
198                         Resize += new EventHandler (ResizeHandler);
199                         SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick, false);
200                         SetStyle (ControlStyles.FixedHeight, true);
201                         SetStyle (ControlStyles.Selectable, true);
202
203                         CalculateFormats ();
204                 }
205                 
206                 #endregion
207                 
208                 #region public properties
209                 
210                 [Browsable(false)]
211                 [EditorBrowsable(EditorBrowsableState.Never)]
212                 public override Color BackColor {
213                         set {
214                                 base.BackColor = value;
215                         }
216                         get {
217                                 return base.BackColor;
218                         }
219                 }
220                 
221                 [Browsable(false)]
222                 [EditorBrowsable(EditorBrowsableState.Never)]
223                 public override Image BackgroundImage {
224                         set {
225                                 base.BackgroundImage = value;
226                         }
227                         get {
228                                 return base.BackgroundImage;
229                         }
230                 }
231
232 #if NET_2_0
233                 [Browsable (false)]
234                 [EditorBrowsable (EditorBrowsableState.Never)]
235                 public override ImageLayout BackgroundImageLayout {
236                         get{
237                                 return base.BackgroundImageLayout;
238                         }
239                         set {
240                                 base.BackgroundImageLayout = value;
241                         }
242                 }
243 #endif
244
245                 [AmbientValue(null)]
246                 [Localizable(true)]
247                 public Font CalendarFont {
248                         set {
249                                 month_calendar.Font = value;
250                         }
251                         get {
252                                 return month_calendar.Font;
253                         }
254                 }
255
256                 public Color CalendarForeColor {
257                         set {
258                                 month_calendar.ForeColor = value;
259                         }
260                         get {
261                                 return month_calendar.ForeColor;
262                         }
263                 }
264
265                 public Color CalendarMonthBackground {
266                         set {
267                                 month_calendar.BackColor = value;
268                         }
269                         get {
270                                 return month_calendar.BackColor;
271                         }
272                 }
273
274                 public Color CalendarTitleBackColor {
275                         set {
276                                 month_calendar.TitleBackColor = value;
277                         }
278                         get {
279                                 return month_calendar.TitleBackColor;
280                         }
281                 }
282
283                 public Color CalendarTitleForeColor {
284                         set {
285                                 month_calendar.TitleForeColor = value;
286                         }
287                         get {
288                                 return month_calendar.TitleForeColor;
289                         }
290                 }
291
292                 public Color CalendarTrailingForeColor {
293                         set {
294                                 month_calendar.TrailingForeColor = value;
295                         }
296                         get {
297                                 return month_calendar.TrailingForeColor;
298                         }
299                 }
300                 
301                 // when checked the value is grayed out
302                 [Bindable(true)]
303                 [DefaultValue(true)]
304                 public bool Checked {
305                         set {
306                                 if (is_checked != value) {
307                                         is_checked = value;
308                                         // invalidate the value inside this control
309                                         if (ShowCheckBox) {
310                                                 for (int i = 0; i < part_data.Length; i++)
311                                                         part_data [i].is_selected = false;
312                                                 Invalidate (date_area_rect);
313                                         }
314                                 }
315                         }
316                         get {
317                                 return is_checked;
318                         }
319                 }
320                 
321                 // the custom format string to format this control with
322 #if NET_2_0
323                 [Localizable (true)]
324 #endif
325                 [DefaultValue(null)]
326                 [RefreshProperties(RefreshProperties.Repaint)]
327                 public string CustomFormat {
328                         set {
329                                 if (custom_format != value) {
330                                         custom_format = value;
331                                         if (this.Format == DateTimePickerFormat.Custom) {
332                                                 CalculateFormats ();
333                                         }
334                                 }
335                         }
336                         get {
337                                 return custom_format;
338                         }
339                 }
340                 
341 #if NET_2_0
342                 [EditorBrowsable (EditorBrowsableState.Never)]
343                 protected override bool DoubleBuffered {
344                         get {
345                                 return base.DoubleBuffered;
346                         }
347                         set {
348                                 base.DoubleBuffered = value;
349                         }
350                 }
351 #endif
352
353                 // which side the drop down is to be aligned on
354                 [DefaultValue(LeftRightAlignment.Left)]
355                 [Localizable(true)]
356                 public LeftRightAlignment DropDownAlign {
357                         set {
358                                 if (drop_down_align != value) {
359                                         drop_down_align = value;
360                                 }
361                         }
362                         get {
363                                 return drop_down_align;
364                         }
365                 }
366
367                 [Browsable(false)]
368                 [EditorBrowsable(EditorBrowsableState.Never)]
369                 public override Color ForeColor {
370                         set {
371                                 base.ForeColor = value;
372                         }
373                         get {
374                                 return base.ForeColor;
375                         }
376                 }
377                 
378                 // the format of the date time picker text, default is long
379                 [RefreshProperties(RefreshProperties.Repaint)]
380                 public DateTimePickerFormat Format {
381                         set {
382                                 if (format != value) {
383                                         format = value;
384                                         RecreateHandle (); // MS recreates the handle on every format change.
385                                         CalculateFormats ();
386                                         this.OnFormatChanged (EventArgs.Empty);
387                                         // invalidate the value inside this control
388                                         this.Invalidate (date_area_rect);
389                                 }
390                         }
391                         get {
392                                 return format;
393                         }
394                 }
395                 
396                 public DateTime MaxDate {
397                         set {
398                                 if (value < min_date) {
399                                         string msg = string.Format (CultureInfo.CurrentCulture,
400                                                 "'{0}' is not a valid value for 'MaxDate'. 'MaxDate' "
401                                                 + "must be greater than or equal to MinDate.",
402                                                 value.ToString ("G"));
403 #if NET_2_0
404                                         throw new ArgumentOutOfRangeException ("MaxDate", msg);
405 #else
406                                         throw new ArgumentException (msg);
407 #endif
408                                 }
409                                 if (value > MaxDateTime) {
410                                         string msg = string.Format (CultureInfo.CurrentCulture,
411                                                 "DateTimePicker does not support dates after {0}.",
412                                                 MaxDateTime.ToString ("G", CultureInfo.CurrentCulture));
413 #if NET_2_0
414                                         throw new ArgumentOutOfRangeException ("MaxDate", msg);
415 #else
416                                         throw new ArgumentException (msg, "value");
417 #endif
418                                 }
419                                 if (max_date != value) {
420                                         max_date = value;
421                                         if (Value > max_date) {
422                                                 Value = max_date;
423                                                 // invalidate the value inside this control
424                                                 this.Invalidate (date_area_rect);
425                                         }
426                                 }
427                         }
428                         get {
429                                 return max_date;
430                         }
431                 }
432                 
433 #if NET_2_0
434                 public static DateTime MaximumDateTime {
435                         get {
436                                 return MaxDateTime;
437                         }
438                 }
439 #endif
440                 
441                 public DateTime MinDate {
442                         set {
443                                 // If the user tries to set DateTime.MinValue, fix it to
444                                 // DateTimePicker's minimum.
445                                 if (value == DateTime.MinValue)
446                                         value = MinDateTime;
447                                         
448 #if NET_2_0
449                                 if (value > MaxDate) {
450 #else
451                                 if (value >= MaxDate) {
452 #endif
453                                         string msg = string.Format (CultureInfo.CurrentCulture,
454                                                 "'{0}' is not a valid value for 'MinDate'. 'MinDate' "
455                                                 + "must be less than MaxDate.",
456                                                 value.ToString ("G"));
457 #if NET_2_0
458                                         throw new ArgumentOutOfRangeException ("MinDate", msg);
459 #else
460                                         throw new ArgumentException (msg);
461 #endif
462                                 }
463                                 if (value < MinDateTime) {
464                                         string msg = string.Format (CultureInfo.CurrentCulture,
465                                                 "DateTimePicker does not support dates before {0}.",
466                                                 MinDateTime.ToString ("G", CultureInfo.CurrentCulture));
467 #if NET_2_0
468                                         throw new ArgumentOutOfRangeException ("MinDate", msg);
469 #else
470                                         throw new ArgumentException (msg, "value");
471 #endif
472
473                                 }
474                                 if (min_date != value) {
475                                         min_date = value;
476                                         if (Value < min_date) {
477                                                 Value = min_date;
478                                                 // invalidate the value inside this control
479                                                 this.Invalidate (date_area_rect);
480                                         }
481                                 }
482                         }
483                         get {
484                                 return min_date;
485                         }
486                 }
487 #if NET_2_0
488                 public static DateTime MinimumDateTime {
489                         get {
490                                 return MinDateTime;
491                         }
492                 }
493 #endif
494 #if NET_2_0
495                 [EditorBrowsable (EditorBrowsableState.Never)]
496                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
497                 [Browsable (false)]
498                 public new Padding Padding {
499                         get { return base.Padding; }
500                         set { base.Padding = value; }
501                 }
502 #endif
503
504                 // the prefered height to draw this control using current font
505                 [Browsable(false)]
506                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
507                 public int PreferredHeight {
508                         get {
509                                 // Make it proportional
510                                 return (int) Math.Ceiling (Font.Height * 1.5);
511                         }
512                 }
513
514 #if NET_2_0
515                 [DefaultValue (false)]
516                 [Localizable (true)]
517                 public virtual bool RightToLeftLayout {
518                         get {
519                                 return right_to_left_layout;
520                         }
521                         set {
522                                 if (right_to_left_layout != value) {
523                                         right_to_left_layout = value;
524                                         OnRightToLeftLayoutChanged (EventArgs.Empty);
525                                 }
526                         }
527                 }
528 #endif
529
530                 // whether or not the check box is shown
531                 [DefaultValue(false)]
532                 public bool ShowCheckBox {
533                         set {
534                                 if (show_check_box != value) {
535                                         show_check_box = value;
536                                         // invalidate the value inside this control
537                                         this.Invalidate (date_area_rect);
538                                 }
539                         }
540                         get {
541                                 return show_check_box;
542                         }
543                 }
544                 
545                 // if true show the updown control, else popup the monthcalendar
546                 [DefaultValue(false)]
547                 public bool ShowUpDown {
548                         set {
549                                 if (show_up_down != value) {
550                                         show_up_down = value;
551                                         // need to invalidate the whole control
552                                         this.Invalidate ();
553                                 }
554                         }
555                         get {
556                                 return show_up_down;
557                         }
558                 }
559
560                 [Browsable(false)]
561                 [EditorBrowsable(EditorBrowsableState.Advanced)]
562                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
563                 public override string Text {
564                         set {
565                                 DateTime parsed_value;
566                                 
567                                 if (value == null || value == string.Empty) {
568                                         date_value = DateTime.Now;
569                                         OnValueChanged (EventArgs.Empty);
570                                         OnTextChanged (EventArgs.Empty);
571                                         return;
572                                 }
573                                 
574                                 if (format == DateTimePickerFormat.Custom) {
575                                         // TODO: if the format is a custom format we need to do a custom parse here
576                                         // This implementation will fail if the custom format is set to something that can
577                                         // be a standard datetime format string
578                                         // http://msdn2.microsoft.com/en-us/library/az4se3k1.aspx
579                                         parsed_value = DateTime.ParseExact (value, GetExactFormat (), null);
580                                 } else {
581                                         parsed_value = DateTime.ParseExact (value, GetExactFormat (), null);
582                                 }
583                                 
584                                 if (date_value != parsed_value) {
585                                         Value = parsed_value;
586                                 }
587                         }
588                         get {
589                                 if (!IsHandleCreated)
590                                         return "";
591                                 
592                                 if (format == DateTimePickerFormat.Custom) {
593                                         System.Text.StringBuilder result = new System.Text.StringBuilder ();
594                                         for (int i = 0; i < part_data.Length; i++) { 
595                                                 result.Append(part_data[i].GetText(date_value));
596                                         }
597                                         return result.ToString ();
598                                 } else {
599                                         return Value.ToString (GetExactFormat ());
600                                 }
601                         }
602                 }       
603
604                 [Bindable(true)]
605                 [RefreshProperties(RefreshProperties.All)]
606                 public DateTime Value {
607                         set {
608                                 if (date_value != value) {
609                                         if (value < MinDate || value > MaxDate)
610 #if NET_2_0
611                                                 throw new ArgumentOutOfRangeException ("value", "value must be between MinDate and MaxDate");
612 #else
613                                                 throw new ArgumentException ("value", "value must be between MinDate and MaxDate");
614 #endif
615
616                                         date_value = value;
617                                         this.OnValueChanged (EventArgs.Empty);
618                                         this.Invalidate (date_area_rect);
619                                 }
620                         }
621                         get {
622                                 return date_value;
623                         }                       
624                 }
625
626                 #endregion      // public properties
627                 
628                 #region public methods
629                 
630                 // just return the text value
631                 public override string ToString () {
632                         return this.Text;
633                 } 
634                                 
635                 #endregion      // public methods
636                 
637                 #region public events
638                 static object CloseUpEvent = new object ();
639                 static object DropDownEvent = new object ();
640                 static object FormatChangedEvent = new object ();
641                 static object ValueChangedEvent = new object ();
642 #if NET_2_0
643                 static object RightToLeftLayoutChangedEvent = new object ();
644 #endif
645
646                 // raised when the monthcalendar is closed
647                 public event EventHandler CloseUp {
648                         add { Events.AddHandler (CloseUpEvent, value); }
649                         remove { Events.RemoveHandler (CloseUpEvent, value); }
650                 }
651                 
652                 // raised when the monthcalendar is opened
653                 public event EventHandler DropDown {
654                         add { Events.AddHandler (DropDownEvent, value); }
655                         remove { Events.RemoveHandler (DropDownEvent, value); }
656                 }
657                 
658                 // raised when the format of the value is changed
659                 public event EventHandler FormatChanged {
660                         add { Events.AddHandler (FormatChangedEvent, value); }
661                         remove { Events.RemoveHandler (FormatChangedEvent, value); }
662                 }
663                 
664                 // raised when the date Value is changed
665                 public event EventHandler ValueChanged {
666                         add { Events.AddHandler (ValueChangedEvent, value); }
667                         remove { Events.RemoveHandler (ValueChangedEvent, value); }
668                 }
669
670                 [Browsable(false)]
671                 [EditorBrowsable(EditorBrowsableState.Never)]
672                 public new event EventHandler BackColorChanged {
673                         add {
674                                 base.BackColorChanged += value;
675                         }
676
677                         remove {
678                                 base.BackColorChanged -= value;
679                         }
680                 }
681
682                 [Browsable(false)]
683                 [EditorBrowsable(EditorBrowsableState.Never)]
684                 public new event EventHandler BackgroundImageChanged {
685                         add {
686                                 base.BackgroundImageChanged += value;
687                         }
688
689                         remove {
690                                 base.BackgroundImageChanged -= value;
691                         }
692                 }
693 #if NET_2_0
694                 [Browsable (false)]
695                 [EditorBrowsable (EditorBrowsableState.Never)]
696                 public new event EventHandler BackgroundImageLayoutChanged {
697                         add
698                         {
699                                 base.BackgroundImageLayoutChanged += value;
700                         }
701
702                         remove
703                         {
704                                 base.BackgroundImageLayoutChanged -= value;
705                         }
706                 }
707                 
708                 [Browsable (false)]
709                 [EditorBrowsable (EditorBrowsableState.Never)]
710                 public new event EventHandler Click {
711                         add {
712                                 base.Click += value;
713                         }
714                         remove {
715                                 base.Click -= value;
716                         }
717                 }
718                 
719                 [Browsable (false)]
720                 [EditorBrowsable (EditorBrowsableState.Never)]
721                 public new event EventHandler DoubleClick {
722                         add {
723                                 base.DoubleClick += value;
724                         }
725                         remove {
726                                 base.DoubleClick -= value;
727                         }
728                 }
729 #endif
730
731                 [Browsable(false)]
732                 [EditorBrowsable(EditorBrowsableState.Never)]
733                 public new event EventHandler ForeColorChanged {
734                         add {
735                                 base.ForeColorChanged += value;
736                         }
737
738                         remove {
739                                 base.ForeColorChanged -= value;
740                         }
741                 }
742 #if NET_2_0
743                 [Browsable (false)]
744                 [EditorBrowsable (EditorBrowsableState.Never)]
745                 public new event MouseEventHandler MouseClick {
746                         add {
747                                 base.MouseClick += value;
748                         }
749                         remove {
750                                 base.MouseClick -= value;
751                         }
752                 }
753                 
754                 [Browsable (false)]
755                 [EditorBrowsable (EditorBrowsableState.Never)]
756                 public new event MouseEventHandler MouseDoubleClick {
757                         add {
758                                 base.MouseDoubleClick += value;
759                         }
760                         remove {
761                                 base.MouseDoubleClick -= value;
762                         }
763                 }
764
765                 [Browsable (false)]
766                 [EditorBrowsable (EditorBrowsableState.Never)]
767                 public new event EventHandler PaddingChanged {
768                         add
769                         {
770                                 base.PaddingChanged += value;
771                         }
772                         remove
773                         {
774                                 base.PaddingChanged -= value;
775                         }
776                 }
777 #endif
778
779                 [Browsable(false)]
780                 [EditorBrowsable(EditorBrowsableState.Never)]
781                 public new event PaintEventHandler Paint {
782                         add {
783                                 base.Paint += value;
784                         }
785
786                         remove {
787                                 base.Paint -= value;
788                         }
789                 }
790 #if NET_2_0
791                 public event EventHandler RightToLeftLayoutChanged {
792                         add {
793                                 Events.AddHandler (RightToLeftLayoutChangedEvent, value);
794                         }
795                         remove {
796                                 Events.RemoveHandler (RightToLeftLayoutChangedEvent, value);
797                         }
798                 }
799 #endif
800
801                 [Browsable(false)]
802                 [EditorBrowsable(EditorBrowsableState.Advanced)]
803                 public new event EventHandler TextChanged {
804                         add {
805                                 base.TextChanged += value;
806                         }
807
808                         remove {
809                                 base.TextChanged -= value;
810                         }
811                 }
812                 #endregion      // public events
813                 
814                 #region protected properties
815
816                 // not sure why we're overriding this one               
817                 protected override CreateParams CreateParams {
818                         get {
819                                 return base.CreateParams;
820                         }
821                 }
822                 
823                 // specify the default size for this control
824                 protected override Size DefaultSize {
825                         get {
826                                 // todo actually measure this properly
827                                 return new Size (200, PreferredHeight);
828                         }
829                 }
830                 
831                 #endregion      // protected properties
832                 
833                 #region protected methods
834                 
835                 // not sure why we're overriding this one
836                 protected override AccessibleObject CreateAccessibilityInstance () {
837                         return base.CreateAccessibilityInstance ();
838                 }
839                 
840                 // not sure why we're overriding this one
841                 protected override void CreateHandle () {
842                         base.CreateHandle ();
843                 }
844                 
845                 // not sure why we're overriding this one
846                 protected override void DestroyHandle () {
847                         base.DestroyHandle ();
848                 }
849
850 #if !NET_2_0
851                 // not sure why we're overriding this one
852                 protected override void Dispose (bool disposing) {
853                         updown_timer.Dispose ();
854                         base.Dispose (disposing);
855                 }
856 #endif
857
858                 // find out if this key is an input key for us, depends on which date part is focused
859                 protected override bool IsInputKey (Keys keyData) {
860                         switch (keyData)
861                         {
862                                 case Keys.Up:
863                                 case Keys.Down:
864                                 case Keys.Left:
865                                 case Keys.Right:
866                                         return true;
867                         }
868                         return false;
869                 }
870                 
871                 // raises the CloseUp event
872                 protected virtual void OnCloseUp (EventArgs eventargs) {
873                         EventHandler eh = (EventHandler)(Events [CloseUpEvent]);
874                         if (eh != null)
875                                 eh (this, eventargs);
876                 }
877                 
878                 // raise the drop down event
879                 protected virtual void OnDropDown (EventArgs eventargs) {
880                         EventHandler eh = (EventHandler)(Events [DropDownEvent]);
881                         if (eh != null)
882                                 eh (this, eventargs);
883                 }
884
885                 protected override void OnFontChanged(EventArgs e) {
886                         // FIXME - do we need to update/invalidate/recalc our stuff?
887                         month_calendar.Font = Font;
888                         Size = new Size (Size.Width, PreferredHeight);
889
890                         base.OnFontChanged (e);
891                 }
892                 
893                 // raises the format changed event
894                 protected virtual void OnFormatChanged (EventArgs e) {
895                         EventHandler eh = (EventHandler)(Events [FormatChangedEvent]);
896                         if (eh != null)
897                                 eh (this, e);
898                 }
899 #if NET_2_0
900                 protected override void  OnHandleCreated (EventArgs e) {
901                          base.OnHandleCreated(e);
902                 }
903                 protected override void  OnHandleDestroyed (EventArgs e) {
904                          base.OnHandleDestroyed(e);
905                 }
906                 
907                 [EditorBrowsable (EditorBrowsableState.Advanced)]
908                 protected virtual void OnRightToLeftLayoutChanged (EventArgs e) {
909                         EventHandler eh = (EventHandler) Events [RightToLeftLayoutChangedEvent];
910                         if (eh != null)
911                                 eh (this, e);
912                 }
913 #endif
914                 // not sure why we're overriding this one 
915                 protected override void OnSystemColorsChanged (EventArgs e) {
916                         base.OnSystemColorsChanged (e);
917                 }
918                 
919                 // raise the ValueChanged event
920                 protected virtual void OnValueChanged (EventArgs eventargs) {
921                         EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
922                         if (eh != null)
923                                 eh (this, eventargs);
924                 }
925                 
926                 // overridden to set the bounds of this control properly
927                 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
928                         // TODO: ensure I implemented the bounds core setting properly.
929                         if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height ||
930                                 (specified & BoundsSpecified.Size) == BoundsSpecified.Size)  {
931                                 base.SetBoundsCore (x, y, width, DefaultSize.Height, specified);
932                         } else {
933                                 base.SetBoundsCore (x, y, width, height, specified);
934                         }
935                         
936                         // need to set the rectangles for all the support internal rects
937                         // this is done here as a optimisation since this is an array of rects
938                         if ((specified & BoundsSpecified.X) == BoundsSpecified.X ||
939                                 (specified & BoundsSpecified.Y) == BoundsSpecified.Y) {
940                                 // TODO set up all the datepart rects
941                         }
942                 }
943
944                 // not sure why we're overriding this
945                 protected override void WndProc (ref Message m) {
946                         base.WndProc (ref m);
947                 }
948                 
949                 #endregion      // protected methods
950                 
951                 #region internal / private properties
952                 
953                 // this is the region that the date and the check box is drawn on
954                 internal Rectangle date_area_rect {
955                         get {
956                                 Rectangle rect = this.ClientRectangle;
957                                 if (ShowUpDown) {
958                                         // set the space to the left of the up/down button
959                                         if (rect.Width > (up_down_width + 4)) {
960                                                 rect.Width -= (up_down_width + 4);
961                                         } else {
962                                                 rect.Width = 0;
963                                         }
964                                 } else {
965                                         // set the space to the left of the up/down button
966                                         // TODO make this use up down button
967                                         if (rect.Width > (SystemInformation.VerticalScrollBarWidth + 4)) {
968                                                 rect.Width -= SystemInformation.VerticalScrollBarWidth;
969                                         } else {
970                                                 rect.Width = 0;
971                                         }
972                                 }
973                                 
974                                 rect.Inflate (-2, -2);
975                                 return rect;
976                         }
977                 }
978
979                 internal Rectangle CheckBoxRect {
980                         get {
981                                 Rectangle retval = new Rectangle (check_box_space, ClientSize.Height / 2 - check_box_size / 2, 
982                                                 check_box_size, check_box_size);
983                                 return retval;
984                         }
985                 }
986                 
987                 // the rectangle for the drop down arrow
988                 internal Rectangle drop_down_arrow_rect {
989                         get {
990                                 Rectangle rect = this.ClientRectangle;
991                                 rect.X = rect.Right - SystemInformation.VerticalScrollBarWidth - 2;
992                                 if (rect.Width > (SystemInformation.VerticalScrollBarWidth + 2)) {
993                                         rect.Width = SystemInformation.VerticalScrollBarWidth;
994                                 } else {
995                                         rect.Width = Math.Max (rect.Width - 2, 0);
996                                 }
997                                 
998                                 rect.Inflate (0, -2);
999                                 return rect;
1000                         }
1001                 }
1002                 
1003                 // the part of the date that is currently hilighted
1004                 internal Rectangle hilight_date_area {
1005                         get {
1006                                 // TODO: put hilighted part calculation in here
1007                                 return Rectangle.Empty;
1008                         }
1009                 }       
1010                         
1011                 #endregion
1012                 
1013                 #region internal / private methods
1014
1015                 private void ResizeHandler (object sender, EventArgs e)
1016                 {
1017                         Invalidate ();
1018                 }
1019
1020                 private void UpDownTimerTick (object sender, EventArgs e)
1021                 {
1022                         if (updown_timer.Interval == initial_timer_delay)
1023                                 updown_timer.Interval = subsequent_timer_delay;
1024
1025                         if (is_down_pressed)
1026                                 IncrementSelectedPart (-1);
1027                         else if (is_up_pressed)
1028                                 IncrementSelectedPart (1);
1029                         else
1030                                 updown_timer.Enabled = false;
1031                 }
1032                 
1033                 // calculates the maximum width 
1034                 internal Single CalculateMaxWidth(string format, Graphics gr, StringFormat string_format)
1035                 {
1036                         SizeF size;
1037                         float result = 0;
1038                         string text;
1039                         Font font = this.Font;
1040
1041                         switch (format)
1042                         {
1043                                 case "M":
1044                                 case "MM":
1045                                 case "MMM":
1046                                 case "MMMM":
1047                                         for (int i = 1; i <= 12; i++) {
1048                                                 text = PartData.GetText (Value.AddMonths (i), format);
1049                                                 size = gr.MeasureString (text, font, int.MaxValue, string_format);
1050                                                 result = Math.Max (result, size.Width);
1051                                         }
1052                                         return result;
1053                                 case "d":
1054                                 case "dd":
1055                                 case "ddd":
1056                                 case "dddd":
1057                                         for (int i = 1; i <= 12; i++) {
1058                                                 text = PartData.GetText (Value.AddDays (i), format);
1059                                                 size = gr.MeasureString (text, font, int.MaxValue, string_format);
1060                                                 result = Math.Max (result, size.Width);
1061                                         }
1062                                         return result;
1063                                 case "h":
1064                                 case "hh":
1065                                         for (int i = 1; i <= 12; i++) {
1066                                                 text = PartData.GetText (Value.AddHours (i), format);
1067                                                 size = gr.MeasureString (text, font, int.MaxValue, string_format);
1068                                                 result = Math.Max (result, size.Width);
1069                                         }
1070                                         return result;
1071                                 case "H":
1072                                 case "HH":
1073                                         for (int i = 1; i <= 24; i++) {
1074                                                 text = PartData.GetText (Value.AddDays (i), format);
1075                                                 size = gr.MeasureString (text, font, int.MaxValue, string_format);
1076                                                 result = Math.Max (result, size.Width);
1077                                         }
1078                                         return result;
1079                                 case "m":
1080                                 case "mm":
1081                                         for (int i = 1; i <= 60; i++) {
1082                                                 text = PartData.GetText (Value.AddMinutes (i), format);
1083                                                 size = gr.MeasureString (text, font, int.MaxValue, string_format);
1084                                                 result = Math.Max (result, size.Width);
1085                                         }
1086                                         return result;
1087                                 case "s":
1088                                 case "ss":
1089                                         for (int i = 1; i <= 60; i++) {
1090                                                 text = PartData.GetText (Value.AddSeconds (i), format);
1091                                                 size = gr.MeasureString (text, font, int.MaxValue, string_format);
1092                                                 result = Math.Max (result, size.Width);
1093                                         }
1094                                         return result;
1095                                 case "t":
1096                                 case "tt":
1097                                         for (int i = 1; i <= 2; i++) {
1098                                                 text = PartData.GetText (Value.AddHours (i * 12), format);
1099                                                 size = gr.MeasureString (text, font, int.MaxValue, string_format);
1100                                                 result = Math.Max (result, size.Width);
1101                                         }
1102                                         return result;
1103                                 case "y":
1104                                 case "yy":
1105                                 case "yyyy":
1106                                         for (int i = 1; i <= 10; i++) {
1107                                                 text = PartData.GetText (Value.AddYears (i), format);
1108                                                 size = gr.MeasureString (text, font, int.MaxValue, string_format);
1109                                                 result = Math.Max (result, size.Width);
1110                                         }
1111                                         return result;
1112                                 default:
1113                                         return gr.MeasureString (format, font, int.MaxValue, string_format).Width;
1114                         }
1115                 }
1116
1117                 // returns the format of the date as a string 
1118                 // (i.e. resolves the Format enum values to it's corresponding string format)
1119                 // Why CurrentCulture and not CurrentUICulture is explained here:
1120                 // http://blogs.msdn.com/michkap/archive/2007/01/11/1449754.aspx
1121                 private string GetExactFormat()
1122                 {
1123                         switch (this.format) {
1124                         case DateTimePickerFormat.Long:
1125                                 return Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.LongDatePattern;
1126                         case DateTimePickerFormat.Short:
1127                                 return Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern;
1128                         case DateTimePickerFormat.Time:
1129                                 return Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.LongTimePattern;
1130                         case DateTimePickerFormat.Custom:
1131                                 return this.custom_format == null ? String.Empty : this.custom_format;
1132                         default:
1133                                 return Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat.LongDatePattern;
1134                         }
1135                 }
1136
1137                 private void CalculateFormats()
1138                 {
1139                         string real_format;
1140                         System.Text.StringBuilder literal = new System.Text.StringBuilder ();
1141                         System.Collections.ArrayList formats = new ArrayList ();
1142                         bool is_literal = false;
1143                         char lastch = (char) 0;
1144                         char ch;
1145
1146                         real_format = GetExactFormat ();
1147
1148                         // parse the format string
1149                         for (int i = 0; i < real_format.Length; i++)
1150                         {
1151                                 ch = real_format [i];
1152
1153                                 if (is_literal && ch != '\'')
1154                                 {
1155                                         literal.Append (ch);
1156                                         continue;
1157                                 }
1158
1159                                 switch (ch)
1160                                 {
1161                                         case 't':
1162                                         case 'd':
1163                                         case 'h':
1164                                         case 'H':
1165                                         case 'm':
1166                                         case 'M':
1167                                         case 's':
1168                                         case 'y':
1169                                         case 'g': // Spec says nothing about g, but it seems to be treated like spaces.
1170                                                 if (!(lastch == ch || lastch == 0) && literal.Length != 0)
1171                                                 {
1172                                                         formats.Add (new PartData(literal.ToString (), false));
1173                                                         literal.Length = 0;
1174                                                 }
1175                                                 literal.Append (ch);
1176                                                 break;
1177                                         case '\'':
1178                                                 if (is_literal && i < real_format.Length - 1 && real_format [i + 1] == '\'') {
1179                                                         literal.Append (ch);
1180                                                         i++;
1181                                                         break;
1182                                                 }
1183                                                 if (literal.Length == 0) {
1184                                                         is_literal = !is_literal;
1185                                                         break;
1186                                                 }
1187                                                 formats.Add (new PartData (literal.ToString (), is_literal));
1188                                                 literal.Length = 0;
1189                                                 is_literal = !is_literal;
1190                                                 break;
1191                                         default:
1192                                                 if (literal.Length != 0)
1193                                                 {
1194                                                         formats.Add (new PartData(literal.ToString (), false));
1195                                                         literal.Length = 0;
1196                                                 }
1197                                                 formats.Add (new PartData (ch.ToString(), true));
1198                                                 break;
1199
1200                                 }
1201                                 lastch = ch;
1202                         }
1203                         if (literal.Length >= 0)
1204                                 formats.Add (new PartData (literal.ToString (), is_literal));
1205
1206                         part_data = new PartData [formats.Count];
1207                         formats.CopyTo (part_data);
1208                 }
1209
1210                 private Point CalculateDropDownLocation (Rectangle parent_control_rect, Size child_size, bool align_left)
1211                 {
1212                         // default bottom left
1213                         Point location = new Point(parent_control_rect.Left + 5, parent_control_rect.Bottom);
1214                         // now adjust the alignment
1215                         if (!align_left) {
1216                                 location.X = parent_control_rect.Right - child_size.Width;                              
1217                         }
1218                         
1219                         Point screen_location = PointToScreen (location);                       
1220                         Rectangle working_area = Screen.FromControl(this).WorkingArea;
1221                         // now adjust if off the right side of the screen                       
1222                         if (screen_location.X < working_area.X) {
1223                                 screen_location.X = working_area.X;
1224                         }  
1225                         // now adjust if it should be displayed above control
1226                         if (screen_location.Y + child_size.Height > working_area.Bottom) {
1227                                 screen_location.Y -= (parent_control_rect.Height + child_size.Height);
1228                         }
1229
1230                         // since the parent of the month calendar is the form, adjust accordingly.
1231                         if (month_calendar.Parent != null) {
1232                                 screen_location = month_calendar.Parent.PointToClient(screen_location);
1233                         }
1234
1235                         return screen_location;
1236                 }
1237                 
1238                 // actually draw this control
1239                 internal void Draw (Rectangle clip_rect, Graphics dc)
1240                 {                       
1241                         ThemeEngine.Current.DrawDateTimePicker (dc, clip_rect, this);
1242                 }                       
1243                 
1244                 // drop the calendar down
1245                 internal void DropDownMonthCalendar ()
1246                 {
1247                         // ensure the right date is set for the month_calendar
1248                         month_calendar.SetDate (this.date_value);
1249                         // get a rectangle that has the dimensions of the text area,
1250                         // but the height of the dtp control.
1251                         Rectangle align_area = this.date_area_rect;
1252                         align_area.Y = this.ClientRectangle.Y;
1253                         align_area.Height = this.ClientRectangle.Height;
1254
1255                         // establish the month calendar's location
1256                         month_calendar.Location = CalculateDropDownLocation (
1257                                 align_area,
1258                                 month_calendar.Size,
1259                                 (this.DropDownAlign == LeftRightAlignment.Left));
1260                         month_calendar.Show ();
1261                         month_calendar.Focus ();
1262                         month_calendar.Capture = true;
1263
1264                         // fire any registered events
1265                         // XXX should this just call OnDropDown?
1266                         EventHandler eh = (EventHandler)(Events [DropDownEvent]);
1267                         if (eh != null)
1268                                 eh (this, EventArgs.Empty);
1269                 }
1270                 
1271                 // hide the month calendar
1272                 internal void HideMonthCalendar () 
1273                 {
1274                         this.is_drop_down_visible = false;
1275                         Invalidate (drop_down_arrow_rect);
1276                         month_calendar.Capture = false;
1277                         if (month_calendar.Visible) {
1278                                 month_calendar.Hide ();
1279                         }
1280         }
1281
1282                 private int GetSelectedPartIndex()
1283                 {
1284                         for (int i = 0; i < part_data.Length; i++)
1285                         {
1286                                 if (part_data[i].is_selected && !part_data[i].is_literal)
1287                                         return i;
1288                         }
1289                         return -1;
1290                 }
1291
1292                 private void IncrementSelectedPart(int delta)
1293                 {
1294                         int selected_index = GetSelectedPartIndex();
1295
1296                         if (selected_index == -1) {
1297                                 return;
1298                         }
1299                         
1300                         switch (part_data[selected_index].value)
1301                         {
1302                                 case "d":
1303                                 case "dd": // number day formats
1304                                         if (delta < 0) {
1305                                                 if (Value.Day == 1)
1306                                                         SetPart(DateTime.DaysInMonth(Value.Year, Value.Month), 'd');
1307                                                 else
1308                                                         SetPart(Value.Day + delta, 'd');
1309                                         } else {
1310                                                 if (Value.Day == DateTime.DaysInMonth(Value.Year, Value.Month))
1311                                                         SetPart(1, 'd');
1312                                                 else
1313                                                         SetPart(Value.Day + delta, 'd') ;
1314                                         }
1315                                         break;
1316                                 case "ddd":
1317                                 case "dddd": // text day formats
1318                                         Value = Value.AddDays(delta);
1319                                         break;
1320                                 case "h":
1321                                 case "hh":
1322                                 case "H":
1323                                 case "HH": // hour formats
1324                                         SetPart(Value.Hour + delta, 'h');
1325                                         break;
1326                                 case "m":
1327                                 case "mm": // minute formats
1328                                         SetPart(Value.Minute + delta, 'm');
1329                                         break;
1330                                 case "M":
1331                                 case "MM":
1332                                 case "MMM":
1333                                 case "MMMM": // month formats
1334                                         SetPart(Value.Month + delta, 'M');
1335                                         break;
1336                                 case "s":
1337                                 case "ss": // second format
1338                                         SetPart(Value.Second + delta, 's');
1339                                         break;
1340                                 case "t":
1341                                 case "tt": // AM / PM specifier
1342                                         SetPart(Value.Hour + delta * 12, 'h');
1343                                         break;
1344                                 case "y":
1345                                 case "yy":
1346                                 case "yyy":
1347                                 case "yyyy":
1348                                         SetPart(Value.Year + delta, 'y');
1349                                         break;
1350                         }
1351                 }
1352
1353                 private void SelectNextPart()
1354                 {
1355                         int selected_index;
1356                         if (is_checkbox_selected) {
1357                                 for (int i = 0; i < part_data.Length; i++)
1358                                 {
1359                                         if (!part_data[i].is_literal)
1360                                         {
1361                                                 is_checkbox_selected = false;
1362                                                 part_data[i].is_selected = true;
1363                                                 Invalidate();
1364                                                 break;
1365                                         }
1366                                 }
1367                         } else {
1368                                 selected_index = GetSelectedPartIndex();
1369                                 if (selected_index >= 0)
1370                                         part_data[selected_index].is_selected = false;
1371                                 for (int i = selected_index + 1; i < part_data.Length; i++)
1372                                 {
1373                                         if (!part_data[i].is_literal)
1374                                         {
1375                                                 part_data[i].is_selected = true;
1376                                                 Invalidate();
1377                                                 break;
1378                                         }
1379                                 }
1380                                 if (GetSelectedPartIndex() == -1)
1381                                 { // if no part was found before the end, look from the beginning
1382                                         if (ShowCheckBox)
1383                                         {
1384                                                 is_checkbox_selected = true;
1385                                                 Invalidate();
1386                                         }
1387                                         else
1388                                         {
1389                                                 for (int i = 0; i <= selected_index; i++)
1390                                                 {
1391                                                         if (!part_data[i].is_literal)
1392                                                         {
1393                                                                 part_data[i].is_selected = true;
1394                                                                 Invalidate();
1395                                                                 break;
1396                                                         }
1397                                                 }
1398                                         }
1399                                 }
1400                         }
1401
1402                 }
1403
1404                 private void SelectPreviousPart()
1405                 {
1406                         if (is_checkbox_selected)
1407                         {
1408                                 for (int i = part_data.Length - 1; i >= 0; i--)
1409                                 {
1410                                         if (!part_data[i].is_literal)
1411                                         {
1412                                                 is_checkbox_selected = false;
1413                                                 part_data[i].is_selected = true;
1414                                                 Invalidate();
1415                                                 break;
1416                                         }
1417                                 }
1418                         }
1419                         else
1420                         {
1421                                 int selected_index = GetSelectedPartIndex();
1422
1423                                 if (selected_index >= 0)
1424                                         part_data[selected_index].is_selected = false;
1425
1426                                 for (int i = selected_index - 1; i >= 0; i--)
1427                                 {
1428                                         if (!part_data[i].is_literal)
1429                                         {
1430                                                 part_data[i].is_selected = true;
1431                                                 Invalidate();
1432                                                 break;
1433                                         }
1434                                 }
1435                                 if (GetSelectedPartIndex() == -1)
1436                                 {       // if no part was found before the beginning, look from the end
1437                                         if (ShowCheckBox)
1438                                         {
1439                                                 is_checkbox_selected = true;
1440                                                 Invalidate();
1441                                         }
1442                                         else
1443                                         {
1444                                                 for (int i = part_data.Length - 1; i >= selected_index; i--)
1445                                                 {
1446                                                         if (!part_data[i].is_literal)
1447                                                         {
1448                                                                 part_data[i].is_selected = true;
1449                                                                 Invalidate();
1450                                                                 break;
1451                                                         }
1452                                                 }
1453                                         }
1454                                 }
1455                         }
1456                 }
1457
1458                 // raised by key down events.
1459                 private void KeyDownHandler(object sender, KeyEventArgs e)
1460                 {
1461                         switch (e.KeyCode)
1462                         {
1463                                 case Keys.Add:
1464                                 case Keys.Up:
1465                                         {
1466                                                 if (ShowCheckBox && Checked == false)
1467                                                         break;
1468                                                 IncrementSelectedPart(1);
1469                                                 e.Handled = true;
1470                                                 break;
1471                                         }
1472                                 case Keys.Subtract:
1473                                 case Keys.Down:
1474                                         {
1475                                                 if (ShowCheckBox && Checked == false)
1476                                                         break;
1477                                                 IncrementSelectedPart(-1);
1478                                                 e.Handled = true;
1479                                                 break;
1480                                         }
1481                                 case Keys.Left:
1482                                         {// select the next part to the left
1483                                                 if (ShowCheckBox && Checked == false)
1484                                                         break;
1485                                                 SelectPreviousPart();
1486                                                 e.Handled = true;
1487                                                 break;
1488                                         }
1489                                 case Keys.Right:
1490                                         {// select the next part to the right
1491                                                 if (ShowCheckBox && Checked == false)
1492                                                         break;
1493                                                 SelectNextPart();
1494                                                 e.Handled = true;
1495                                                 break;
1496                                         }
1497                                 case Keys.F4:
1498                                         if (!e.Alt && !is_drop_down_visible) {
1499                                                 DropDownMonthCalendar ();
1500                                                 e.Handled = true;
1501                                         }
1502
1503                                         break;
1504                         }
1505                 }
1506
1507                 // raised by any key down events
1508                 private void KeyPressHandler (object sender, KeyPressEventArgs e)
1509                 {
1510                         switch (e.KeyChar) {
1511                                 case ' ':
1512                                         if (is_checkbox_selected)
1513                                         {
1514                                                 Checked = !Checked;
1515                                         }
1516                                         break;
1517                                 case '0':
1518                                 case '1':
1519                                 case '2':
1520                                 case '3':
1521                                 case '4':
1522                                 case '5':
1523                                 case '6':
1524                                 case '7':
1525                                 case '8':
1526                                 case '9':
1527                                         int number = e.KeyChar - (int) '0';
1528                                         int selected_index = GetSelectedPartIndex();
1529                                         if (selected_index == -1)
1530                                                 break;
1531                                         if (!part_data[selected_index].is_numeric_format)
1532                                                 break;
1533                                         switch (part_data[selected_index].value)
1534                                         {
1535                                                 case "d":
1536                                                 case "dd":
1537                                                         int newDay = Value.Day * 10 + number;
1538                                                         if (DateTime.DaysInMonth(Value.Year, Value.Month) < newDay)
1539                                                                 newDay = number;
1540                                                         SetPart(newDay, 'd');
1541                                                         break;
1542                                                 case "M":
1543                                                 case "MM":
1544                                                         int newMonth = Value.Month * 10 + number;
1545                                                         if (newMonth > 12)
1546                                                                 newMonth = number;
1547                                                         SetPart(newMonth, 'M');
1548                                                         break;
1549                                                 case "y":
1550                                                 case "yy":
1551                                                 case "yyyy":
1552                                                         int newYear = Value.Year * 10 + number;
1553                                                         if (newYear > 9999)
1554                                                                 newYear = number;
1555                                                         SetPart(newYear, 'y');
1556                                                         break;
1557                                                 case "h":
1558                                                 case "hh":
1559                                                 case "H":
1560                                                 case "HH":
1561                                                         int newHour = Value.Hour * 10 + number;
1562                                                         if (newHour >= 24)
1563                                                                 newHour = number;
1564                                                         SetPart(newHour, 'h');
1565                                                         break;
1566                                                 case "m":
1567                                                 case "mm":
1568                                                         int newMinute = Value.Minute* 10 + number;
1569                                                         if (newMinute >= 60)
1570                                                                 newMinute = number;
1571                                                         SetPart(newMinute, 'm');
1572                                                         break;
1573                                                 case "s":
1574                                                 case "ss":
1575                                                         int newSecond = Value.Second * 10 + number;
1576                                                         if (newSecond >= 60)
1577                                                                 newSecond = number;
1578                                                         SetPart(newSecond, 's');
1579                                                         break;
1580
1581                                         }
1582                                         break;
1583                                 default:
1584                                         break;
1585                         }
1586                         e.Handled = true;
1587                 }
1588
1589                 // set the specified part of the date to the specified value
1590                 private void SetPart(int value, char part)
1591                 {
1592                         switch (part)
1593                         {
1594                                 case 's': // seconds
1595                                         value %= 60;
1596                                         if (value == -1)
1597                                                 value = 59;
1598                                         if (value >= 0 && value <= 59)
1599                                                 Value = new DateTime(Value.Year, Value.Month, Value.Day, Value.Hour, Value.Minute, value, Value.Millisecond);
1600                                         break;
1601                                 case 'm': // minutes
1602                                         value %= 60;
1603                                         if (value == -1)
1604                                                 value = 59;
1605                                         if (value >= 0 && value <= 59)
1606                                                 Value = new DateTime(Value.Year, Value.Month, Value.Day, Value.Hour, value, Value.Second, Value.Millisecond);
1607                                         break;
1608                                 case 'h':
1609                                 case 'H': // hours
1610                                         value %= 24;
1611                                         if (value == -1)
1612                                                 value = 23;
1613                                         if (value >= 0 && value <= 23)
1614                                                 Value = new DateTime(Value.Year, Value.Month, Value.Day, value, Value.Minute, Value.Second, Value.Millisecond);
1615                                         break;
1616                                 case 'd': // days
1617                                         int max_days = DateTime.DaysInMonth(Value.Year, Value.Month);
1618                                         if (value > max_days)
1619                                                 Value = new DateTime(Value.Year, Value.Month, max_days, Value.Hour, Value.Minute, Value.Second, Value.Millisecond);
1620                                         if (value >= 1 && value <= 31)
1621                                                 Value = new DateTime(Value.Year, Value.Month, value, Value.Hour, Value.Minute, Value.Second, Value.Millisecond);
1622                                         break;
1623                                 case 'M': // months
1624                                         value %= 12;
1625                                         if (value == 0)
1626                                                 value = 12;
1627                                                 
1628                                         if (value >= 1 && value <= 12) {
1629                                                 // if we move from say december to november with days on 31, we must
1630                                                 // remap to the maximum number of days
1631                                                 int days_in_new_month = DateTime.DaysInMonth (Value.Year, value);
1632                                                 
1633                                                 if (Value.Day > days_in_new_month)
1634                                                         Value = new DateTime (Value.Year, value, days_in_new_month, Value.Hour, Value.Minute, Value.Second, Value.Millisecond);
1635                                                 else
1636                                                         Value = new DateTime (Value.Year, value, Value.Day, Value.Hour, Value.Minute, Value.Second, Value.Millisecond);
1637                                         }
1638                                         break;
1639                                 case 'y': // years
1640                                         value %= 10000;
1641                                         
1642                                         if (value > 0 && value <= 9999) {
1643                                                 // if we move to a leap year, the days in month could throw an exception
1644                                                 int days_in_new_month = DateTime.DaysInMonth (value, Value.Month);
1645                                                 
1646                                                 if (Value.Day > days_in_new_month)
1647                                                         Value = new DateTime (value, Value.Month, days_in_new_month, Value.Hour, Value.Minute, Value.Second, Value.Millisecond);
1648                                                 else
1649                                                         Value = new DateTime (value, Value.Month, Value.Day, Value.Hour, Value.Minute, Value.Second, Value.Millisecond);
1650                                         }
1651                                         break;
1652                         }
1653                 }
1654
1655                 // if we loose focus deselect any selected parts.
1656                 private void LostFocusHandler (object sender, EventArgs e) 
1657                 {
1658                         int selected_index = GetSelectedPartIndex ();
1659                         if (selected_index != -1)
1660                         {
1661                                 part_data [selected_index].is_selected = false;
1662                                 Rectangle invalidate_rect = Rectangle.Ceiling (part_data [selected_index].drawing_rectangle);
1663                                 invalidate_rect.Inflate (2, 2);
1664                                 Invalidate (invalidate_rect);
1665                         }
1666                         else if (is_checkbox_selected)
1667                         {
1668                                 is_checkbox_selected = false;
1669                                 Invalidate (CheckBoxRect);
1670                         }
1671                 }
1672
1673                 // if month calendar looses focus and the drop down is up, then close it
1674                 private void MonthCalendarLostFocusHandler(object sender, EventArgs e)
1675                 {
1676                         if (is_drop_down_visible && !month_calendar.Focused)
1677                         {
1678                                 //this.HideMonthCalendar(); 
1679                                 //This is handled from the monthcalender itself, 
1680                                 //it may loose focus, but still has to be visible,
1681                                 //for instance when the context menu is displayed.
1682                         }
1683
1684                 }
1685
1686                 private void MonthCalendarDateChangedHandler (object sender, DateRangeEventArgs e)
1687                 {
1688                         if (month_calendar.Visible)
1689                                 this.Value = e.Start.Date.Add (this.Value.TimeOfDay);
1690                 }
1691
1692                 // fired when a user clicks on the month calendar to select a date
1693                 private void MonthCalendarDateSelectedHandler (object sender, DateRangeEventArgs e)
1694                 {
1695                         this.HideMonthCalendar ();      
1696                         this.Focus ();                  
1697                 } 
1698
1699                 private void MouseUpHandler(object sender, MouseEventArgs e)
1700                 {
1701                         if (ShowUpDown)
1702                         {
1703                                 if (is_up_pressed || is_down_pressed)
1704                                 {
1705                                         updown_timer.Enabled = false;
1706                                         is_up_pressed = false;
1707                                         is_down_pressed = false;
1708                                         Invalidate (drop_down_arrow_rect);
1709                                 }
1710                         }
1711                 }
1712
1713                 // to check if the mouse has come down on this control
1714                 private void MouseDownHandler (object sender, MouseEventArgs e)
1715                 {
1716                         // Only left clicks are handled.
1717                         if (e.Button != MouseButtons.Left)
1718                                 return;
1719
1720                         is_checkbox_selected = false;
1721
1722                         if (ShowCheckBox && CheckBoxRect.Contains(e.X, e.Y))
1723                         {
1724                                 is_checkbox_selected = true;
1725                                 Checked = !Checked;
1726                                 return;
1727                         }
1728
1729
1730                         if (ShowUpDown && drop_down_arrow_rect.Contains (e.X, e.Y))
1731                         {
1732                                 if (!(ShowCheckBox && Checked == false))
1733                                 {
1734                                         if (e.Y < this.Height / 2) {
1735                                                 is_up_pressed = true;
1736                                                 is_down_pressed = false;
1737                                                 IncrementSelectedPart (1);
1738                                         } else {
1739                                                 is_up_pressed = false;
1740                                                 is_down_pressed = true;
1741                                                 IncrementSelectedPart (-1);
1742                                         }
1743                                         Invalidate (drop_down_arrow_rect);
1744                                         updown_timer.Interval = initial_timer_delay;
1745                                         updown_timer.Enabled = true;
1746                                 }
1747                         } else if (is_drop_down_visible == false && drop_down_arrow_rect.Contains (e.X, e.Y)) {
1748                                 is_drop_down_visible = true;
1749                                 if (!Checked)
1750                                         Checked = true;
1751                                 Invalidate (drop_down_arrow_rect);
1752                                 DropDownMonthCalendar ();
1753                         } else {
1754                                 // mouse down on this control anywhere else collapses it
1755                                 if (is_drop_down_visible) {                             
1756                                         HideMonthCalendar ();
1757                                         this.Focus ();
1758                                 }
1759                                 if (!(ShowCheckBox && Checked == false))
1760                                 {
1761                                         // go through the parts to see if the click is in any of them
1762                                         bool invalidate_afterwards = false;
1763                                         for (int i = 0; i < part_data.Length; i++) {
1764                                                 bool old = part_data [i].is_selected;
1765
1766                                                 if (part_data [i].is_literal)
1767                                                         continue;
1768
1769                                                 if (part_data [i].drawing_rectangle.Contains (e.X, e.Y)) {
1770                                                         part_data [i].is_selected = true;
1771                                                 } else {
1772                                                         part_data [i].is_selected = false;
1773                                                 }
1774                                                 if (old != part_data [i].is_selected) 
1775                                                         invalidate_afterwards = true;
1776                                         }
1777                                         if (invalidate_afterwards)
1778                                                 Invalidate ();
1779                                 }
1780                                 
1781                         }
1782                 }
1783                 
1784                 
1785                 // paint this control now
1786                 private void PaintHandler (object sender, PaintEventArgs pe) {
1787                         if (Width <= 0 || Height <=  0 || Visible == false)
1788                                 return;
1789
1790                         Draw (pe.ClipRectangle, pe.Graphics);
1791                 }
1792                 
1793                 #endregion              
1794
1795                 #region internal classes
1796                 internal class PartData
1797                 {
1798                         internal string value;
1799                         internal bool is_literal;
1800                         internal bool is_selected;
1801                         internal RectangleF drawing_rectangle;
1802
1803                         internal bool is_numeric_format
1804                         {
1805                                 get
1806                                 {
1807                                         if (is_literal)
1808                                                 return false;
1809                                         switch (value) {
1810                                         case "m":
1811                                         case "mm":
1812                                         case "d":
1813                                         case "dd":
1814                                         case "h":
1815                                         case "hh":
1816                                         case "H":
1817                                         case "HH":
1818                                         case "M":
1819                                         case "MM":
1820                                         case "s":
1821                                         case "ss":
1822                                         case "y":
1823                                         case "yy":
1824                                         case "yyyy":
1825                                                 return true;
1826                                         case "ddd":
1827                                         case "dddd":
1828                                                 return false;
1829                                         default:
1830                                                 return false;
1831                                         }
1832                                 }
1833                         }
1834
1835                         internal PartData(string value, bool is_literal)
1836                         {
1837                                 this.value = value;
1838                                 this.is_literal = is_literal;
1839                         }
1840
1841                         // calculate the string to show for this data
1842                         internal string GetText(DateTime date)
1843                         {
1844                                 if (is_literal) {
1845                                         return value;
1846                                 } else {
1847                                         return GetText (date, value);
1848                                 }
1849                         }
1850
1851                         static internal string GetText(DateTime date, string format)
1852                         {
1853                                 if (format.StartsWith ("g")) 
1854                                         return " ";
1855                                 else if (format.Length == 1)
1856                                         return date.ToString ("%" + format);
1857                                 else if (format == "yyyyy" || format == "yyyyyy" || format == "yyyyyyy" || format == "yyyyyyyy")
1858                                         return date.ToString ("yyyy");
1859                                 else if (format.Length > 1)
1860                                         return date.ToString (format);
1861                                 else
1862                                         return string.Empty;
1863                         }
1864                 }
1865                 
1866                 #endregion              
1867         }
1868 }