* TreeView.cs: Don't draw the selected node when we lose
[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 Novell, Inc.
21 //
22 // Authors:
23 //      John BouAntoun  jba-mono@optusnet.com.au
24 //
25 // TODO:
26 //              - implement custom formatting of the date time value
27 //              - implement any behaviour associate with UseUpDown (painting, key and mouse)
28 //              - implement key processing and responding
29 //              - fix MonthCalendar Popdown on form move
30 //              - wire in all events from monthcalendar
31
32
33 using System;
34 using System.Drawing;
35 using System.Collections;
36 using System.ComponentModel;
37 using System.Windows.Forms;
38
39 namespace System.Windows.Forms {
40         [DefaultEvent("ValueChanged")]
41         [DefaultProperty("Value")]
42         [Designer("System.Windows.Forms.Design.DateTimePickerDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
43         public class DateTimePicker : Control {
44
45                 #region Public variables
46                 
47                 // this class has to have the specified hour, minute and second, as it says in msdn
48                 public static readonly DateTime MaxDateTime = new DateTime (9998, 12, 31, 23, 59, 59);
49                 public static readonly DateTime MinDateTime = new DateTime (1753, 1, 1);
50                 
51                 #endregion      // Public variables
52                 
53                 #region Local variables
54                 
55                 protected static readonly Color DefaultMonthBackColor = ThemeEngine.Current.ColorWindow;
56                 protected static readonly Color DefaultTitleBackColor = ThemeEngine.Current.ColorActiveCaption;
57                 protected static readonly Color DefaultTitleForeColor = ThemeEngine.Current.ColorActiveCaptionText;
58                 protected static readonly Color DefaultTrailingForeColor = Color.Gray;
59                 
60                 internal MonthCalendar                  month_calendar;
61                 bool                                                    is_checked;
62                 string                                                  custom_format;
63                 LeftRightAlignment                              drop_down_align;
64                 DateTimePickerFormat                    format;
65                 DateTime                                                max_date;
66                 DateTime                                                min_date;
67                 bool                                                    show_check_box;
68                 bool                                                    show_up_down;
69                 string                                                  text;
70                 DateTime                                                date_value;
71                 
72                 // variables used for drawing and such
73                 internal int                                    up_down_width;
74                 internal bool                                   is_drop_down_visible;
75                 
76                 #endregion      // Local variables
77                 
78                 #region public constructors
79                 
80                 // only public constructor
81                 public DateTimePicker () {
82                 
83                         // initialise the month calendar
84                         month_calendar = new MonthCalendar (this);
85                         month_calendar.CalendarDimensions = new Size (1, 1);
86                         month_calendar.MaxSelectionCount = 1;
87                         month_calendar.ForeColor = Control.DefaultForeColor;
88                         month_calendar.BackColor = DefaultMonthBackColor;
89                         month_calendar.TitleBackColor = DefaultTitleBackColor;
90                         month_calendar.TitleForeColor = DefaultTitleForeColor;
91                         month_calendar.TrailingForeColor = DefaultTrailingForeColor;
92                         month_calendar.Visible = false;
93
94                         
95                         // initialise other variables
96                         is_checked = false;
97                         custom_format = string.Empty;
98                         drop_down_align = LeftRightAlignment.Left;
99                         format = DateTimePickerFormat.Long;
100                         max_date = MaxDateTime;
101                         min_date = MinDateTime;
102                         show_check_box = false;
103                         show_up_down = false;
104                         date_value = DateTime.Now;
105                         text = FormatValue ();          
106                         
107                         up_down_width = 10;
108                         is_drop_down_visible = false;
109                         
110                         month_calendar.DateSelected += new DateRangeEventHandler (MonthCalendarDateSelectedHandler);
111                         KeyPress += new KeyPressEventHandler (KeyPressHandler);
112 //                      LostFocus += new EventHandler (LostFocusHandler);
113                         MouseDown += new MouseEventHandler (MouseDownHandler);                  
114                         Paint += new PaintEventHandler (PaintHandler);
115                         
116                         SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick, false);
117                         SetStyle (ControlStyles.FixedHeight, true);
118                 }
119                 
120                 #endregion
121                 
122                 #region public properties
123                 
124                 // no reason why this is overridden
125                 [Browsable(false)]
126                 [EditorBrowsable(EditorBrowsableState.Never)]
127                 public Color BackColor {
128                         set {
129                                 base.BackColor = value;
130                         }
131                         get {
132                                 return base.BackColor;
133                         }
134                 }
135                 
136                 // no reason why this is overridden
137                 [Browsable(false)]
138                 [EditorBrowsable(EditorBrowsableState.Never)]
139                 public override Image BackgroundImage {
140                         set {
141                                 base.BackgroundImage = value;
142                         }
143                         get {
144                                 return base.BackgroundImage;
145                         }
146                 }
147
148                 [AmbientValue(null)]
149                 [Localizable(true)]
150                 public Font CalendarFont {
151                         set {
152                                 month_calendar.Font = value;
153                         }
154                         get {
155                                 return month_calendar.Font;
156                         }
157                 }
158
159                 public Color CalendarForeColor {
160                         set {
161                                 month_calendar.ForeColor = value;
162                         }
163                         get {
164                                 return month_calendar.ForeColor;
165                         }
166                 }
167
168                 public Color CalendarMonthBackground {
169                         set {
170                                 month_calendar.BackColor = value;
171                         }
172                         get {
173                                 return month_calendar.BackColor;
174                         }
175                 }
176
177                 public Color CalendarTitleBackColor {
178                         set {
179                                 month_calendar.TitleBackColor = value;
180                         }
181                         get {
182                                 return month_calendar.TitleBackColor;
183                         }
184                 }
185
186                 public Color CalendarTitleForeColor {
187                         set {
188                                 month_calendar.TitleForeColor = value;
189                         }
190                         get {
191                                 return month_calendar.TitleForeColor;
192                         }
193                 }
194
195                 public Color CalendarTrailingForeColor {
196                         set {
197                                 month_calendar.TrailingForeColor = value;
198                         }
199                         get {
200                                 return month_calendar.TrailingForeColor;
201                         }
202                 }
203                 
204                 // when checked the value is grayed out
205                 [Bindable(true)]
206                 [DefaultValue(true)]
207                 public bool Checked {
208                         set {
209                                 if (is_checked != value) {
210                                         is_checked = value;
211                                         // invalidate the value inside this control
212                                         this.Invalidate (date_area_rect);
213                                 }
214                         }
215                         get {
216                                 return is_checked;
217                         }
218                 }
219                 
220                 // the custom format string to format this control with
221                 [DefaultValue(null)]
222                 [RefreshProperties(RefreshProperties.Repaint)]
223                 public string CustomFormat {
224                         set {
225                                 if (custom_format != value) {
226                                         custom_format = value;
227                                         if (this.Format == DateTimePickerFormat.Custom) {
228                                                 // TODO: change the text value of the dtp                                               
229                                         }
230                                 }
231                         }
232                         get {
233                                 return custom_format;
234                         }
235                 }
236                 
237                 // which side the drop down is to be aligned on
238                 [DefaultValue(LeftRightAlignment.Left)]
239                 [Localizable(true)]
240                 public LeftRightAlignment DropDownAlign {
241                         set {
242                                 if (drop_down_align != value) {
243                                         drop_down_align = value;
244                                 }
245                         }
246                         get {
247                                 return drop_down_align;
248                         }
249                 }
250
251                 [Browsable(false)]
252                 [EditorBrowsable(EditorBrowsableState.Never)]
253                 public override Color ForeColor {
254                         set {
255                                 base.ForeColor = value;
256                         }
257                         get {
258                                 return base.ForeColor;
259                         }
260                 }
261                 
262                 // the format of the date time picker text, default is long
263                 [RefreshProperties(RefreshProperties.Repaint)]
264                 public DateTimePickerFormat Format {
265                         set {
266                                 if (format != value) {
267                                         format = value;
268                                         this.OnFormatChanged (EventArgs.Empty);
269                                         // invalidate the value inside this control
270                                         this.Invalidate (date_area_rect);
271                                 }
272                         }
273                         get {
274                                 return format;
275                         }
276                 }
277                 
278                 public DateTime MaxDate {
279                         set {
280                                 if (value < min_date) {
281                                         throw new ArgumentException ();
282                                 }
283                                 if (value > MaxDateTime) {
284                                         throw new SystemException ();
285                                 }
286                                 if (max_date != value) {
287                                         max_date = value;
288                                         
289                                         // TODO: verify this is correct behaviour when value > max date
290                                         if (Value > max_date) {
291                                                 Value = max_date;
292                                                 // invalidate the value inside this control
293                                                 this.Invalidate (date_area_rect);
294                                         }
295                                 }
296                         }
297                         get {
298                                 return max_date;
299                         }
300                 }
301                 
302                 public DateTime MinDate {
303                         set {
304                                 if (value < min_date) {
305                                         throw new ArgumentException ();
306                                 }
307                                 if (value < MinDateTime) {
308                                         throw new SystemException ();
309                                 }
310                                 if (min_date != value) {
311                                         min_date = value;
312                                         
313                                         // TODO: verify this is correct behaviour when value > max date
314                                         if (Value < min_date) {
315                                                 Value = min_date;
316                                                 // invalidate the value inside this control
317                                                 this.Invalidate (date_area_rect);
318                                         }
319                                 }
320                         }
321                         get {
322                                 return min_date;
323                         }
324                 }
325                 
326                 // the prefered height to draw this control using current font
327                 [Browsable(false)]
328                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
329                 public int PreferredHeight {
330                         get {
331                                 return this.Font.Height + 7;
332                         }
333                 }
334                 
335                 // whether or not the check box is shown
336                 [DefaultValue(false)]
337                 public bool ShowCheckBox {
338                         set {
339                                 if (show_check_box != value) {
340                                         show_check_box = value;
341                                         // invalidate the value inside this control
342                                         this.Invalidate (date_area_rect);
343                                 }
344                         }
345                         get {
346                                 return show_check_box;
347                         }
348                 }
349                 
350                 // if true show the updown control, else popup the monthcalendar
351                 [DefaultValue(false)]
352                 public bool ShowUpDown {
353                         set {
354                                 if (show_up_down != value) {
355                                         show_up_down = value;
356                                         // need to invalidate the whole control
357                                         this.Invalidate ();
358                                 }
359                         }
360                         get {
361                                 return show_up_down;
362                         }
363                 }
364
365                 [Browsable(false)]
366                 [EditorBrowsable(EditorBrowsableState.Advanced)]
367                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
368                 public string Text {
369                         set {
370                                 // TODO: if the format is a custom format we need to do a custom parse here
371                                 DateTime parsed_value = DateTime.Parse (value);
372                                 if (date_value != parsed_value) {
373                                         Value = parsed_value;
374                                 }
375                                 text = FormatValue (); 
376                         }
377                         get {
378                                 return text;
379                         }
380                 }       
381
382                 [Bindable(true)]
383                 [RefreshProperties(RefreshProperties.All)]
384                 public DateTime Value {
385                         set {
386                                 if (date_value != value) {
387                                         date_value = value;
388                                         text = FormatValue ();
389                                         this.OnValueChanged (EventArgs.Empty);
390                                         this.Invalidate (date_area_rect);
391                                 }
392                         }
393                         get {
394                                 return date_value;
395                         }                       
396                 }
397
398                 #endregion      // public properties
399                 
400                 #region public methods
401                 
402                 // just return the text value
403                 public override string ToString () {
404                         return this.Text;
405                 } 
406                                 
407                 #endregion      // public methods
408                 
409                 #region public events
410                 
411                 // raised when the monthcalendar is closed
412                 public event EventHandler CloseUp;
413                 
414                 // raised when the monthcalendar is opened
415                 public event EventHandler DropDown;
416                 
417                 // raised when the format of the value is changed
418                 public event EventHandler FormatChanged;
419                 
420                 // raised when the date Value is changed
421                 public event EventHandler ValueChanged;
422                 
423                 #endregion      // public events
424                 
425                 #region protected properties
426
427                 // not sure why we're overriding this one               
428                 protected override CreateParams CreateParams {
429                         get {
430                                 return base.CreateParams;
431                         }
432                 }
433                 
434                 // specify the default size for this control
435                 protected override Size DefaultSize {
436                         get {
437                                 // todo actually measure this properly
438                                 return new Size (200, PreferredHeight);
439                         }
440                 }
441                 
442                 #endregion      // protected properties
443                 
444                 #region protected methods
445                 
446                 // not sure why we're overriding this one
447                 protected override AccessibleObject CreateAccessibilityInstance () {
448                         return base.CreateAccessibilityInstance ();
449                 }
450                 
451                 // not sure why we're overriding this one
452                 protected override void CreateHandle () {
453                         base.CreateHandle ();
454                 }
455                 
456                 // not sure why we're overriding this one
457                 protected override void DestroyHandle () {
458                         base.DestroyHandle ();
459                 }
460                 
461                 // not sure why we're overriding this one
462                 protected override void Dispose (bool disposing) {
463                         base.Dispose (disposing);
464                 }
465                 
466                 // find out if this key is an input key for us, depends on which date part is focused
467                 protected override bool IsInputKey (Keys keyData) {
468                         // TODO: fix this implementation of IsInputKey
469                         return false;
470                 }
471                 
472                 // raises the CloseUp event
473                 protected virtual void OnCloseUp (EventArgs eventargs) {
474                         if (this.CloseUp != null) {
475                                 this.CloseUp (this, eventargs);
476                         }
477                 }
478                 
479                 // raise the drop down event
480                 protected virtual void OnDropDown (EventArgs eventargs) {
481                         if (this.DropDown != null) {
482                                 this.DropDown (this, eventargs);
483                         }
484                 }
485                 
486                 // raises the format changed event
487                 protected virtual void OnFormatChanged (EventArgs e) {
488                         if (this.FormatChanged != null) {
489                                 this.FormatChanged (this, e);
490                         }
491                 }
492                 
493                 // not sure why we're overriding this one 
494                 protected override void OnSystemColorsChanged (EventArgs e) {
495                         base.OnSystemColorsChanged (e);
496                 }
497                 
498                 // raise the ValueChanged event
499                 protected virtual void OnValueChanged (EventArgs eventargs) {
500                         if (this.ValueChanged != null) {
501                                 this.ValueChanged (this, eventargs);
502                         }
503                 }
504                 
505                 // overridden to set the bounds of this control properly
506                 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
507                         // TODO: ensure I implemented the bounds core setting properly.
508                         if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height ||
509                                 (specified & BoundsSpecified.Size) == BoundsSpecified.Size)  {
510                                 base.SetBoundsCore (x, y, width, DefaultSize.Height, specified);
511                         } else {
512                                 base.SetBoundsCore (x, y, width, height, specified);
513                         }
514                         
515                         // need to set the rectangles for all the support internal rects
516                         // this is done here as a optimisation since this is an array of rects
517                         if ((specified & BoundsSpecified.X) == BoundsSpecified.X ||
518                                 (specified & BoundsSpecified.Y) == BoundsSpecified.Y) {
519                                 // TODO set up all the datepart rects
520                         }
521                 }
522
523                 // not sure why we're overriding this
524                 protected override void WndProc (ref Message m) {
525                         base.WndProc (ref m);
526                 }
527                 
528                 #endregion      // protected methods
529                 
530                 #region internal / private properties
531                 
532                 // this is the region that the date and the check box is drawn on
533                 internal Rectangle date_area_rect {
534                         get {
535                                 Rectangle rect = this.ClientRectangle;
536                                 if (ShowUpDown) {
537                                         // set the space to the left of the up/down button
538                                         if (rect.Width > (up_down_width + 4)) {
539                                                 rect.Width -= (up_down_width + 4);
540                                         } else {
541                                                 rect.Width = 0;
542                                         }
543                                 } else {
544                                         // set the space to the left of the up/down button
545                                         // TODO make this use up down button
546                                         if (rect.Width > (SystemInformation.VerticalScrollBarWidth + 4)) {
547                                                 rect.Width -= SystemInformation.VerticalScrollBarWidth;
548                                         } else {
549                                                 rect.Width = 0;
550                                         }
551                                 }
552                                 
553                                 rect.Inflate (-2, -2);
554                                 return rect;
555                         }
556                 }
557                 
558                 // the rectangle for the drop down arrow
559                 internal Rectangle drop_down_arrow_rect {
560                         get {
561                                 Rectangle rect = this.ClientRectangle;
562                                 rect.X = rect.Right - SystemInformation.VerticalScrollBarWidth - 2;
563                                 if (rect.Width > (SystemInformation.VerticalScrollBarWidth + 2)) {
564                                         rect.Width = SystemInformation.VerticalScrollBarWidth;
565                                 } else {
566                                         rect.Width = Math.Max (rect.Width - 2, 0);
567                                 }
568                                 
569                                 rect.Inflate (0, -2);
570                                 return rect;
571                         }
572                 }
573                 
574                 // the part of the date that is currently hilighted
575                 internal Rectangle hilight_date_area {
576                         get {
577                                 // TODO: put hilighted part calculation in here
578                                 return Rectangle.Empty;
579                         }
580                 }       
581                         
582                 #endregion
583                 
584                 #region internal / private methods
585                 
586                 [MonoTODO("Fix Dropdown location when System.Windows.Forms.Screen gets added")]
587                 private Point CalculateDropDownLocation (Rectangle parent_control_rect, Size child_size, bool align_left)
588                 {
589                         // default bottom left
590                         Point location = new Point(parent_control_rect.Left + 5, parent_control_rect.Bottom);
591                         // now adjust the alignment
592                         if (!align_left) {
593                                 location.X = parent_control_rect.Right - child_size.Width;                              
594                         }
595                         
596                         Point screen_location = PointToScreen (location);                       
597 // TODO: enable this part when screen comes into the classes
598 /*                      
599                         Rectangle working_area = Screen.FromControl(this).WorkingArea;
600                         // now adjust if off the right side of the screen                       
601                         if (screen_location.X < working_area.X) {
602                                 screen_location.X = working_area.X;
603                         }  
604                         // now adjust if it should be displayed above control
605                         if (screen_location.Y + child_size.Height > working_area.Bottom) {
606                                 screen_location.Y -= (parent_control_rect.Height + child_size.Height);
607                         }
608 */
609                         return screen_location;
610                 }
611                 
612                 // actually draw this control
613                 internal void Draw (Rectangle clip_rect, Graphics dc)
614                 {                       
615                         ThemeEngine.Current.DrawDateTimePicker (dc, clip_rect, this);
616                 }                       
617                 
618                 // drop the calendar down
619                 internal void DropDownMonthCalendar ()
620                 {
621                         // ensure the right date is set for the month_calendar
622                         month_calendar.SetDate (this.date_value);
623                         // get a rectangle that has the dimensions of the text area,
624                         // but the height of the dtp control.
625                         Rectangle align_area = this.date_area_rect;
626                         align_area.Y = this.ClientRectangle.Y;
627                         align_area.Height = this.ClientRectangle.Height;
628                         
629                         // establish the month calendar's location
630                         month_calendar.Location = CalculateDropDownLocation (
631                                 align_area,
632                                 month_calendar.Size,
633                                 (this.DropDownAlign == LeftRightAlignment.Left));
634                         month_calendar.Show ();
635                         month_calendar.Focus ();
636                         month_calendar.Capture = true;  
637                         
638                         // fire any registered events
639                         if (this.DropDown != null) {
640                                 this.DropDown (this, EventArgs.Empty);
641                         }
642                 }
643                 
644                 // hide the month calendar
645                 internal void HideMonthCalendar () 
646                 {
647                         this.is_drop_down_visible = false;
648                 Invalidate (drop_down_arrow_rect);
649                 month_calendar.Capture = false;
650                 if (month_calendar.Visible) {
651                         month_calendar.Hide ();
652                 }
653         }
654
655                 // raised by any key down events
656                 private void KeyPressHandler (object sender, KeyPressEventArgs e) {
657                         switch (e.KeyChar) {
658                                 default:
659                                         break;
660                         }
661                         e.Handled = true;
662                 }
663                 
664 //              // if we lose focus and the drop down is up, then close it
665 //              private void LostFocusHandler (object sender, EventArgs e) 
666 //              {
667 //                      if (is_drop_down_visible && !month_calendar.Focused) {
668 //                              this.HideMonthCalendar ();                              
669 //                      }                       
670 //              }
671                 
672                 // fired when a user clicks on the month calendar to select a date
673                 private void MonthCalendarDateSelectedHandler (object sender, DateRangeEventArgs e)
674                 {
675                         this.Value = e.Start.Date.Add (this.Value.TimeOfDay);
676                         this.HideMonthCalendar ();      
677                         this.Focus ();                  
678                 } 
679
680                 // to check if the mouse has come down on this control
681                 private void MouseDownHandler (object sender, MouseEventArgs e)
682                 {
683                         /* Click On button*/
684                         if (ShowUpDown) {
685                                 // TODO: Process clicking for UPDown
686                         } else {
687                                 if (is_drop_down_visible == false && drop_down_arrow_rect.Contains (e.X, e.Y)) {
688                                         is_drop_down_visible = true;
689                                         Invalidate (drop_down_arrow_rect);
690                                         DropDownMonthCalendar ();
691                         } else {
692                                 // mouse down on this control anywhere else collapses it
693                                 if (is_drop_down_visible) {                             
694                                         HideMonthCalendar ();
695                                 }
696                         } 
697                 }
698                 }
699                 
700                 
701                 // paint this control now
702                 private void PaintHandler (object sender, PaintEventArgs pe) {
703                         if (Width <= 0 || Height <=  0 || Visible == false)
704                                 return;
705
706                         Draw (pe.ClipRectangle, pe.Graphics);
707                 }
708                 
709                 private string FormatValue () {
710                         string ret_value = string.Empty;
711                         switch (format) {
712                                 case DateTimePickerFormat.Custom:
713                                         // TODO implement custom text formatting
714                                         ret_value = date_value.ToString ();
715                                         break;
716                                 case DateTimePickerFormat.Short:
717                                         ret_value = date_value.ToShortDateString ();
718                                         break;
719                                 case DateTimePickerFormat.Time:
720                                         ret_value = date_value.ToLongTimeString ();
721                                         break;
722                                 default:
723                                         ret_value = date_value.ToLongDateString ();
724                                         break;
725                         }
726                         return ret_value;
727                 }
728                 
729                 #endregion              
730         }
731 }