* ImageList.cs: When the image stream is set pull all the images
[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)]
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 = DateTime.Parse ("31 December 9998");//, 23:59:59");
49                 public static readonly DateTime MaxDateTime = new DateTime (9998, 12, 31, 23, 59, 59);
50                 public static readonly DateTime MinDateTime = new DateTime (1753, 1, 1);
51                 
52                 #endregion      // Public variables
53                 
54                 #region Local variables
55                 
56                 protected static readonly Color DefaultMonthBackColor = ThemeEngine.Current.ColorWindow;
57                 protected static readonly Color DefaultTitleBackColor = ThemeEngine.Current.ColorActiveTitle;
58                 protected static readonly Color DefaultTitleForeColor = ThemeEngine.Current.ColorTitleText;
59                 protected static readonly Color DefaultTrailingForeColor = Color.Gray;
60                 
61                 internal MonthCalendar                  month_calendar;
62                 bool                                                    is_checked;
63                 string                                                  custom_format;
64                 LeftRightAlignment                              drop_down_align;
65                 DateTimePickerFormat                    format;
66                 DateTime                                                max_date;
67                 DateTime                                                min_date;
68                 bool                                                    show_check_box;
69                 bool                                                    show_up_down;
70                 string                                                  text;
71                 DateTime                                                date_value;
72                 
73                 // variables used for drawing and such
74                 internal int                                    up_down_width;
75                 internal bool                                   is_drop_down_visible;
76                 
77                 #endregion      // Local variables
78                 
79                 #region public constructors
80                 
81                 // only public constructor
82                 public DateTimePicker () {
83                 
84                         // initialise the month calendar
85                         month_calendar = new MonthCalendar (this);
86                         month_calendar.CalendarDimensions = new Size (1, 1);
87                         month_calendar.MaxSelectionCount = 1;
88                         month_calendar.ForeColor = Control.DefaultForeColor;
89                         month_calendar.BackColor = DefaultMonthBackColor;
90                         month_calendar.TitleBackColor = DefaultTitleBackColor;
91                         month_calendar.TitleForeColor = DefaultTitleForeColor;
92                         month_calendar.TrailingForeColor = DefaultTrailingForeColor;
93                         month_calendar.Visible = false;
94
95                         
96                         // initialise other variables
97                         is_checked = false;
98                         custom_format = string.Empty;
99                         drop_down_align = LeftRightAlignment.Left;
100                         format = DateTimePickerFormat.Long;
101                         max_date = MaxDateTime;
102                         min_date = MinDateTime;
103                         show_check_box = false;
104                         show_up_down = false;
105                         date_value = DateTime.Now;
106                         text = FormatValue ();          
107                         
108                         up_down_width = 10;
109                         is_drop_down_visible = false;
110                         
111                         month_calendar.DateSelected += new DateRangeEventHandler (MonthCalendarDateSelectedHandler);
112                         KeyPress += new KeyPressEventHandler (KeyPressHandler);
113                         LostFocus += new EventHandler (LostFocusHandler);
114                         MouseDown += new MouseEventHandler (MouseDownHandler);                  
115                         Paint += new PaintEventHandler (PaintHandler);
116                         
117                 }
118                 
119                 #endregion
120                 
121                 #region public properties
122                 
123                 // no reason why this is overridden
124                 [Browsable(false)]
125                 [EditorBrowsable(EditorBrowsableState.Never)]
126                 public Color BackColor {
127                         set {
128                                 base.BackColor = value;
129                         }
130                         get {
131                                 return base.BackColor;
132                         }
133                 }
134                 
135                 // no reason why this is overridden
136                 [Browsable(false)]
137                 [EditorBrowsable(EditorBrowsableState.Never)]
138                 public override Image BackgroundImage {
139                         set {
140                                 base.BackgroundImage = value;
141                         }
142                         get {
143                                 return base.BackgroundImage;
144                         }
145                 }
146
147                 [AmbientValue(null)]
148                 [Localizable(true)]
149                 public Font CalendarFont {
150                         set {
151                                 month_calendar.Font = value;
152                         }
153                         get {
154                                 return month_calendar.Font;
155                         }
156                 }
157
158                 public Color CalendarForeColor {
159                         set {
160                                 month_calendar.ForeColor = value;
161                         }
162                         get {
163                                 return month_calendar.ForeColor;
164                         }
165                 }
166
167                 public Color CalendarMonthBackground {
168                         set {
169                                 month_calendar.BackColor = value;
170                         }
171                         get {
172                                 return month_calendar.BackColor;
173                         }
174                 }
175
176                 public Color CalendarTitleBackColor {
177                         set {
178                                 month_calendar.TitleBackColor = value;
179                         }
180                         get {
181                                 return month_calendar.TitleBackColor;
182                         }
183                 }
184
185                 public Color CalendarTitleForeColor {
186                         set {
187                                 month_calendar.TitleForeColor = value;
188                         }
189                         get {
190                                 return month_calendar.TitleForeColor;
191                         }
192                 }
193
194                 public Color CalendarTrailingForeColor {
195                         set {
196                                 month_calendar.TrailingForeColor = value;
197                         }
198                         get {
199                                 return month_calendar.TrailingForeColor;
200                         }
201                 }
202                 
203                 // when checked the value is grayed out
204                 [Bindable(true)]
205                 [DefaultValue(true)]
206                 public bool Checked {
207                         set {
208                                 if (is_checked != value) {
209                                         is_checked = value;
210                                         // invalidate the value inside this control
211                                         this.Invalidate (date_area_rect);
212                                 }
213                         }
214                         get {
215                                 return is_checked;
216                         }
217                 }
218                 
219                 // the custom format string to format this control with
220                 [DefaultValue(null)]
221                 [RefreshProperties(RefreshProperties.Repaint)]
222                 public string CustomFormat {
223                         set {
224                                 if (custom_format != value) {
225                                         custom_format = value;
226                                         if (this.Format == DateTimePickerFormat.Custom) {
227                                                 // invalidate the value inside this control
228                                                 this.Invalidate (date_area_rect);
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                                 DateTime parsed_value = DateTime.Parse (value);
371                                 if (date_value != parsed_value) {
372                                         Value = parsed_value;
373                                 } 
374                         }
375                         get {
376                                 return text;
377                         }
378                 }       
379
380                 [Bindable(true)]
381                 [RefreshProperties(RefreshProperties.All)]
382                 public DateTime Value {
383                         set {
384                                 if (date_value != value) {
385                                         date_value = value;
386                                         text = FormatValue ();
387                                         this.OnValueChanged (EventArgs.Empty);
388                                         this.Invalidate (date_area_rect);
389                                 }
390                         }
391                         get {
392                                 return date_value;
393                         }                       
394                 }
395
396                 #endregion      // public properties
397                 
398                 #region public methods
399                 
400                 // just return the text value
401                 public override string ToString () {
402                         return this.Text;
403                 } 
404                                 
405                 #endregion      // public methods
406                 
407                 #region public events
408                 
409                 // raised when the monthcalendar is closed
410                 public event EventHandler CloseUp;
411                 
412                 // raised when the monthcalendar is opened
413                 public event EventHandler DropDown;
414                 
415                 // raised when the format of the value is changed
416                 public event EventHandler FormatChanged;
417                 
418                 // raised when the date Value is changed
419                 public event EventHandler ValueChanged;
420                 
421                 #endregion      // public events
422                 
423                 #region protected properties
424
425                 // not sure why we're overriding this one               
426                 protected override CreateParams CreateParams {
427                         get {
428                                 return base.CreateParams;
429                         }
430                 }
431                 
432                 // specify the default size for this control
433                 protected override Size DefaultSize {
434                         get {
435                                 // todo actually measure this properly
436                                 return new Size (200, PreferredHeight);
437                         }
438                 }
439                 
440                 #endregion      // protected properties
441                 
442                 #region protected methods
443                 
444                 // not sure why we're overriding this one
445                 protected override AccessibleObject CreateAccessibilityInstance () {
446                         return base.CreateAccessibilityInstance ();
447                 }
448                 
449                 // not sure why we're overriding this one
450                 protected override void CreateHandle () {
451                         base.CreateHandle ();
452                 }
453                 
454                 // not sure why we're overriding this one
455                 protected override void DestroyHandle () {
456                         base.DestroyHandle ();
457                 }
458                 
459                 // not sure why we're overriding this one
460                 protected override void Dispose (bool disposing) {
461                         base.Dispose (disposing);
462                 }
463                 
464                 // find out if this key is an input key for us, depends on which date part is focused
465                 protected override bool IsInputKey (Keys keyData) {
466                         // TODO: fix this implementation of IsInputKey
467                         return false;
468                 }
469                 
470                 // raises the CloseUp event
471                 protected virtual void OnCloseUp (EventArgs eventargs) {
472                         if (this.CloseUp != null) {
473                                 this.CloseUp (this, eventargs);
474                         }
475                 }
476                 
477                 // raise the drop down event
478                 protected virtual void OnDropDown (EventArgs eventargs) {
479                         if (this.DropDown != null) {
480                                 this.DropDown (this, eventargs);
481                         }
482                 }
483                 
484                 // raises the format changed event
485                 protected virtual void OnFormatChanged (EventArgs e) {
486                         if (this.FormatChanged != null) {
487                                 this.FormatChanged (this, e);
488                         }
489                 }
490                 
491                 // not sure why we're overriding this one 
492                 protected override void OnSystemColorsChanged (EventArgs e) {
493                         base.OnSystemColorsChanged (e);
494                 }
495                 
496                 // raise the ValueChanged event
497                 protected virtual void OnValueChanged (EventArgs eventargs) {
498                         if (this.ValueChanged != null) {
499                                 this.ValueChanged (this, eventargs);
500                         }
501                 }
502                 
503                 // overridden to set the bounds of this control properly
504                 protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
505                         // TODO: ensure I implemented the bounds core setting properly.
506                         if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height ||
507                                 (specified & BoundsSpecified.Size) == BoundsSpecified.Size)  {
508                                 base.SetBoundsCore (x, y, width, DefaultSize.Height, specified);
509                         } else {
510                                 base.SetBoundsCore (x, y, width, height, specified);
511                         }
512                         
513                         // need to set the rectangles for all the support internal rects
514                         // this is done here as a optimisation since this is an array of rects
515                         if ((specified & BoundsSpecified.X) == BoundsSpecified.X ||
516                                 (specified & BoundsSpecified.Y) == BoundsSpecified.Y) {
517                                 // TODO set up all the datepart rects
518                         }
519                 }
520
521                 // not sure why we're overriding this
522                 protected override void WndProc (ref Message m) {
523                         base.WndProc (ref m);
524                 }
525                 
526                 #endregion      // protected methods
527                 
528                 #region internal / private properties
529                 
530                 // this is the region that the date and the check box is drawn on
531                 internal Rectangle date_area_rect {
532                         get {
533                                 Rectangle rect = this.ClientRectangle;
534                                 if (ShowUpDown) {
535                                         // set the space to the left of the up/down button
536                                         if (rect.Width > (up_down_width + 4)) {
537                                                 rect.Width -= (up_down_width + 4);
538                                         } else {
539                                                 rect.Width = 0;
540                                         }
541                                 } else {
542                                         // set the space to the left of the up/down button
543                                         // TODO make this use up down button
544                                         if (rect.Width > (SystemInformation.VerticalScrollBarWidth + 4)) {
545                                                 rect.Width -= SystemInformation.VerticalScrollBarWidth;
546                                         } else {
547                                                 rect.Width = 0;
548                                         }
549                                 }
550                                 
551                                 rect.Inflate (-2, -2);
552                                 return rect;
553                         }
554                 }
555                 
556                 // the rectangle for the drop down arrow
557                 internal Rectangle drop_down_arrow_rect {
558                         get {
559                                 Rectangle rect = this.ClientRectangle;
560                                 rect.X = rect.Right - SystemInformation.VerticalScrollBarWidth - 2;
561                                 if (rect.Width > (SystemInformation.VerticalScrollBarWidth + 2)) {
562                                         rect.Width = SystemInformation.VerticalScrollBarWidth;
563                                 } else {
564                                         rect.Width = Math.Max (rect.Width - 2, 0);
565                                 }
566                                 
567                                 rect.Inflate (0, -2);
568                                 return rect;
569                         }
570                 }
571                 
572                 // the part of the date that is currently hilighted
573                 internal Rectangle hilight_date_area {
574                         get {
575                                 // TODO: put hilighted part calculation in here
576                                 return Rectangle.Empty;
577                         }
578                 }       
579                         
580                 #endregion
581                 
582                 #region internal / private methods
583                 
584                 [MonoTODO("Fix Dropdown location when System.Windows.Forms.Screen gets added")]
585                 private Point CalculateDropDownLocation (Rectangle parent_control_rect, Size child_size, bool align_left)
586                 {
587                         // default bottom left
588                         Point location = new Point(parent_control_rect.Left + 5, parent_control_rect.Bottom);
589                         // now adjust the alignment
590                         if (!align_left) {
591                                 location.X = parent_control_rect.Right - child_size.Width;                              
592                         }
593                         
594                         Point screen_location = PointToScreen (location);
595 // TODO: enable this part when screen comes into the classes
596 /*                      
597                         Rectangle working_area = Screen.FromControl(this).WorkingArea;
598                         // now adjust if off the right side of the screen                       
599                         if (screen_location.X < working_area.X) {
600                                 screen_location.X = working_area.X;
601                         }  
602                         // now adjust if it should be displayed above control
603                         if (screen_location.Y + child_size.Height > working_area.Bottom) {
604                                 screen_location.Y -= (parent_control_rect.Height + child_size.Height);
605                         }
606 */
607                         return screen_location;
608                 }
609                 
610                 // actually draw this control
611                 internal void Draw (Rectangle clip_rect)
612                 {                       
613                         ThemeEngine.Current.DrawDateTimePicker(DeviceContext, clip_rect, this);
614                 }                       
615                 
616                 // drop the calendar down
617                 internal void DropDownMonthCalendar ()
618                 {
619                         // ensure the right date is set for the month_calendar
620                         month_calendar.SetDate (this.date_value);
621                         // get a rectangle that has the dimensions of the text area,
622                         // but the height of the dtp control.
623                         Rectangle align_area = this.date_area_rect;
624                         align_area.Y = this.ClientRectangle.Y;
625                         align_area.Height = this.ClientRectangle.Height;
626                         
627                         // establish the month calendar's location
628                         month_calendar.Location = CalculateDropDownLocation (
629                                 align_area,
630                                 month_calendar.Size,
631                                 (this.DropDownAlign == LeftRightAlignment.Left));
632                         
633                         month_calendar.Show ();
634                         month_calendar.Focus ();
635                         
636                         // fire any registered events
637                         if (this.DropDown != null) {
638                                 this.DropDown (this, EventArgs.Empty);
639                         }
640                 }
641                 
642                 // hide the month calendar
643                 internal void HideMonthCalendar () 
644                 {
645                         this.is_drop_down_visible = false;
646                 Invalidate (drop_down_arrow_rect);
647                 if (month_calendar.Visible) {
648                         month_calendar.Hide ();
649                 }
650         }
651
652                 // raised by any key down events
653                 private void KeyPressHandler (object sender, KeyPressEventArgs e) {
654                         switch (e.KeyChar) {
655                                 default:
656                                         break;
657                         }
658                         e.Handled = true;
659                 }
660                 
661                 // if we lose focus and the drop down is up, then close it
662                 private void LostFocusHandler (object sender, EventArgs e) 
663                 {
664                         if (is_drop_down_visible && !month_calendar.Focused) {
665                                 this.HideMonthCalendar ();                              
666                         }                       
667                 }
668                 
669                 // fired when a user clicks on the month calendar to select a date
670                 private void MonthCalendarDateSelectedHandler (object sender, DateRangeEventArgs e)
671                 {
672                         this.Value = e.Start.Date.Add (this.Value.TimeOfDay);
673                         this.HideMonthCalendar ();      
674                         this.Focus ();          
675                         System.Console.WriteLine("MonthCalendarDateSelectedHandler");
676                 } 
677
678                 // to check if the mouse has come down on this control
679                 private void MouseDownHandler (object sender, MouseEventArgs e)
680                 {
681                         /* Click On button*/
682                         if (ShowUpDown) {
683                                 // TODO: Process clicking for UPDown
684                         } else {
685                                 if (is_drop_down_visible == false && drop_down_arrow_rect.Contains (e.X, e.Y)) {
686                                         is_drop_down_visible = true;
687                                         Invalidate (drop_down_arrow_rect);
688                                         DropDownMonthCalendar ();
689                         } else {
690                                 // mouse down on this control anywhere else collapses it
691                                 if (is_drop_down_visible) {
692                                 System.Console.WriteLine("hiding cause of mouse down");
693                                         HideMonthCalendar ();
694                                 }
695                         } 
696                 }
697                 }
698                 
699                 
700                 // paint this control now
701                 private void PaintHandler (object sender, PaintEventArgs pe) {
702                         if (Width <= 0 || Height <=  0 || Visible == false)
703                                 return;
704
705                         Draw (pe.ClipRectangle);
706                         pe.Graphics.DrawImage (ImageBuffer, 0, 0);
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.ToShortDateString ();
721                                         break;
722                                 default:
723                                         ret_value = date_value.ToLongDateString ();
724                                         break;
725                         }
726                         return ret_value;
727                 }
728                 
729                 #endregion              
730         }
731 }